java基础
2025-10-20 19:25:30 0 举报
AI智能生成
java基础,核心类库,并发包,关键字,锁,线程,jdk新特性相关总结
作者其他创作
大纲/内容
核心类库
IO
各种IO模型
IO多路复用
IO多路复用概述
IO多路复用就是通过一个线程可以同事监视多个文件描述符(读就绪或者写接续,也就是可以监视多个IO操作),如果有一个描述符就绪,那么就可以通知程序做相应的读写操作;
本质上还是同步IO;相对于普通的同步IO,多路复用就可以让一个用户线程监视多个IO操作,普通的同步IO,一个用户线程只能只能监听一个IO操作
IO多路复用实现方式
select
概念
调用TCP文件系统的poll函数,不停的查询,直到有一个连接有想要的数据为止
过程
每次调用select函数都需要需要把文件描述符从用户内传递到内核态,内核再不断的去轮询这些文件描述符对应的io操作
缺点
文件描述符数量比较少,只有1024个
poll
概念
和select类似, 也是把用户传入的文件描述符数组复制到内核中,然后中再去不断的去做轮询操作;
优点
没有文件描述符限制
缺点
用户态到内核态的文件描述符的复制
过程
epoll
概念
类似也是让内核去做轮询操作,但是文件描述符只需要从用户空间拷贝到内核空间只拷贝一次
优点
没有最大并发限制,
减少了从内核空间到用户空间的拷贝过程
过程
java-NIO
NIO组件
组件名称
组件功能
NIO工作流程
同步阻塞IO
概念
用户线程调用内核IO操作,需要等IO彻底结束之后才能返回到用户空间,因此IO过程是阻塞的。
调用阻塞直到完成
优缺点
优点
用户线程无延迟,就可以拿到IO之后的数据
缺点
用户线程处于等待之中需要消耗性能;
应用场景
同步非阻塞IO
概念
IO调用后,如果内核这个时候没有把数据准备好,那么就会直接返回一个状态,这个时候用户的IO线程就不会一直等待内核把数据准备好,而是间隔时间一段时间去轮询内核数据是否已经准备好了。最终是内核把数据准备好了,等待下一次用户线程的询问,就把数据从内核复制到用户内存中
优缺点
优点
无需阻塞等待内核准备数据,这个期间可以做其他的事情;
缺点
需要去轮询内核,内核无法以最短的时间把数据复制到用户内存中
应用场景
异步IO(AIO)
概念
用户线程对内核发起了IO操作,用户线程没有任何组阻塞;内核准备好数据并复制到用户内存后,会给用户线程发送一个信号,表明数据IO操作已经完成了;
优缺点
优点
内核会把数据复制到用户内存中,减少了用户线程去复制的过程;
缺点
应用场景
工作模式区别
IO基础概念
阻塞非阻塞
概念
一个线程做io操作,如果数据还没有准备好,线程是直接返回还是继续等待
非阻塞
不等待
阻塞
等待
区别
阻塞,如果内核没有准备好数据,那么线程就会一直阻塞直到有数据返回为止;非阻塞,如果内核没有准备数据,那么就会直接返回,并且还会不断的去轮询内核是否有准备好数据;
现成发起 I/O 请求时是否会被挂起(继续等待还是立即返回)
异步同步
同步
用户线程发起io操作之后,要么阻塞,要么自己不断轮训数据是否准备好
异步
用户线程发起io操作之后,立即返回,不用自己去轮训;io操作完成之后,内核会通知用户线程
描述 I/O 完成通知机制,由谁负责告知结果
IO操作步骤
1.用户代码向操作系统发出IO请求
2.轮询设备是否可以进行操作
3.将数据复制到用户内存之中
集合
ConcurrentHashMap
jdk1.7
数据结构
子主题
外层是segment数组,每个segment对象都是一个ReentrantLock锁
每一个segment 是继承了ReentrantLock 的 一个HashMap,每一个segment就是一把锁,并发度就是segment数组长度
segment 中维护了 HashEntry[] 数组
每一个HashEntry元素可以是一个链表
HashEntry[] table
├── HashEntry(key1, value1, next -> key2)
├── HashEntry(key3, value3, next -> key4 -> key5)
├── null
└── ...
├── HashEntry(key1, value1, next -> key2)
├── HashEntry(key3, value3, next -> key4 -> key5)
├── null
└── ...
读写操作步骤
读操作
步骤
1、根据key,计算出hashCode;
2、根据步骤1计算出的hashCode定位segment,
3、根据hashCode值和hashEntry数组的长度运算,定位到hashEntry数组中具体某一个hashEntry
4、遍历hashEntry,拿到key在链表中做比较
写操作
1、根据key,计算出hashCode;
2、给segement加锁,保证同一时刻只有一个线程修改segement
3、根据hashCode值和hashEntry数组的长度运算,定位到hashEntry数组中具体某一个hashEntry
4、获取到具体的链表,遍历链表查找 key 是否已存在
存在则更新
不存在则在头部插入
5、扩容检查,当前segement中元素的个数是否超过扩容条件
满足,则当前segement进行扩容操作
6、释放锁
扩容条件
当单个Segment元素的数量超过capacity * loadFactor 阈值的时候会进行单个Segment扩容
1.8
数据结构
Node 数组 + 链表 + 红黑树
nodes数组的每一个节点相当一个桶(bucket)
桶里存放的数据结构可能有三种:
单个 Node 节点(只有一个 key-value)
链表(发生哈希冲突时,多个 Node 串起来)
红黑树(链表长度超过 8 时,转成 TreeNode 红黑树)
demo
假设 table.length = 16,插入 3 个 key,哈希分布如下:
• key1 → index = 3
• key2 → index = 3 (冲突)
• key3 → index = 7
此时数据结构是:
• key1 → index = 3
• key2 → index = 3 (冲突)
• key3 → index = 7
此时数据结构是:
table[3] ──> Node(key1, val1) → Node(key2, val2) (链表)
table[7] ──> Node(key3, val3) (单节点)
其他 table[i] = null
table[7] ──> Node(key3, val3) (单节点)
其他 table[i] = null
如果链表长度超过 8,table[3] 会变成:
table[3] ──> TreeNode(key1, val1)
├── left → TreeNode(...)
└── right → TreeNode(...)
├── left → TreeNode(...)
└── right → TreeNode(...)
扩容条件
CAS + synchronized 来保证并发安全
读写操作步骤
读操作
步骤
1、计算 hash,扰动hash值
2、根据hash值桶长度计算桶的位置
3、定位到桶
如果桶为空
返回null
桶是链表
遍历链表,对比key
红黑树
在红黑树里面查找
如果查询到正在扩容
则去新的链表中查找
get 操作是无锁的
写操作
示意图
子主题
步骤
1、针对key做hash算法,扰动hash值
主要目的让后续计算定位hash桶的时候hash值能更加分散
2、初始化 table(懒加载)
如果桶还没有初始化,那么就通过CAS 创建初始容量
3、通过hash值重新计算定位到某一个hash桶
4、写入节点
如果hash桶为空则通 CAS 插入新节点,成功即结束(无锁)。
如果已经有元素:
使用:synchronized锁住桶的头结点
判断是链表还是树
链表
遍历,更新或者是尾部插入
红黑树
走红黑树的插入逻辑
5、检查当前桶是否要做链表转红黑树
如果桶内链表长度大于8,转成红黑树
6、扩容检查
全部的元素个数是否超过扩容阈值,如果超过,则出发多线程扩容
扩容条件
元素总数 size ≥ 扩容阈值 threshold
对比
1.8锁粒度下,针对的每一个桶元素
锁粒度
1.7的锁粒度大,针对是每一个Segment,也就是桶数组(最大也就是16个并发度)
扩容的时候单个segment中的,增加了HashEntry元素个数,扩容只是在segment内部做扩容
1.8的锁粒度小,并发度高,并发度取决于hash桶的数量
扩容的时候是全局扩容,每一次扩容hash桶的数量增加一倍,并发度就咱家一倍;
大量数据插入的情况下,因为1.8的红黑树,以及链表的尾插法,会减少线程对锁的持有时间
扩容
JDK 1.7
单个 Segment 元素数 > threshold
Segment 级别(局部扩容)
分段扩容,减少全表停顿,但可能空间利用率不均匀
单个 Segment 元素数 > threshold
Segment 级别(局部扩容)
分段扩容,减少全表停顿,但可能空间利用率不均匀
Segment中的HashEntry 元素个数增加,只是在sgement内部做扩容
JDK 1.8
全局 size >= capacity * loadFactor
整表扩容(协作迁移)
全局一致性更好,支持多线程并发迁移,避免长时间阻塞
全局 size >= capacity * loadFactor
整表扩容(协作迁移)
全局一致性更好,支持多线程并发迁移,避免长时间阻塞
LinkedHashMap
SkipList
LinkedList,ArrayList
LinkedList是双向链表实现的;ArrayList是数组实现的
查询的时候
ArrayList查询的时间复杂度都是O1,LinkedList查询的复杂度是On(因为是从表头或者表位做一个一个遍历查询),
添加删除修改操作
如果都是在尾部添加,那么添加的效率是一样的,
如果都是在头部添加,那么ArrayList效率很低,因为需要有数组元素的复制跃迁;LinkedList集合只是需要把引用之前新的元素就行了。
中间添加,效率都很低,ArrayList需要做数据复制移动;LinkedList需要先遍历查询到具体的位置,然后再前后引用的断开与重新连接;
HashMap
1.HashMap的散列表是什么时候创建的?在第一次put元素的时候创建的,也就是和懒加载机制很像;2.链表转红黑树的前提条件? 链表的长度要达到8,当前散列数组(最外层的数组)长度达到643.hash算法的理解?把任意长度的值转化为固定长度的输出,会有hash冲突,hash冲突是很难避免的。4.为啥要使用红黑树结果?如果hash冲突严重,那么就会导致hash槽下面的数据的链表过长,对于查询数据效率低查询的时间复杂度从 O(n) 变成 O(log)5.扩容规则?也就是扩容之后的容量是扩容之前的两倍6.扩容之后的老数组中的数据怎么往新的数组里面迁移??因为hash槽里面存在四种情况:1.hash槽完全没有数据这个其实没啥说的;2.hash槽里面只有一个数据;根据新计算出来的tableSize,存放过去;3.hash槽里面是链表;4.hash槽里面是红黑树;7.扩容之后的hash槽计算?比如说当前是16个hash,在第1个槽位,在扩容之后,需要迁移的数据就会落在1+16 =17 也就是在第17个槽位上面。
JDK1.7和JDK1.8的区别
1.7的元素是放在单向链表的头部,1.8是放在单向链表的尾部
1.7的存储形式是外层是数组,内层是链表;1.8是外层是数组,内层是链表或者红黑树
1.8元素存储可以转换成红黑树,提高了,元素读取的效率;1.7是没有将链表转成红黑树的功能
1.7外层是Entry数组;1.8外层是Node数组,
1.8重写了Hash方法,降低了hash冲突,提高了Hash表的存,去效率(增加了异或运算,位运算)
1.7扩容后添加元素,1.8扩容前添加元素
因为 1.7 头插法扩容时,头插法会使链表发生反转,多线程环境下会产生环。
1.7扩容,需要将元素重新hash然后把数据存放,1.8扩容后不需要重新hash,新位置不变或者新位置是索引+扩容大小
提高扩容效率
1.7的散列函数做了多次位移,异或运算,1.8只做一次
提高hash效率
1.7
数据结构
外层
Entry<K,V>[] table
Entry<K,V>[] table
内层
链表(Entry)
Entry<K,V>
存取过程
存储过程
1、计算 hash
2、根据hash值再次计算定位到桶
3、插入数据
桶为空,直接插入新的entry
桶非空
遍历链表找到key
key存在,做更新
key不存在,插入链表头部
4、扩容检查,如果是超过了阈值,外层table数组元素个数翻倍,重新计算每一个桶的位置,做数据迁移
获取过程
1. 计算哈希值
int hash = hash(key); // 调用扰动函数,减少碰撞
2. 定位桶
Node<K,V> first = table[(n - 1) & hash];
3. 桶判空
如果 first == null → 说明该桶没有数据,直接返回 null。
4. 遍历链表
先比对 hash 值是否相等,再比对 key 是否相等
1.8
数据结构
外层
Node<K,V>[] table
内层
Node 节点(链表节点)
TreeNode 节点(红黑树节点)
存取过程
存储
1. 计算哈希
int hash = hash(key); // 扰动运算:减少 hash 碰撞
2. 定位桶
int i = (n - 1) & hash; // n = table.length,位运算替代取模
3. 插入逻辑
桶为空,直接创建新的Node节点放入数组
桶非空
如果第一个节点就是目标key,直接覆盖
如果首节点是红黑树,按照红黑树的插入逻辑
如果是链表,则遍历链表,存在覆盖,不存在尾部插入
如果插入后链表长度>= 8 且table的容量>=64 链表转红黑树
4.扩容检查
插入后元素总数超过了额定值,则需要扩容
获取
1. 计算 hash 定位桶
int hash = hash(key);
Node<K,V> first = table[(n - 1) & hash];
Node<K,V> first = table[(n - 1) & hash];
2. 桶判空
如果桶为空 → 返回 null
3.桶首节点检查
如果桶首节点 hash 和 key 相同 → 返回该节点的 value
4.继续查找
如果是 TreeNode → 在红黑树中查找(O(log n))
否则在链表中顺序查找(O(n))
HashMap并发环境下的问题
多线程扩容,引起死锁问题
多线程put的时候可能导致元素丢失
put非null元素后get出来的却是null
1.7和1.8都存在的问题
常见问题
1.HashMap的散列表是什么时候创建?
在第一次put元素的时候创建的,也就是和懒加载机制很像
2.链表转红黑树的前提条件?
1.当前链表的长度大于8;
2.外层Hash桶的数组长度达到64个;
3.为啥要使用红黑树存储?
1.如果hash冲突严重,那么就会导致Hash槽下面的数据的链表长度过长;查询数据的复杂度就是0(n),换成红黑树之后就变成了O(log)
4.扩容相关
扩容之后的hash槽计算?
比如说当前是16个hash槽,扩容之后就会变成32个hash槽;当前第一个槽位中需要被迁移的数据会迁移到第17个槽位上;也就是以此类推,当前第16个槽位的需要被迁移的数据被迁移到第32个上;
什么情况下回触发扩容?
当前实际存储元素的个数超过一定百分比(默认的75%,这个比例可以通过构造函数设置)
扩容之后的老数组中的数据怎么往新的数组里面迁移?
1.hash槽没有数据
也就是没有需要被迁移的数据
2.hash槽里面只有一个数据
根据新计算出来的hash槽位把数据存放过去
3.hash槽里面是链表
4.hash槽里面是红黑树
扩容之后的hash槽计算?比如说当前是16个hash,在第1个槽位,在扩容之后,需要迁移的数据就会落在1+16 =17 也就是在第17个槽位上面。
扩容因子为啥是0.75
假设扩容因子=1,也就是每一次扩容都前集合的元素个数和当前集合的最大容量相同,无形之中就增加了hash碰撞,hash碰撞之后,就会形成对应的链表过长,红黑树高度过高,这样就会导致在查询的时候耗时过高。
如果扩容引子过小,map集合在存储元素的时候,就会经常发生扩容操作,导致添加元素耗时过高。
0.75是经过很多科学计算之后,平衡了添加和查询操作之后,得出来最佳的数值
5.get死循环,put闭环
不安全原因
代码截图
原因分析
key已经存在,需要修改HashEntry对应的value;
key不存在,在HashEntry中做插入
源码解析
hash方法
1.如果key为null,那么hash值就是为1;
2.如果key不为null
1.获取当前key的hashCode值与value的Code值做异或运算
2.拿到第一步的值,让当前值和当前值的与16做位运算之后的值做异或运算
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value);}
Put方法解析
1.第一次put的时候,如果散列数组为空,那么就会进行扩容散列操作;
2.如果hash桶只要单个数据,那么先对比数据是否一样,一样就覆盖原来的值;
4.如果hash桶的数据是红黑树,如果key不重复,那么就把数据放在key的末尾;如果key重复了,那么就替代源码的value值;
3.如果hash桶的数据,是链表,如果key不重复,那么就把数据放在key的末尾;如果key重复了,那么就替代源码的value值;
1.8扩容问题
扩容相关参数
capacity
当前数组的长度(桶的数量)
loadFactor
负载因子,默认 0.75
当 size > threshold(capacity*loadFactor) 时,会触发 扩容
扩容条件
都是 size >= threshold
扩容策略
扩容倍数:Java 默认是 2 倍
新数组长度 = 旧数组长度 × 2
阈值也会相应调整 = 新容量 × loadFactor
扩容过程
jdk1.7
扩容后的数组长度是扩容前的两倍
单个元素的保留在原来的桶
如果是链表需要拆分成两个链表,一个保留在低位 的桶,一个保存在高位的桶;
JDK1.7 链表头插法
jdk1.8
扩容后的数组长度是扩容前的两倍
单个元素的保留在原来的桶
如果是链表需要拆分成两个链表,一个保留在低位 的桶,一个保存在高位的桶;
如果是红黑树,拆分红黑树,一个保留在原来索引位置的桶,一个保留在高位的桶(红黑树不会退化成链表)
JDK1.8 尾插法
阻塞队列
概述
阻塞队列也是一种先进先出的队列
特性
线程安全队列
带有阻塞功能
如果队列满,继续入队列时,入队列操作就会阻塞,直到队列不满才能完成入队列
如果队列空,继续出队列时,出队列操作就会阻塞,直到队列不空才能完成出队列
引用场景
生产者消费者模型
代码“解耦合”
“削峰填谷”
阻塞队列概览
AQS
CAS
序列化
java序列化
序列化作用
网络之间传输数据,做持久化;
序列化方式
jdk自带Serializable
json序列化
xml序列化
message pack
Protocol Buffers
Marshalling
设计模式
代理
jdk代理
被代理类必须要实现接口;
底层实现反射
JDK的Proxy类在我们调用方法newProxyInstance时,生成一个代理对象在内存中,该对象根据参数实现了被代理类的接口,重写了接口中的方法。在此方法内,调用了我们创建的InvocationHandler的invoke()方法。实际调用的就是InvocationHandler中我们重写的invoke方法。所以动态代理在运行时会根据我们重写的代码进行增强
cglib代理
ASM
通过cglib包来实现cglib代理,cglib包下最终也是调用的asm,字节码修改技术来相当于生成了一个新的类来替代原有的类执行方法。
单例
策略
设计模式
关键字
synchronized
Synchronized实现
字节码层面
编译后生成了加锁,解锁指令
monitorenter
加锁指令
monitorentexit
解锁指令
JVM层面
jvm调用了操作系统提供的同步方法;
操作系统和硬件
操作系统本身有一些同步的操作
锁对象
锁优化
锁升级
Synchronized 锁升级过程
Synchronize 内部,有一个锁升级的过程;jdk1.6自动打开;1.jvm会偏向第一个执行的线程,给他加上偏向锁;
2.如果这个时候还有其他线程来竞争,这个偏向锁就会升级成轻量级锁,这轻量级锁,多数情况下是自旋锁;
3.如果并发程度高,这个自旋锁会自旋很多次,如果超过十次,还没有拿到锁对象,那么锁又会升级成重量级锁(悲观锁);
详细说明:
线程第一次进入 synchronized 块时,JVM 会把 线程 ID 写入对象头 Mark Word
后续同一线程再进入时,只需检查 Mark Word 是否保存的是自己的线程 ID
锁自旋
线程竞争失败后,不立刻阻塞,而是 循环(自旋)一段时间,反复尝试获取锁:
一般情况:
当线程竞争锁失败时,会被 阻塞,等待操作系统唤醒,这涉及 用户态 → 内核态切换,开销很大。
优点:
避免线程阻塞/唤醒带来的昂贵系统调用
在锁占用时间很短时性能极佳
缺点
自旋期间线程不做有用工作,浪费 CPU 时间
如果锁长期被占用,自旋会一直失败 → 白白消耗 CPU
锁状态转换过程
示意图
锁状态
无锁状态
偏向锁状态
偏向锁:适用于单线程适用锁的情况,如果线程争用激烈,那么应该禁用偏向锁。
轻量级锁状态
轻量级锁:适用于竞争较不激烈的情况(这和乐观锁的使用范围类似)
重量级锁状态
重量级锁:适用于竞争激烈的情况
锁粗化
编译器发现某段代码有连续的加锁解锁操作就会进行锁粗化,将连续的加锁解锁变成一个加锁解锁,提高代码执行效率;
锁消除
编译器发现某段代码不存在多线程竞争共享资源,编译之后就会把锁消除掉,减少加锁解锁的性能消耗
transient
瞬时态
特点
用此修饰符的字段是不会被序列化
使用场景
类中的字段是可以通过其他字段被推导出来,此字段不需要做序列化
一些安全性的信息,一般情况下是不能离开JVM的
需要使用一些类,但是这个类对程序逻辑没有影响,也可以用来修饰
native
本地方法
native 方法就是 本地方法
Java 代码里只声明,不实现
实现部分交给 JNI(Java Native Interface),通常在 .dll(Windows)、.so(Linux)、.dylib(Mac)等动态链接库中
volatile
可见性机制
变量被修改之后,立马从自己的工作内存复制到主内存当中
一致性机制
该关键字修改的变量,在被使用的时候都会先去主内存中获取到该变量的最新值,复制到工作内存当中
使用场景
不存在并发问题但是又被很多地方同时使用。
缺陷
并发环境下使用会有线程安全问题;java的操作并不是原子操作
volatile关键字特点
能够屏蔽指令重排序
保证了 volatile 读写与前后指令之间的 有序性
可见性
一个线程修改了 volatile 变量,其他线程能立即看到
一致性的开销小于锁的开销
实现
字节码层面
给字段加上了ACC_volatile标记
JVM层面
编译后给该字段加上读写屏障
读操作
StoreStoreBarrier
读操作
StoreLoadBarrier
每次读(针对所有线程),都会从主内存中读取,而不是从工作内存中读取
写操作
LoadLoadBarrier
写操作
LoadStoreBarrier
每次写操作,都会将工作内存中的值,刷新到主内存中
操作系统和硬件
翻译成对应的汇编指令,然后给读写加锁
线程
线程池
Executors
newFixedThreadPool
定长线程池,
newWorkStealingPool
newCachedThreadPool
可缓存线程池
特点一个核心线程都没有
newSingleThreadScheduledExecutor
单线程定时任务线程
newScheduledThreadPool
定时任务线程
newSingleThreadExecutor
单线程线程池
ForkJoinPool
ThreadPoolExecutor
ScheduledThreadPoolExecutor
线程池工作过程
1.线程池未达到 核心线程数 → 创建新线程。
除非创建线程池后,调用了prestartAllCoreThreads(),prestartCoreThread(),也就是创建线程池后会创建核心线程数量
2.达到核心线程数 → 任务进入队列。
3.队列满 & 线程数 < 最大线程数 → 创建非核心线程执行任务。
4.队列满 & 线程数 = 最大线程数 → 启动拒绝策略。
工作流程示意图
线程池参数
corePoolSize
核心线程个数
核心线程会一直存活,即使空闲。
当有新任务时,优先使用核心线程执行。
最大线程个数
maximumPoolSize
线程池能容纳的最大线程数。
当任务很多且队列满时,会启用非核心线程,直到达到最大值。
非核心线程多久不做任务就会被回收
keepAliveTime(空闲存活时间)
非核心线程存活时间的单位
unit(存活时间的单位)
工作队列
阻塞队列中传递Runnable
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
存放等待执行的任务。
线程工厂
线程工厂,主要用来创建线程;
拒绝策略
AbortPolicy
丢弃任务并抛出RejectedExecutionException异常。
线程池默认的拒绝策略
线程池默认的拒绝策略
DiscardPolicy
丢弃任务,但是不抛出异常。
如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。
如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。
CallerRunsPolicy
由调用线程(提交任务的线程)处理该任务
DiscardOldestPolicy
该任务有提交这个任务的线程去执行。
自定义
表示当拒绝处理任务时的策略
线程池状态
线程状态说明
RUNNING
自然运行状态
SHUTDOWN
不接受新任务,执行已添加任务
STOP
不接受新任务,也不执行已添加任务
TIDYING
所有任务已终止,任务数量为0
TERMINATED
线程彻底终止
线程状态扭转
示意图
使用场景
定时生成报表
定时任务处理大量报表信息
外部接口调用
优化接口的时候
数据清理/归档
缓存定期刷新
高并发抢购、秒杀场景
线程
Thread
线程的状态
线程状态转换
示意图
线程各种转态
NEW
新创建的未启动状态的线程
RUNNABLE
线程准备或者运行中
BLOCKED
线程阻塞
发生IO阻塞
发生锁的争用阻塞
WAITING
未指定时间的线程等待
Object.wait,不带超时时间
Object.notify,Object.notifyAll 唤醒线程
Thread.join不带超时时间
等待终止
LockSupport.park
子主题
TIMED_WAITING
指定时间的线程等待
Thread.sleep 方法
指定超时值的 Object.wait 方法
指定超时值的 Thread.join 方法
LockSupport.parkNanos
LockSupport.parkUntil
TEMINATED
线程终止
Thread中的方法
静态方法
sleep
当前线程进行相应时间的休眠,并且不释放锁对象
yield
当前线程放弃cpu执行权,让其他线程去执行,放弃的时间不确定,可能放弃后,又获得了执行权;
currentThread
获取当前执行程序的线程
实例方法
isAlive() 方法的功能是判断当前线程是否处于活动状态
getId() 方法的作用是取得线程的唯一标识
isDaeMon、setDaemon(boolean on) 方法,是否是守护线程,设置守护线程
start()
线程可以开始运行
run()
线程真正执行
getPriority()和setPriority(int newPriority)
设置优先级,获取优先级
interrupt()
中断线程
join()
线程加入,抢占cpu执行权
Runable
开启线程的方式
继承Thread类,重写run方法
实现runnable接口,重写run方法
线程池类
线程调度
抢占式
cpu的执行权由系统去分配
java就是采用的抢占式,可以设置线程的优先级,但是并不会有太好的结果;
协同式
线程在完成某个任务之后通知cpu,进行线程的切换
特殊场景
CountDownLatch
CountDownLatch
Semaphore
线程模型
内核线程实现
线程相当于于一个轻量级进程,一个轻量级进程对应一个内核
线程的切换调度完全依赖内核
用户线程实现
用户实现对线程的创建消耗,和内核没有关系
线程和进程的比例为1:n
用户线程轻量级进程实现
轻量级进程是线程和内核之间沟通的桥梁
线程创建消耗会是由用户来控制
sun公司提供的jdk,使用的线程模型都是内核模型;有的公司提供的方式不是使用内核模型;
线程安全等级
1.final
2.线程绝对安全,类的方法字段做了synchronized同步,调用端单个调用这些的时候是线程安全的;
3.线程相对安全:传统意义上的线程安全,没法保证调用端是线程安全的
4.线程兼容:代码本身不是安全的,通过添加同步方式,保证这块线程是安全的;
5.线程对立:不管是调用方还是被调用方,线程都不安全
概述:线程的安全,需要考虑调用端和被调用端;
锁
锁的分类
锁的性质
java中的锁
jdk新特性
jdk9新特性
http请求是2.0版本
接口可有默认的实现
jdk7
interface IMyInterface {
public void test0();
}
public void test0();
}
jdk8
interface IMyInterface {
public static void test1 () {
System.out.println("静态方法");
}
default void test2() { // 可以被实现类覆写
System.out.println("默认方法");
}
public void test0();
}
public static void test1 () {
System.out.println("静态方法");
}
default void test2() { // 可以被实现类覆写
System.out.println("默认方法");
}
public void test0();
}
jdk9
interface IMyInterface {
private void test() {
System.out.println("接口中的私有方法");
}
public static void test1 () {
System.out.println("静态方法");
}
default void test2() {
System.out.println("默认方法");
}
public void test0();
}
private void test() {
System.out.println("接口中的私有方法");
}
public static void test1 () {
System.out.println("静态方法");
}
default void test2() {
System.out.println("默认方法");
}
public void test0();
}
多版本jar包兼容
在某个版本的java程序运行的时候可以选择不同版本的class版本
模块化
解决的问题
1、jvm启动的需要加载整个jra包,导致不需要使用的类,模块也被加在到内存中,浪费不必要浪费的内存;
在终端开发中尤其明显
不同版本的类库交叉依赖;每个公共类可以被类路径下任何其他的公共类所访问到,会导致使用并不想开放的api;类路径可能重复等
暂时无法理解这些
模块化好处
降低启动所需要的内存,按需加载对应的包
简化类库,方便开发维护
模块化实现
模块化实际上就是包外面在包一层,来管理包。
jshell命令
提供类似python交互式编程环境
可以在dos窗口,终端中进行编程
新增api
多分辨率图像api
mac系统已经支持
暂时还无法看到的api
引入轻量级的JSON API
新的货币api
弃用api
Applet和appletviewer的api都被标记为废弃
主流浏览器已经取消了对java浏览器插件的支持
改进
钻石操作符(泛型)的升级
具体解释
对应的demo
public class DiamondOperatorTest {
public static void main(String[] args) {
new DiamondOperatorTest().test();;
}
public void test() {
Set<String> set = new HashSet<>(){
@Override
public boolean add(String s) {
return super.add(s + "..");
}
};
set.add("1");
set.add("2");
set.add("3");
set.add("4");
for (String s : set) {
System.out.println(s);
}
}
}
public static void main(String[] args) {
new DiamondOperatorTest().test();;
}
public void test() {
Set<String> set = new HashSet<>(){
@Override
public boolean add(String s) {
return super.add(s + "..");
}
};
set.add("1");
set.add("2");
set.add("3");
set.add("4");
for (String s : set) {
System.out.println(s);
}
}
}
对应结果
try语句升级
jdk8
传统语句
优化后,jvm自动释放资源
需要将资源的实例化放在try 的小括号里面
jdk9
一次性可以释放多个资源
String实现
jdk8
底层是char 数组
jdk9
底层是byte数组
不同的编码字符集一个字符所需要的字节个数不一致,1-2个
相对于jdk8可以减少字符串内存空间消耗
快速创建只读集合
提高编码效率
所创建的集合都只能是做读取操作,无法做添加删除操作
增强的流api
List<Integer> list = Arrays.asList(11, 32, 44, 29, 52, 72, 82, 64, 4, 19);
list.stream().takeWhile(integer -> integer < 50).forEach(System.out::println);
list.stream().takeWhile(integer -> integer < 50).forEach(System.out::println);
只是做筛选,并不会修改list集合本身元素
List<Integer> list = Arrays.asList(11, 32, 44, 29, 52, 72, 82, 64, 4, 19);
list.stream().dropWhile(x -> x < 50).forEach(System.out::println);
list.stream().dropWhile(x -> x < 50).forEach(System.out::println);
只是做筛选,并不会修改list集合本身元素
Stream.ofNullable(null).forEach(System.out::println);
Stream.iterate(0, x -> x < 5, x -> x + 1).forEach(System.out::println);
类似下面代码
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
System.out.println(i);
}
Optional.ofNullable(list).stream().forEach(System.out::println);
动态编译器
AOT(Ahead of time)
java引用在jvm启动钱就被编译成二进制代码
jdk9中提出来,jdk10正式发布该新特性
jdk1.9 默认垃圾收集器G1
jdk9版本采用单线程回收
G1回收期新老年代都可以做回收
替换了jdk7,8 Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk10新特性
局部变量类型推断
var 可以作为局部变量
var str = "abc"; // 推断为 字符串类型
var l = 10L; // 推断为long 类型
var flag = true; // 推断为 boolean 类型
var list = new ArrayList<String>(); // 推断为 ArrayList<String>
var stream = list.stream(); // 推断为 Stream<String>
var l = 10L; // 推断为long 类型
var flag = true; // 推断为 boolean 类型
var list = new ArrayList<String>(); // 推断为 ArrayList<String>
var stream = list.stream(); // 推断为 Stream<String>
但是不可以作为全局变量,还有参数,已经方法返回值
G1引入并行 Full
发生full gc 的时候可以使用多个线程并行回收
jdk9的时候单线程回收
应用程序类数据共享
同一个机器下的多个jvm可以实现数据共享
减少不必要的内存消耗
线程本地握手
ThreadLocal 握手交互。在不进入到全局 JVM 安全点 (Safepoint) 的情况下,对线程执行回调。优化可以只停止单个线程,而不是停全部线程或一个都不停
在备用存储装置上进行堆内存分配
新增加Graal编译器
基于Java的JIT编译器,目前还属于实验阶段
预先把 Java 代码编译成本地代码来提升效能
删除javah工具
javah 用于生成C语言的头文件
JDK8开始,javah的功能已经集成到了javac中。去掉javah工具
新增api
List、Set、Map新增加了一个静态方法 copyOf
class Main {
public static void main(String[] args) {
List<Integer> list=new ArrayList<Integer>(3);
list.add(1);
list.add(2);
list.add(3);
List<Integer> temp = List.copyOf(list);
}
}
public static void main(String[] args) {
List<Integer> list=new ArrayList<Integer>(3);
list.add(1);
list.add(2);
list.add(3);
List<Integer> temp = List.copyOf(list);
}
}
IO
transferTo方法复制文件
JDK10 给 InputStream 和 Reader 类中新增了 transferTo 方法
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("a.txt")));
long nums = reader.transferTo(new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt"))));
System.out.println("一共复制的字节数量: "+nums);
long nums = reader.transferTo(new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt"))));
System.out.println("一共复制的字节数量: "+nums);
IO流大家族添加Charset参数的方法
ByteArrayOutputStream新增toString方法
JDK10给 ByteArrayOutputStream 新增重载 toString(Charset charset) 方法,通过指定的字符集编码字节,将缓冲区的内容转换为字符串
jdk11新特性
新增api
String类新增方法
判断是否是空白字符串
String s1 = "\t \n";
System.out.println(s1.isBlank()); // 判断是否为空白 true
System.out.println(s1.isBlank()); // 判断是否为空白 true
去除首尾空白
s1 = "\t sss\n";
System.out.println(s1.trim().length()); // 去除首尾空白,不能去除全角空格
System.out.println(s1.strip().length()); // 去除首尾空白,可以去除全角空格
System.out.println(s1.stripLeading()); // 去除头部空白
System.out.println(s1.stripTrailing()); // 去除尾部空白
System.out.println(s1.trim().length()); // 去除首尾空白,不能去除全角空格
System.out.println(s1.strip().length()); // 去除首尾空白,可以去除全角空格
System.out.println(s1.stripLeading()); // 去除头部空白
System.out.println(s1.stripTrailing()); // 去除尾部空白
lines()
方法可以对字符串每一行进行流式处理
"asc\nccc\nwww".lines().map(str -> str.toUpperCase()).forEach(System.out::println);
ASC
CCC
WWW
CCC
WWW
Optional方法增强
System.out.println(Optional.ofNullable(null).orElse("b")); // 如果为空,返回"b"
System.out.println(Optional.ofNullable(null).orElseGet(() -> "b")); // 也可以使用函数式接口实现orElse()
System.out.println(Optional.ofNullable(null).orElseThrow()); // 如果为空,排除异常
System.out.println(Optional.ofNullable(null).orElseGet(() -> "b")); // 也可以使用函数式接口实现orElse()
System.out.println(Optional.ofNullable(null).orElseThrow()); // 如果为空,排除异常
b
b
Exception in thread "main" java.util.NoSuchElementException: No value present
at java.base/java.util.Optional.orElseThrow(Optional.java:382)
at com.szc.Main.main(Main.java:42)
b
Exception in thread "main" java.util.NoSuchElementException: No value present
at java.base/java.util.Optional.orElseThrow(Optional.java:382)
at com.szc.Main.main(Main.java:42)
transferTo()方法
try (var inputStream = new FileInputStream("D:/test.jar");
var outputStream = new FileOutputStream("test2.jar")) {
inputStream.transferTo(outputStream);
} catch (Exception e) {
e.printStackTrace();
}
var outputStream = new FileOutputStream("test2.jar")) {
inputStream.transferTo(outputStream);
} catch (Exception e) {
e.printStackTrace();
}
移除废弃api
废弃
jvm参数
-XX:+AggressiveOpts、-XX:UnlockCommercialFeatures、-XX:+LogCommercialFeatures
废弃 Nashorn js引擎,可以考虑使用GraalVM
废弃 pack200和unpack200,这是以前压缩jar包的工具
移除
com.sun.awt.AWTUtilities、sum.misc.Unsafe.defineClass(被java.lang.invoke.MethodHandles.Lookup.defineClass替代)、Thread.destroy()、Thread.stop(Throwable)、sun.nio.disableSystemWideOverlappingFileLockCheck属性、sun.locale.formatasdefault属性、jdk.snmp模块、javafx模块、javaMissionControl等
JavaEE和CORBA模块
更简化的编译运行程序
解释:如果java文件里没有使用别的文件里的自定义类,那么就可以直接使用java就可以编译运行java文件,也不会输出class文件
Unicode编码
Unicode10加入了8518个字符,4个脚本和56个新的emoji表情符号
完全支持linux容器
jdk11以前的java应用程序在docker中运行的性能会下降,但现在此问题在容器控制组(cgroups)的帮助下得以解决,使JVM和docker配合得更加默契
免费的低耗能分析
通过JVMTI的SampledObjectAlloc回调提供一个低开销的堆分析方式
新的加密算法
ChaCha20-Poly1305这种更加高效安全的加密算法,代替RC4;采用新的默认根权限证书集,跟随最新的HTTPS安全协议TLS1.3
Flight Recorder
记录运行过程中发生的一系列事件
Java 层面的事件,如线程事件、锁事件,以及 Java 虚拟机内部的事件,如新建对象,垃圾回收和即时编译事件
启用设置 -XX:StartFilghtRecording
新增垃圾回收器
Epsilon GC
处理内存分配但不负责内存回收的垃圾回收器,堆内存用完,JVM即刻OOM退出
EpsilonGC使用场景
性能测试(过滤GC引起的性能消耗,相当于控制变量)
内存压力测试(看看不回收的情况下,到底能不能消耗指定大小的内存)
执行非常短的任务(GC反而是浪费时间)
VM接口测试、延迟吞吐量的改进等实验性质的调优
ZGC
ZGC是一个并发、基于区域(region)、标记压缩算法的GC
特性
不管堆内存多大,STW 时间不会超过10ms
只在根节点扫描阶段发生stw ,所以停顿事件不会随着堆内存增长和激活对象的增长而增长
启用ZGC的方法:-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
目前ZGC只能用于64位的linux操作系统下
目前ZGC只能用于64位的linux操作系统下
G1的完全并行GC
jdk12新特性
新增Shenandoah GC
雪兰多收集器使用的内存结构和G1类似,都是将内存划分为区域,整体流程也和G1相似
最大的区别在于雪兰多收集器实现了并发疏散环节,引入的Brooks Forwarding Pointer技术使得GC在移动对象时,对象的引用仍然可以访问,这样就降低了延迟
工作流程
其工作周期如下:
1)、初始标记,并启动并发标记阶段
2)、并发标记遍历堆阶段
3)、并发标记完成阶段
4)、并发整理回收无活动区域阶段
5)、并发疏散,整理内存区域
6)、初始化更新引用阶段
7)、并发更新引用
8)、完成引用更新阶段
9)、并发回收无引用区域阶段
1)、初始标记,并启动并发标记阶段
2)、并发标记遍历堆阶段
3)、并发标记完成阶段
4)、并发整理回收无活动区域阶段
5)、并发疏散,整理内存区域
6)、初始化更新引用阶段
7)、并发更新引用
8)、完成引用更新阶段
9)、并发回收无引用区域阶段
启用方法:
XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
新增一套微基准测试
现有微基准测试的运行和新微基准测试的创建过程
改进
switch表达式
demo
public class Main {
public static void main(String[] args) {
Fruit f = Fruit.APPLE;
switch (f) {
case APPLE -> System.out.println(1);
case ORANGE, GRAPE -> System.out.println(2);
case PEAR, MANGO, WATERMALLON -> System.out.println(3);
default ->
System.out.println("No such fruit");
}
}
}
enum Fruit {
APPLE, ORANGE, GRAPE, PEAR, MANGO, WATERMALLON
}
public static void main(String[] args) {
Fruit f = Fruit.APPLE;
switch (f) {
case APPLE -> System.out.println(1);
case ORANGE, GRAPE -> System.out.println(2);
case PEAR, MANGO, WATERMALLON -> System.out.println(3);
default ->
System.out.println("No such fruit");
}
}
}
enum Fruit {
APPLE, ORANGE, GRAPE, PEAR, MANGO, WATERMALLON
}
改进点
简化表达式
可以同时处理多个case
G1回收器
空闲时候G1回收器会自动将java堆内存返回给操作系统
jdk12中的G1将在应用程序不活动期间定期生成或持续循环检测整体的java堆使用情况,以便更及时地将java堆中不使用的内存返回给OS。这一改进带来的优势在云平台的容器环境中更加明显,此时内存利用率的提高会直接降低经济成本
G1 垃圾回收器的回收超过暂停目标,则能中止垃圾回收过程.
把回收集分为必须部分和可选部分,优先处理必须部分
必须部分主要包括G1不能递增处理的部分(如年轻代),也可以包含老年代以提高效率
优先处理必须部分时,会维护可选部分的一些数据,但产生的CPU开销不会超过1%,而且会增加本机内存使用率;处理完必须部分后,如果还有时间,就处理可选部分,如果剩下时间不够,就可能只处理可选部分的一个子集。处理完一个子集后,G1会根据剩余时间来决定是否继续收集
参数设置
G1PeriodicGCInterval
G1PeriodicGCSystemLoadThreshold
以上两个参数值都为0表示禁用此功能
G1PeriodicInvokesConcurrent
定期GC的类型,默认是Full GC,如果设置值了,就会继续上一个或启动一个新的并发周期
jdk13新特性
动态CDS档案
支持java应用执行之后进行动态归档,以后执行java程序后一些类就可以直接从这些归档文件中加载了
改进
ZGC提交未使用的堆内存
ZGC在jdk11中引入的收集器,jdk13中使能了向OS提交未使用的堆内存
过程
ZGC中的区域称之为ZPage,当ZGC压缩堆时,ZPage被释放,然后变成ZPageCache,最后使用LRU算法对PageCache区域进行定时清除。时间间隔默认为5分钟,用户自定义时间间隔尚未实现,而如果-Xms和-Xmx相等的话,这个功能就相当于没有
重新实现旧版套接字api
switch表达式中引入yield
文本块
String s = """
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<p>aaa</p>
</body>
</html>
""";
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<p>aaa</p>
</body>
</html>
""";
s = """
select * from students
where id in (12, 13, 14, 15)
order by grade desc
""";
select * from students
where id in (12, 13, 14, 15)
order by grade desc
""";
jdk14新特性
instanceof省去了强制类型转换的过程
jdk14版本之前写法
Object obj = "kuaidi100";
if(obj instanceof String){
String str = (String) obj;
}
if(obj instanceof String){
String str = (String) obj;
}
jdk14版本的写法
Object obj = "kuaidi100";
if(obj instanceof String str){
//直接使用str
}
if(obj instanceof String str){
//直接使用str
}
if (obj instanceof String s) {
// can use s here
} else {
// can't use s here
}
// can use s here
} else {
// can't use s here
}
垃圾回收器
移除 CMS(Concurrent Mark Sweep)垃圾收集器:删除并发标记清除 (CMS) 垃圾收集器
ZGC
MacOs windows 实现 ZGC
弃用 Parallel Scavenge 和 Serial Old 垃圾回收算法的组合
G1 NUMA感知内存分配:现在尝试跨垃圾收集在年轻一代的同一NUMA节点上分配并保留对象。这类似于并行GC NUMA意识。G1尝试使用
严格的交错在所有可用的NUMA节点上均匀分配Humongous和Old区域。从年轻一代复制到老一代的对象的放置是随机的。这些新的NUMA感知
内存分配试探法通过使用-XX:+UseNUNMA命令行选项自动启用
严格的交错在所有可用的NUMA节点上均匀分配Humongous和Old区域。从年轻一代复制到老一代的对象的放置是随机的。这些新的NUMA感知
内存分配试探法通过使用-XX:+UseNUNMA命令行选项自动启用
NUMA是啥?
这段话怎么理解
更详细的空指针异常
参数-XX:+ShowCodeDetailsInExceptionMessages
更详细地显示空指针异常
Packaging Tool (Incubator)
一个打包工具,可以将java应用直接打包成rpm,dmg或者exe在各自平台可以点击直接运行
改进
JFR Event Streaming JFR事件流
对飞行记录器功能升级
之前是不可以用于实时监控
jdk14之后可以实时获取到JVM的运行情况
弃用
不建议使用线程挂起、删除
涉及的线程挂起Thread的方法已经在jdk14版本种废弃
Thread.suspend(),Thread.
resume(),ThreadGroup.suspend(),ThreadGroup.resume(),ThreadGroup.allowThreadSuspension(boolean)这些方法将在
将来的版本中删除。
resume(),ThreadGroup.suspend(),ThreadGroup.resume(),ThreadGroup.allowThreadSuspension(boolean)这些方法将在
将来的版本中删除。
需要验证下对应的版本是否有这个方法?
椭圆曲线:
security-libs / javax.crypto,已过时的旧椭圆曲线去除
jdk15新特性
垃圾回收器
优化G1回收器
默认的堆区域大小计算已更改为默认情况下返回较大的区域,计算仍以2048个区域为目标
两点改进
仅考虑最大堆大小。旧的计算还考虑了初始堆大小,但是当未设置堆大小时,这可能会
产生意外的行为
产生意外的行为
区域大小四舍五入到最接近的2的幂,而不是减小。在最大堆大小不是2的幂的情况下,
这将返回更大的区域大小
这将返回更大的区域大小
ZGC可以正式使用
使用-XX:+UseZGC命
令行选项启用ZGC
令行选项启用ZGC
Shenandoah正式使用
一个低暂停时间的垃圾收集器
优化
文本块
简化了编写 Java 程序的任务,同时避免了常见情况下的转义序列
增强 Java 程序中表示用非 Java 语言编写的代码的字符串的可读性。
instanceof
二次优化
弃用
禁用和弃用偏向锁定
移除 Nashorn JavaScript 引擎
弃用 RMI 激活以进行删除
新增API
引入 API 以允许 Java 程序安全有效地访问 Java 堆之外的外部内存
隐藏类
引入隐藏类,即不能被其他类的字节码直接使用的类
隐藏类旨在供在运行时生成类并通过反射间接使用它们的框架使用。隐藏类可以定义为访问控制嵌套的成员,并且可以独立于其他类卸载
密封类(第一版预览)
使用密封的类和接口增强 Java 编程语言。密封的类和接口限制了哪些其他类或接口可以扩展或实现它们。
爱德华兹曲线数字签名算法
jdk16新特性
instanceof
最终版本
Records
最终版本
内存相关
Elastic Metaspace
弹性的元空间
元空间中未使用的 class 元数据内存更及时地返回给操作系统,以减少元空间的内存占用空间
垃圾回收器
ZGC 支持并发栈处理
把 ZGC 中的线程栈处理从安全点移到了并发阶段
不太懂ZGC回收过程?
HotSpot 子系统可以通过该机制延迟处理线程栈
不太懂ZGC回收过程?
对内存操作api
默认对内存的操作的api需要使用强封装的类来调用,提高系统的安全性
好处是
使用统一的api有利于以后对系统化版本的升级
说明
Java 9中,我们通过利用模块来限制对JDK内部元素的访问,从而提高了JDK的安全性和可维护性。模块提供了强封装
模块外部的代码只能访问该模块导出的包的公共和受保护元素
protected 修饰的元素只能由定义它的类已经它的子类访问
强封装适用于编译时和运行时,包括已编译代码试图在运行时通过反射访问元素时。导出包的非公共元素和未导出包的所有元素都被称为是强封装的
jdk17新特性
增强
密封类
在jdk15,16中是预览版本,17正式上线
demo
public abstract sealed class Student
permits NameChangeRecordService {
}
permits NameChangeRecordService {
}
类 Student 被 sealed 修饰,说明它是一个密封类,并且只允许指定的 3 个子类继承
回复严格模式的浮点数定义
伪随机数生成器增强
mac系统平面渲染api更换
新的api Apple Metal
老的api Apple OpenGL
上下文特定反序列化过滤器
通过配置过滤器,通过一个 JVM 范围的过滤器工厂,用来为每个单独的反序列化操作选择一个过滤器。
改进
switch(暂未发布)
老代码写法
static String formatter(Object o) {
String formatted = "unknown";
if (o instanceof Integer i) {
formatted = String.format("int %d", i);
} else if (o instanceof Long l) {
formatted = String.format("long %d", l);
} else if (o instanceof Double d) {
formatted = String.format("double %f", d);
} else if (o instanceof String s) {
formatted = String.format("String %s", s);
}
return formatted;
}
String formatted = "unknown";
if (o instanceof Integer i) {
formatted = String.format("int %d", i);
} else if (o instanceof Long l) {
formatted = String.format("long %d", l);
} else if (o instanceof Double d) {
formatted = String.format("double %f", d);
} else if (o instanceof String s) {
formatted = String.format("String %s", s);
}
return formatted;
}
新代码写法
static String formatterPatternSwitch(Object o) {
return switch (o) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> o.toString();
};
}
return switch (o) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> o.toString();
};
}
直接在 switch 上支持 Object 类型,这就等于同时支持多种类型,简化代码
外部函数和内存 API(孵化中)
API 可以调用本地库和处理本地数据,与java环境之外的代码和数据交互
改进14,15引进的一些api
矢量 API(二次孵化中)
增强的 API 允许以一种在运行时,可靠地编译为支持的 CPU 架构上的最佳向量指令的方式表达向量计算
删除
AOT Graal 编译器 移除
删除远程方法调用 (RMI) 激活机制,同时保留 RMI 的其余部分。
弃用
弃用安全管理器
0 条评论
下一页