文章目录
1 Java中的常用容器与集合
- 推荐B站学习资源:2020年最新版Java集合框架详解
- 学习重点
- Collection体系
- List接口、ArrayList,Vector,LinkedList的使用和区别
- 线程安全的集合
- Set,Map的概念,以及HashSet,HashMap,TreeMap的使用和区别
- 进阶学习
- HashMap源码分析,了解HaspMap的分桶、扩容原理
- 扩展:哈希环概念和算法
2 Java中的泛型
2.1 Java中的泛型基础知识
- 推荐B站学习资源:尚硅谷_Java泛型
- 学习重点
- 泛型在集合中的使用方法
- 泛型在继承方面的体现
- 进阶学习
- 自定义泛型类、方向方法
- 通配符的使用
2.2 Java中的泛型练习
- 练习题
- 以下关于泛型和集合说法正确的有哪些?
- A.List<?Super T>存放类型为T及其父类,但添加一定为T和其子类对象;
- B.List<?Extend T> 进行add(T子类)编译报错;
- 因为无法确定到达是哪一个了类C.List<?Extend T>存放的类型一定为T及其子类,但是获取要用T或其父类引用D.
- List<?Super T> get方法返回的对象都是Object类型,因为T的最上层父类是Object,想要向下转型只能强转
3 Lambda 和Stream
3.1 Lambda 和Stream
- 推荐B站学习资源:《怨我直言你可能真的不会java系列-lambda,streamAPI、文本块等特性深入讲解》(2h)
- 重点学习
- Lambda表达式基础语法
- 函数式接口
- Stream流、流的内置方法
- 流的筛选、排序、转换
- 进阶学习
- 串行流、并行流
3.2 练习题
-
有如上一个学生的集合(List),请使用Steam的方式编写代码完成以下练习输出:
/** *为简化代码,属性设置为public */ public class Student( public int id; public String name; public double score; } """ ① 查找姓名为小明学生,并打印分数; ② 统计班里学生得分80以上的人数; ③ 查找班里得分超过80的学生集合; ④ 分组统计班学生得分小于>60,大于等于60分并且小于80分,大于等于80分三个区间的学生人数(按顺序输出一个统计数组或List即可): """
4 Java Spring框架
4.1 学习
- 推荐B站学习资源:【狂神说Java】Spring5最新完整教程IDEA版通俗易懂
- 学习重点
- Spring对象的配置(XML方式)
- 依赖注入ーID方式
- Bean的作用域
- 自动装配
- Spring的注解
- Spring基于注解的方式开发
- 进阶学习
- Spring lOC
- Spring AOP 实现
- Java动态代理
- Spring的事务管理
- Spring MVC
- Spring注解

- Spring核心框架

4.2 练习题
- 按照视频的步骤,在本地IDE中搭建一个Spring + Mybatis + MySQL()项目工程:
- 在数据库中创建学生表(包含:id,name,score列)
- 使用Spring整合mybatis连接数据库
- 创建一个StudentDaolmpl类,和实体类Student
- 在StudentDaolmpl中完成数据增删查改功能
- 通过配置文件的方式加载Spring容器,并获取StudentDaolmpl类,完成学生信息的查找,插入、更新、删除
5 Mybatis开发
5.1 Mybatis架构
- 两大优势:
- 1、学习成本最低,最容易上手和掌握;
- 2、只需关注SQL本身,而不需要花费精力去处理例如注册驱动,创建Connection,以及确保关闭Connection这样繁杂的代码。

5.2 Mybatis开发流程
-
定义对外接口:结合数据库表结构,将其数据字段封装成JavaBean文件,通常是定义yaml接口编译生成的model类
//定义对外接口 <resultMap type="com.huawei.nce.tl1translatorservice.model.Policy" id="Policy*> <result property="name"column="name"/> <result property="desc'column="description"/> <result property="status"column='status"/> <result property="content"column="content"/> <result property="type"column="type"/> </resultMap> -
建立mybatis映射:配置相应Mapper.xml,在文件中配置javaBean以及编写sql增删改查语句>
//建立mybatis映射 <select id="queryAll'resultMap="Policy"resultType="list"> SELECT name,description,status,content,type FROM T POLICYMODULE POLICY </select> -
编写mapper抽象接口:编写抽象接口,将Mppper.xml文件映射成接口,Sql语句与抽象接口–对应
//编写mapper抽象接口 public interface PolicyMapper{ List<Policy> queryAlI(); } -
编写DAO具体接口:编写抽象持久化类,负责mapper管理对象的获取和设置
//编写DAO具体接口 public class PolicyDao { public List<Policy> queryAll() { return getMapper().queryAll(); } }
5.3 Mapper.xml编写
-
if条件判断&Set
<update id="modifyTask" parameterType="string"> UPDATE T PONCUT TASK <trim prefix="SET" suffixoverrides="," suffix= "WHERE task_name = #[taskName] "> <if test="remark != null and ' != remark"> REMARKS = #(remark),</if> <if test="fileName != null and ' != fileName">FILE_NAME = #(fileName),</if> <if test="-1 != isUploadFile">IS_UPLOAD FILE = #(isUploadFile)</if> </trim> </update> -
集合遍历
<select id="getFileNameByTaskName"parameterType="java. util. List"resulttype="java. lang. String"> SELECT FILE NAME FROM T PONCUT TASK WHERE TASK NAME IN <foreach collection="list"item="taskName"separator=","open="("close=")"index="index"> #[ taskName) </foreach> </select>
6 多线程编程与安全
6.1 线程的生命周期
- 新建状态:使用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序start()这个线程。
- 就绪状态:当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
- 运行状态:如果就绪状态的线程获取了CPU资源,就可以执行runO,此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
- 阻塞状态:如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。
在睡眠时间已到或获得设置资源后可以重新进入就绪状态。 - 死亡状态:一个运行状态的线程完成任务或者其它终止条件发生时,该线程就切换到终止状态。

6.2 线程创建与线程池
- Java提供了三种状态创建线程的方法:
- 通过实现Runnable接口;
- 通过继承Thread类本身;
- 通过Callable和Future创建线程。
- 【强制】线程资源必须通过线程池提供
- 说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
- 简单来说使用线程池有以下几个目的:
- 线程是稀缺资源,不能频繁的创建。
- 解耦作用:线程的创建与执行完全分开,方便维护。
- 应当将其放入一个池子中,可以给其他任务进行复用。
- 常见的创建线程池方式有以下几种:
- Executors.newCachedThreadPool():无限线程池。
- Executors.newFixedThreadPool(nThreads):创建固定大小的线程池。
- Executors.newSingleThreadExecutors():创建单个线程的线程池
6.3 线程安全
-
假设,你接到一个需求,要柠200个螺丝,你一看文档现在还有200个整没柠。这时候你的同事也接到这个需求,一看文档剩200个没柠。这时候你们都去柠了一个,各自记录-1,还剩199个没。但其实已经扩了2个了,这就有问题了。用代码来演示一下:300个人拧200个螺丝会出现什么情况。
public class MyRunnable extends Runnable { public static int luosi = 200; @overide public synchronized void run(){ for (int i = 1; i <= 100; i++) { if (luosi > 0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } luosi -= 1; System.out.println(Thread.currentThread().getName() + " luosi " + luosi); } } } } class RunnableDemo { public static void main(String args[]){ MyRunnable thread1 = new MyRunnable(); MyRunnable thread2 = new MyRunnable(); MyRunnable thread3 = new MyRunnable(); ExecutorService pool = Thread.newCachedThreadPool(); pool.execute(thread1); pool.execute(thread2); pool.execute(thread3); pool.shutDown(); } }
6.4 死锁
- 死锁概述
- 线程死锁是指两个或两个以上的线程互相持有对方所需要的资源,由于synchronized的特性,一个线程持有一个资源,或者说获得一个锁,在该线程释放这个锁之前,其它线程是获取不到这个锁的,而且会一直死等下去,因此这便造成了死锁。
- 死锁产生的条件
- ·互斥条件:一个资源,或者说一个锁只能被一个线程所占用,当一个线程首先获取到这个锁之后,在该线程释放这个锁之前,其它线程均是无法获取到这个锁的。
- 占有且等待:一个线程已经获取到一个锁,再获取另一个锁的过程中,即使获取不到也不会释放已经获得的锁。
- 不可剥夺条件:任何一个线程都无法强制获取别的线程已经占有的锁·
- 循环等待条件:线程A拿着线程B的锁,线程B拿着线程A的锁。

6.5 JDK并发工具类
- CountDownLatch:一个或多个线程等待其他线程完成一系列操作后才执行
- Semaphore:用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。

7 常用类库和API
7.1 Apache Commons Lang3中的API

7.2 Apache Commons Collections中的API
7.3 Apache Commons IO中的API
7.4 Jackson组件解析JSON字符串
-
Jackson的核心模块由三部分组成:
- jackson-core,核心包,提供基于"流模式"解析的相关API,它包括JsonPaser 和JsonGenerator.Jackson内部实现正是通过高性能的流模式 API的JsonGenerator和JsonParser来生成和解析 json.
- jackson-annotations,注解包,提供标准注解功能;
- jackson-databind,数据绑定包,提供基于"对象定"解析的相关API(ObjectMapper)和"树模型"解析的相关API(JsonNode):基于"对象绑定”解析的API和"树模型"解析的API依赖基于“流模式"解析的API。
-
从JSON数组字符串中读取对象列表
String jsonArray = "[{/"brand\":\"ford\"},{\"brand\":\"Fiat\"}]"; ObjectMapper objectMapper = new ObjectMapper(); List <Car> cars1 = objectMapper.readValue(jsonArray,new TypeReference <List <Car >>()0); -
将Object转换为JsonNode
ObjectMapper objectMapper = new ObjectMapper(); Car car = new Car(); car.brand ="Cadillac"; car.doors = 4; JsonNode carJsonNode = objectMapper.valueToTree(car);
7.5 常用API练习题
-
1、哪个类支持带占位符的String拼接?
- A.StringUtils(apache commons lang3)
- B.MessageFormat
-
2、编程题:集合a:[1,2,3,3,4,5),集合b:(3,4,4,5,6,7),求A和B的差
class SubtractDemo { public Collection subtract(Collection a,Collection b){} }
8 初识JVM
- 了解JVM的必要性
一般情况下,仅需知道java核心类库,以及第三方类库里API的用法,便可以专注于实现具体业务,并且依赖Java虚拟机自动执行乃至优化应用程序。
如果把核心类库的API比作数学公式的话,那么Java虚拟机的知识就好比公式的推导过程。掌握数学公式固然可以应付考试,但是了解背后的推导过程更加有助于记忆和理解。并且,在遇到哪些没法套公式的情况下,我们也能知道如何解决。 - 学习JVM的好处
- JVM提供了近干个配置参数
- 更好地规避JVM在使用中的Bug
- JVM拥有最前沿、最成熟的垃圾回收算法实现,以及即时编译器实现。了解其背后决策,对其他代码托管技术也能触类旁通。
- 除Javawh,Scala,Clojure,Groovy,以及时下热门的Kotlin,这些语言都可以运行在JVM上。
8.1 JVM的由来
- 所谓虚拟机(Virtual Machine),就是一台虚拟的计算机。它是一款软件,用来执行一系列虚拟计算机指令。大体上,虚拟机可以分为系统虚拟机和程序虚拟机。
- 大名鼎鼎的Visual Box,VMware就属于系统虚拟机,它们完全是对物理计算机的仿真,提供了一个可运行完整操作系统的软件平台。
- 程序虚拟机的典型代表就是Java虚拟机,它专门为执行单个计算机程序而设计,在Java虚拟机中执行的指令我们称为Java字节码指令。
- 无论是系统虚拟机还是程序虚拟机,在上面运行的软件都被限制于虚拟机提供的资源中。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java虚拟机屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行
8.2 JVM 与JDK
- 谈起JVM,不得不提的便是JDK与JRE,JDK物理存在,是Java语言的SDK,是JRE,JVM以及一系列编译,调试工具的集合。JRE物理存在,由JavaAPI和JVM组成,提供了Java应用程序运行的环境。
- 总的来说,我们利用JDK开发Java应用程序,通过JDK中携带的编译工具将Java源文件翻译成Java字节码文件,并在JRE上运行,再由JVM解析字节码,映射到CPU指令集或不同OS的系统调用。
8.3 Write once,run anywhere图解

8.5 JVM内存模型以及相关参数
- 根据JVM规范,JVM内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分。我们主要讲述和我们相关的虚拟机栈、堆和元空间

- 虚拟机栈:每个线程有一个私有的栈,随着线程的创建而创建,栈里面存着的是一种叫“栈帧”的东西,每个方法会创建一个栈帧,栈帧中存放了局部变量表(基本数据类型和对象引用)、操作数栈、方法出口等信息。栈的大小可以固定也可以动态扩展。当栈调用深度大于JVM所允许的范围,会抛出StackOverflowError的错误,不过这个深度范围不是一个恒定的值。
- 堆内存是JVM所有线程共享的部分,在虚拟机启动的时候就已经创建。所有的对象和数组都在堆上进行分配。这部分空间可通过GC进行回收。当申请不到空间时会抛出 OutOfMemoryError。
- 元空间的本质是对JVM规范中方法区的实现。元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小。
- 配置参数
- -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
- -XX:MaxMetaspaceSize,最大空间,默认是没有限制的。
除了上面两个指定大小的选项以外,还有两个与GC相关的属性: - -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
- -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集
- -Xms 为jvm启动时分配堆内存,比如-Xms200m,表示分配200M
- -Xmx 为jvm运行过程中分配的最大堆内存,比如-Xms500m,表示jvm进程最多只能够占用500M内存
- -Xss 为jvm启动的每个线程分配的内存大小
9 JVM内存初探
9.1 GC介绍
- 哪些内存需要回收

- 如何确定对象可回收
- 引用计数算法
- 逻辑:在对象中添加一个引用计数器,对象被引用,计数器+1;引用失效,计数器-1,统计计数器为0的对象作为结果
- 缺点:难以解决对象之间的相互循环引用
- 应用范围:微软COM技术、 ActionScript 的Flash Player和 Python
- 可达性分析算法
- 逻辑:通过GC Roots作为起点,通过起点向下遍历,走过的路径作为引用链,仅判定引用链包含的对象作为可达对象
- GC Roots范围:虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象和本地方法栈中JNI引用的对象
- 应用范围:主流jvm虚拟机
- 引用计数算法
9.2 GC算法
- mark-sweep 标记清除法
就是在分配了大量对象,并且其中只有一小部分存活的情况下,所消耗的时间会大大超过必要的值,这是因为在清除阶段还需要对大量死亡对象进行扫描 - mark-copy 标记复制法
会将从根开始被引用的对象复制到另外的空间中,对非存活对象直接清除,再将复制的对象所能够引用的对象用递归的方式不断复制下去 - mark-compact 标记-整理(也称标记-压缩)法
- generation-collect 分代收集算法
经过大量实际分析,发现内存中的对象,大致可以分为两类:有些生命周期很短,比如一些局部变量/临时对象,而另一些则会存活很久
如果分配的新对象比较大,eden区放不下,但是old区可以放下时,会直接分配到old区(即没有晋升这一过程,直接到老年代了)

9.3 GC收集器
| 收集器 | 串行、并行or并发 | 新生代/老年代 | 算法 | 目标 | 适用场景 |
|---|---|---|---|---|---|
| Serial | 串行 | 新生代 | 复制算法 | 响应速度优先 | 单CPU环境下的Client模式 |
| Serial Old | 串行 | 老年代 | 标记-整理 | 响应速度优先 | 单CPU环境下的Client模式,CMS的后备预案 |
| ParNew | 并行 | 新生代 | 复制算法 | 响应速度优先 | 多CPU环境时在Server模式下与CMS配合 |
| ParallelScavenge | 并行 | 新生代 | 复制算法 | 吞吐量优先 | 在后台运算而不需要太多交互的任务 |
| Parallel Old | 并行 | 老年代 | 标记-整理 | 吞吐量优先 | 在后台运算而不需要太多交互的任务 |
| CMS | 并发 | 老年代 | 标记-清除 | 响应速度优先 | 集中在互联网站或B/S系统服务端上的Java应用 |
| G1 | 并发 | both | 标记-整理+复制算法 | 响应速度优先 | 面向服务端应用,将来替换CMS |
- G1收集器
- 初始标记(Initial Marking)仅仅只是标记一下GC Roots能直接关联到的对象,此阶段需要停顿线但耗时很短。
- 并发标记(Concarrent Marking)从GC Root开始对堆中对象进行可达性分析,找到存活对象,此段耗时较长,但可与用户程序并发执行。
- 最终标记(Final Marking)为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一分标记记录,这阶段需要停顿线程,但是可并行执行。
- 筛选回收(Live Data Counting and&vacuation)首先对各个Region中的回收价值和成本进行排序根据用户所期望的GC停顿是时间来制定回收计划。此阶段其实也可以做到与用户程序一起并发执行,但因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率。
- G1收集器特点
- JAVA应用面对大内存场景,性能不会下降,使用G1的场景通常是大内存,比如64G甚至更大
- 应对短期的大对象回收
- 所有的垃圾回收,都是基于region的,分治思想。对每个region的gc耗时可控STW可控
- 建立可预测的时间模型,GC停顿时间预期,-XX:MaxGCPauseMillis。JVM统计哪些region的存活对象最少,总是会优先收集这些region,这也是Garbage-First得名的由来
- 弥补CMS的缺点,G1有内存整理过程(标记-压缩),避免了内存碎片。
- 小结
- 没有完美的GC收集器,只有更合适的GC收集器
- G1是最好的GC收集器?JDK11->ZGC
- GC分析工具推荐:除了JDK提供的工具命令
- GCViewer:分析gc日志,统计
- MAT:分析dump的文件
- JVM需要适应云上环境,Ali的AJDK->ElasticHeap,在GC时动态收缩堆内存,归还内存资源
9.4 JVM规范
9.4.1 JVM内存结构
- PC Register:也称为程序计数器,记录每个线程当前执行的指令信。eg:当前执行到哪一条指令,下一条该取哪条指令
- JVM Stack:也称为虚拟机栈,记录每个栈帧(Frame)中的局部变量、方法返回地址等
- Native Method Stack:本地(原生)方法栈,顾名思义就是调用操作系统原生本地方法时,所需要的内存区域
- Heap:即鼎鼎大名的堆内存区,也是GC垃圾回收的主站场,用于存放类的实例对象及Arrays实例等
- Method Area:方法区,主要存放类结构、类成员定义,static静态成员等Runtime
- Constant Pool:运行时常量池,比如:字符串,int-128~127范围的值等,它是Method Area中的一部分
- 独占还是共享
- PC Register,JVM Stack,Mative Method Stack,生命周期与Thread相同,即线程创建时,相应的区域分配内存,线程销毁时,释放相应内存
- Heap,Method Area 是线程共享的,都是在虚拟机启动时创建,虚拟机退出时释放
- Method Area区,虚拟机规范只是说必须要有,但是具体怎么实现(比如:是否需要垃圾回收?),交给具体的JVM实现去决定,逻辑上讲,是可以视为Heap区的一部分
- 广义的堆外内存:在分代算法下,新生代,老生代和持久代(MetaSpace)是连续的虚拟地址,-Xmx和-XX:MaxPermSize的总和,那么剩下的都可以认为是堆外内存(广义的)了,这些包括了jvm本身在运行过程中分配的内存,本地方法调用里分配的内存,DirectByteBuffer分配的内存等等
- 狭义的堆外内存主要是指java.nio.DirectByteBuffer在创建的时候分配内存,XX-MavDirectMemorySize限制上限


被折叠的 条评论
为什么被折叠?



