diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" "b/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" index 5c0c5b389d7..ce99f80418e 100644 --- "a/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" +++ "b/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" @@ -3,11 +3,12 @@ - [1. 剖析面试最常见问题之 Java 集合框架](#1-剖析面试最常见问题之-java-集合框架) - [1.1. 集合概述](#11-集合概述) - [1.1.1. Java 集合概览](#111-java-集合概览) - - [1.1.2. 说说 List,Set,Map 三者的区别?](#112-说说-listsetmap-三者的区别) + - [1.1.2. 说说 List, Set, Queue, Map 四者的区别?](#112-说说-list-set-queue-map-四者的区别) - [1.1.3. 集合框架底层数据结构总结](#113-集合框架底层数据结构总结) - [1.1.3.1. List](#1131-list) - [1.1.3.2. Set](#1132-set) - - [1.1.3.3. Map](#1133-map) + - [1.1.3.3 Queue](#1133-queue) + - [1.1.3.4. Map](#1134-map) - [1.1.4. 如何选用集合?](#114-如何选用集合) - [1.1.5. 为什么要使用集合?](#115-为什么要使用集合) - [1.2. Collection 子接口之 List](#12-collection-子接口之-list) @@ -22,25 +23,29 @@ - [1.3.1.2. 重写 compareTo 方法实现按年龄来排序](#1312-重写-compareto-方法实现按年龄来排序) - [1.3.2. 无序性和不可重复性的含义是什么](#132-无序性和不可重复性的含义是什么) - [1.3.3. 比较 HashSet、LinkedHashSet 和 TreeSet 三者的异同](#133-比较-hashsetlinkedhashset-和-treeset-三者的异同) - - [1.4. Map 接口](#14-map-接口) - - [1.4.1. HashMap 和 Hashtable 的区别](#141-hashmap-和-hashtable-的区别) - - [1.4.2. HashMap 和 HashSet 区别](#142-hashmap-和-hashset-区别) - - [1.4.3. HashMap 和 TreeMap 区别](#143-hashmap-和-treemap-区别) - - [1.4.4. HashSet 如何检查重复](#144-hashset-如何检查重复) - - [1.4.5. HashMap 的底层实现](#145-hashmap-的底层实现) - - [1.4.5.1. JDK1.8 之前](#1451-jdk18-之前) - - [1.4.5.2. JDK1.8 之后](#1452-jdk18-之后) - - [1.4.6. HashMap 的长度为什么是 2 的幂次方](#146-hashmap-的长度为什么是-2-的幂次方) - - [1.4.7. HashMap 多线程操作导致死循环问题](#147-hashmap-多线程操作导致死循环问题) - - [1.4.8. HashMap 有哪几种常见的遍历方式?](#148-hashmap-有哪几种常见的遍历方式) - - [1.4.9. ConcurrentHashMap 和 Hashtable 的区别](#149-concurrenthashmap-和-hashtable-的区别) - - [1.4.10. ConcurrentHashMap 线程安全的具体实现方式/底层具体实现](#1410-concurrenthashmap-线程安全的具体实现方式底层具体实现) - - [1.4.10.1. JDK1.7(上面有示意图)](#14101-jdk17上面有示意图) - - [1.4.10.2. JDK1.8 (上面有示意图)](#14102-jdk18-上面有示意图) - - [1.5. Collections 工具类](#15-collections-工具类) - - [1.5.1. 排序操作](#151-排序操作) - - [1.5.2. 查找,替换操作](#152-查找替换操作) - - [1.5.3. 同步控制](#153-同步控制) + - [1.4 Collection 子接口之 Queue](#14-collection-子接口之-queue) + - [1.4.1 Queue 与 Deque 的区别](#141-queue-与-deque-的区别) + - [1.4.2 ArrayDeque 与 LinkedList 的区别](#142-arraydeque-与-linkedlist-的区别) + - [1.4.3 说一说 PriorityQueue](#143-说一说-priorityqueue) + - [1.5. Map 接口](#15-map-接口) + - [1.5.1. HashMap 和 Hashtable 的区别](#151-hashmap-和-hashtable-的区别) + - [1.5.2. HashMap 和 HashSet 区别](#152-hashmap-和-hashset-区别) + - [1.5.3. HashMap 和 TreeMap 区别](#153-hashmap-和-treemap-区别) + - [1.5.4. HashSet 如何检查重复](#154-hashset-如何检查重复) + - [1.5.5. HashMap 的底层实现](#155-hashmap-的底层实现) + - [1.5.5.1. JDK1.8 之前](#1551-jdk18-之前) + - [1.5.5.2. JDK1.8 之后](#1552-jdk18-之后) + - [1.5.6. HashMap 的长度为什么是 2 的幂次方](#156-hashmap-的长度为什么是-2-的幂次方) + - [1.5.7. HashMap 多线程操作导致死循环问题](#157-hashmap-多线程操作导致死循环问题) + - [1.5.8. HashMap 有哪几种常见的遍历方式?](#158-hashmap-有哪几种常见的遍历方式) + - [1.5.9. ConcurrentHashMap 和 Hashtable 的区别](#159-concurrenthashmap-和-hashtable-的区别) + - [1.5.10. ConcurrentHashMap 线程安全的具体实现方式/底层具体实现](#1510-concurrenthashmap-线程安全的具体实现方式底层具体实现) + - [1.5.10.1. JDK1.7(上面有示意图)](#15101-jdk17上面有示意图) + - [1.5.10.2. JDK1.8 (上面有示意图)](#15102-jdk18-上面有示意图) + - [1.6. Collections 工具类](#16-collections-工具类) + - [1.6.1. 排序操作](#161-排序操作) + - [1.6.2. 查找,替换操作](#162-查找替换操作) + - [1.6.3. 同步控制](#163-同步控制) @@ -50,19 +55,21 @@ ### 1.1.1. Java 集合概览 -从下图可以看出,在 Java 中除了以 `Map` 结尾的类之外, 其他类都实现了 `Collection` 接口。 +Java 集合, 也叫作容器,主要是由两大接口派生而来:一个是 `Collecton`接口,主要用于存放单一元素;另一个是 `Map` 接口,主要用于存放键值对。对于`Collection` 接口,下面又有三个主要的子接口:`List`、`Set` 和 `Queue`。 -并且,以 `Map` 结尾的类都实现了 `Map` 接口。 +Java 集合框架如下图所示: -![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/source-code/dubbo/java-collection-hierarchy.png) +![](./images/java-collection-hierarchy.png) -

https://www.javatpoint.com/collections-in-java

-### 1.1.2. 说说 List,Set,Map 三者的区别? +注:图中只列举了主要的继承派生关系,并没有列举所有关系。比方省略了`AbstractList`, `NavigableSet`等抽象类以及其他的一些辅助类,如想深入了解,可自行查看源码。 -- `List`(对付顺序的好帮手): 存储的元素是有序的、可重复的。 +### 1.1.2. 说说 List, Set, Queue, Map 四者的区别? + +- `List`(对付顺序的好帮手): 存储的元素是有序的、可重复的。 - `Set`(注重独一无二的性质): 存储的元素是无序的、不可重复的。 -- `Map`(用 Key 来搜索的专家): 使用键值对(key-value)存储,类似于数学上的函数 y=f(x),“x”代表 key,"y"代表 value,Key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值。 +- `Queue`(实现排队功能的叫号机): 按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的。 +- `Map`(用 key 来搜索的专家): 使用键值对(key-value)存储,类似于数学上的函数 y=f(x),"x" 代表 key,"y" 代表 value,key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值。 ### 1.1.3. 集合框架底层数据结构总结 @@ -70,19 +77,23 @@ #### 1.1.3.1. List -- `Arraylist`: `Object[]`数组 -- `Vector`:`Object[]`数组 +- `Arraylist`: `Object[]` 数组 +- `Vector`:`Object[]` 数组 - `LinkedList`: 双向链表(JDK1.6 之前为循环链表,JDK1.7 取消了循环) #### 1.1.3.2. Set -- `HashSet`(无序,唯一): 基于 `HashMap` 实现的,底层采用 `HashMap` 来保存元素 -- `LinkedHashSet`:`LinkedHashSet` 是 `HashSet` 的子类,并且其内部是通过 `LinkedHashMap` 来实现的。有点类似于我们之前说的 `LinkedHashMap` 其内部是基于 `HashMap` 实现一样,不过还是有一点点区别的 -- `TreeSet`(有序,唯一): 红黑树(自平衡的排序二叉树) +- `HashSet`(无序,唯一): 基于 `HashMap` 实现的,底层采用 `HashMap` 来保存元素 +- `LinkedHashSet`: `LinkedHashSet` 是 `HashSet` 的子类,并且其内部是通过 `LinkedHashMap` 来实现的。有点类似于我们之前说的 `LinkedHashMap` 其内部是基于 `HashMap` 实现一样,不过还是有一点点区别的 +- `TreeSet`(有序,唯一): 红黑树(自平衡的排序二叉树) + +#### 1.1.3.3 Queue +- `PriorityQueue`: `Object[]` 数组来实现二叉堆 +- `ArrayQueue`: `Object[]` 数组 + 双指针 再来看看 `Map` 接口下面的集合。 -#### 1.1.3.3. Map +#### 1.1.3.4. Map - `HashMap`: JDK1.8 之前 `HashMap` 由数组+链表组成的,数组是 `HashMap` 的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间 - `LinkedHashMap`: `LinkedHashMap` 继承自 `HashMap`,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,`LinkedHashMap` 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:[《LinkedHashMap 源码详细分析(JDK1.8)》](https://www.imooc.com/article/22931) @@ -304,9 +315,66 @@ Output: `TreeSet` 底层使用红黑树,能够按照添加元素的顺序进行遍历,排序的方式有自然排序和定制排序。 -## 1.4. Map 接口 +## 1.4 Collection 子接口之 Queue + +### 1.4.1 Queue 与 Deque 的区别 + +`Queue` 是单端队列,只能从一端插入元素,另一端删除元素,实现上一般遵循 **先进先出(FIFO)** 规则。 + +`Queue` 扩展了 `Collection` 的接口,根据 **因为容量问题而导致操作失败后处理方式的不同** 可以分为两类方法: 一种在操作失败后会抛出异常,另一种则会返回特殊值。 + +| `Queue` 接口| 抛出异常 | 返回特殊值 | +| ------------ | --------- | ---------- | +| 插入队尾 | add(E e) | offer(E e) | +| 删除队首 | remove() | poll() | +| 查询队首元素 | element() | peek() | + +`Deque` 是双端队列,在队列的两端均可以插入或删除元素。 + +`Deque` 扩展了 `Queue` 的接口, 增加了在队首和队尾进行插入和删除的方法,同样根据失败后处理方式的不同分为两类: + +| `Deque` 接口 | 抛出异常 | 返回特殊值 | +| ------------ | ------------- | --------------- | +| 插入队首 | addFirst(E e) | offerFirst(E e) | +| 插入队尾 | addLast(E e) | offerLast(E e) | +| 删除队首 | removeFirst() | pollFirst() | +| 删除队尾 | removeLast() | pollLast() | +| 查询队首元素 | getFirst() | peekFirst() | +| 查询队尾元素 | getLast() | peekLast() | + +事实上,`Deque` 还提供有 `push()` 和 `pop()` 等其他方法,可用于模拟栈。 + + +### 1.4.2 ArrayDeque 与 LinkedList 的区别 + +`ArrayDeque` 和 `LinkedList` 都实现了 `Deque` 接口,两者都具有队列的功能,但两者有什么区别呢? + +- `ArrayDeque` 是基于可变长的数组和双指针来实现,而 `LinkedList` 则通过链表来实现。 + +- `ArrayDeque` 不支持存储 `NULL` 数据,但 `LinkedList` 支持。 + +- `ArrayDeque` 是在 JDK1.6 才被引入的,而`LinkedList` 早在 JDK1.2 时就已经存在。 + +- `ArrayDeque` 插入时可能存在扩容过程, 不过均摊后的插入操作依然为 O(1)。虽然 `LinkedList` 不需要扩容,但是每次插入数据时均需要申请新的堆空间,均摊性能相比更慢。 + +从性能的角度上,选用 `ArrayDeque` 来实现队列要比 `LinkedList` 更好。此外,`ArrayDeque` 也可以用于实现栈。 + +### 1.4.3 说一说 PriorityQueue + +`PriorityQueue` 是在 JDK1.5 中被引入的, 其与 `Queue` 的区别在于元素出队顺序是与优先级相关的,即总是优先级最高的元素先出队。 + +这里列举其相关的一些要点: + +- `PriorityQueue` 利用了二叉堆的数据结构来实现的,底层使用可变长的数组来存储数据 +- `PriorityQueue` 通过堆元素的上浮和下沉,实现了在 O(logn) 的时间复杂度内插入元素和删除堆顶元素。 +- `PriorityQueue` 是非线程安全的,且不支持存储 `NULL` 和 `non-comparable` 的对象。 +- `PriorityQueue` 默认是小顶堆,但可以接收一个 `Comparator` 作为构造参数,从而来自定义元素优先级的先后。 + +`PriorityQueue` 在面试中可能更多的会出现在手撕算法的时候,典型例题包括堆排序、求第K大的数、带权图的遍历等,所以需要会熟练使用才行。 + +## 1.5. Map 接口 -### 1.4.1. HashMap 和 Hashtable 的区别 +### 1.5.1. HashMap 和 Hashtable 的区别 1. **线程是否安全:** `HashMap` 是非线程安全的,`HashTable` 是线程安全的,因为 `HashTable` 内部的方法基本都经过`synchronized` 修饰。(如果你要保证线程安全的话就使用 `ConcurrentHashMap` 吧!); 2. **效率:** 因为线程安全的问题,`HashMap` 要比 `HashTable` 效率高一点。另外,`HashTable` 基本被淘汰,不要在代码中使用它; @@ -351,7 +419,7 @@ Output: } ``` -### 1.4.2. HashMap 和 HashSet 区别 +### 1.5.2. HashMap 和 HashSet 区别 如果你看过 `HashSet` 源码的话就应该知道:`HashSet` 底层就是基于 `HashMap` 实现的。(`HashSet` 的源码非常非常少,因为除了 `clone()`、`writeObject()`、`readObject()`是 `HashSet` 自己不得不实现之外,其他方法都是直接调用 `HashMap` 中的方法。 @@ -362,7 +430,7 @@ Output: | 调用 `put()`向 map 中添加元素 | 调用 `add()`方法向 `Set` 中添加元素 | | `HashMap` 使用键(Key)计算 `hashcode` | `HashSet` 使用成员对象来计算 `hashcode` 值,对于两个对象来说 `hashcode` 可能相同,所以`equals()`方法用来判断对象的相等性 | -### 1.4.3. HashMap 和 TreeMap 区别 +### 1.5.3. HashMap 和 TreeMap 区别 `TreeMap` 和`HashMap` 都继承自`AbstractMap` ,但是需要注意的是`TreeMap`它还实现了`NavigableMap`接口和`SortedMap` 接口。 @@ -430,7 +498,7 @@ TreeMap treeMap = new TreeMap<>((person1, person2) -> { **综上,相比于`HashMap`来说 `TreeMap` 主要多了对集合中的元素根据键排序的能力以及对集合内元素的搜索的能力。** -### 1.4.4. HashSet 如何检查重复 +### 1.5.4. HashSet 如何检查重复 以下内容摘自我的 Java 启蒙书《Head first java》第二版: @@ -473,9 +541,9 @@ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, 对于引用类型(包括包装类型)来说,equals 如果没有被重写,对比它们的地址是否相等;如果 equals()方法被重写(例如 String),则比较的是地址里的内容。 -### 1.4.5. HashMap 的底层实现 +### 1.5.5. HashMap 的底层实现 -#### 1.4.5.1. JDK1.8 之前 +#### 1.5.5.1. JDK1.8 之前 JDK1.8 之前 `HashMap` 底层是 **数组和链表** 结合在一起使用也就是 **链表散列**。**HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。** @@ -514,7 +582,7 @@ static int hash(int h) { ![jdk1.8之前的内部结构-HashMap](images/jdk1.8之前的内部结构-HashMap.png) -#### 1.4.5.2. JDK1.8 之后 +#### 1.5.5.2. JDK1.8 之后 相比于之前的版本, JDK1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。 @@ -522,7 +590,7 @@ static int hash(int h) { > TreeMap、TreeSet 以及 JDK1.8 之后的 HashMap 底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。 -### 1.4.6. HashMap 的长度为什么是 2 的幂次方 +### 1.5.6. HashMap 的长度为什么是 2 的幂次方 为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们上面也讲到了过了,Hash 值的范围值-2147483648 到 2147483647,前后加起来大概 40 亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个 40 亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是“ `(n - 1) & hash`”。(n 代表数组长度)。这也就解释了 HashMap 的长度为什么是 2 的幂次方。 @@ -530,17 +598,17 @@ static int hash(int h) { 我们首先可能会想到采用%取余的操作来实现。但是,重点来了:**“取余(%)操作中如果除数是 2 的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是 2 的 n 次方;)。”** 并且 **采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是 2 的幂次方。** -### 1.4.7. HashMap 多线程操作导致死循环问题 +### 1.5.7. HashMap 多线程操作导致死循环问题 主要原因在于并发下的 Rehash 会造成元素之间会形成一个循环链表。不过,jdk 1.8 后解决了这个问题,但是还是不建议在多线程下使用 HashMap,因为多线程下使用 HashMap 还是会存在其他问题比如数据丢失。并发环境下推荐使用 ConcurrentHashMap 。 详情请查看: -### 1.4.8. HashMap 有哪几种常见的遍历方式? +### 1.5.8. HashMap 有哪几种常见的遍历方式? [HashMap 的 7 种遍历方式与性能分析!](https://mp.weixin.qq.com/s/zQBN3UvJDhRTKP6SzcZFKw) -### 1.4.9. ConcurrentHashMap 和 Hashtable 的区别 +### 1.5.9. ConcurrentHashMap 和 Hashtable 的区别 `ConcurrentHashMap` 和 `Hashtable` 的区别主要体现在实现线程安全的方式上不同。 @@ -567,9 +635,9 @@ static int hash(int h) { JDK1.8 的 `ConcurrentHashMap` 不在是 **Segment 数组 + HashEntry 数组 + 链表**,而是 **Node 数组 + 链表 / 红黑树**。不过,Node 只能用于链表的情况,红黑树的情况需要使用 **`TreeNode`**。当冲突链表达到一定长度时,链表会转换成红黑树。 -### 1.4.10. ConcurrentHashMap 线程安全的具体实现方式/底层具体实现 +### 1.5.10. ConcurrentHashMap 线程安全的具体实现方式/底层具体实现 -#### 1.4.10.1. JDK1.7(上面有示意图) +#### 1.5.10.1. JDK1.7(上面有示意图) 首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。 @@ -584,13 +652,13 @@ static class Segment extends ReentrantLock implements Serializable { 一个 `ConcurrentHashMap` 里包含一个 `Segment` 数组。`Segment` 的结构和 `HashMap` 类似,是一种数组和链表结构,一个 `Segment` 包含一个 `HashEntry` 数组,每个 `HashEntry` 是一个链表结构的元素,每个 `Segment` 守护着一个 `HashEntry` 数组里的元素,当对 `HashEntry` 数组的数据进行修改时,必须首先获得对应的 `Segment` 的锁。 -#### 1.4.10.2. JDK1.8 (上面有示意图) +#### 1.5.10.2. JDK1.8 (上面有示意图) `ConcurrentHashMap` 取消了 `Segment` 分段锁,采用 CAS 和 `synchronized` 来保证并发安全。数据结构跟 HashMap1.8 的结构类似,数组+链表/红黑二叉树。Java 8 在链表长度超过一定阈值(8)时将链表(寻址时间复杂度为 O(N))转换为红黑树(寻址时间复杂度为 O(log(N))) `synchronized` 只锁定当前链表或红黑二叉树的首节点,这样只要 hash 不冲突,就不会产生并发,效率又提升 N 倍。 -## 1.5. Collections 工具类 +## 1.6. Collections 工具类 Collections 工具类常用方法: @@ -598,7 +666,7 @@ Collections 工具类常用方法: 2. 查找,替换操作 3. 同步控制(不推荐,需要线程安全的集合类型时请考虑使用 JUC 包下的并发集合) -### 1.5.1. 排序操作 +### 1.6.1. 排序操作 ```java void reverse(List list)//反转 @@ -609,7 +677,7 @@ void swap(List list, int i , int j)//交换两个索引位置的元素 void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面 ``` -### 1.5.2. 查找,替换操作 +### 1.6.2. 查找,替换操作 ```java int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的 @@ -621,7 +689,7 @@ int indexOfSubList(List list, List target)//统计target在list中第一次出 boolean replaceAll(List list, Object oldVal, Object newVal)//用新元素替换旧元素 ``` -### 1.5.3. 同步控制 +### 1.6.3. 同步控制 `Collections` 提供了多个`synchronizedXxx()`方法·,该方法可以将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题。 diff --git a/docs/java/collection/images/java-collection-hierarchy.png b/docs/java/collection/images/java-collection-hierarchy.png new file mode 100644 index 00000000000..78daf980845 Binary files /dev/null and b/docs/java/collection/images/java-collection-hierarchy.png differ diff --git "a/docs/java/new-features/\344\270\200\346\226\207\345\270\246\344\275\240\347\234\213\351\201\215JDK9\345\210\26014\347\232\204\351\207\215\350\246\201\346\226\260\347\211\271\346\200\247.md" "b/docs/java/new-features/\344\270\200\346\226\207\345\270\246\344\275\240\347\234\213\351\201\215JDK9\345\210\26014\347\232\204\351\207\215\350\246\201\346\226\260\347\211\271\346\200\247.md" index 2d56e512255..4354d219507 100644 --- "a/docs/java/new-features/\344\270\200\346\226\207\345\270\246\344\275\240\347\234\213\351\201\215JDK9\345\210\26014\347\232\204\351\207\215\350\246\201\346\226\260\347\211\271\346\200\247.md" +++ "b/docs/java/new-features/\344\270\200\346\226\207\345\270\246\344\275\240\347\234\213\351\201\215JDK9\345\210\26014\347\232\204\351\207\215\350\246\201\346\226\260\347\211\271\346\200\247.md" @@ -1,32 +1,163 @@ -大家好,我是Guide哥!这篇文章来自读者的投稿,经过了两次较大的改动,两周的完善终于完成。Java 8新特性见这里:[Java8新特性最佳指南](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484744&idx=1&sn=9db31dca13d327678845054af75efb74&chksm=cea24a83f9d5c3956f4feb9956b068624ab2fdd6c4a75fe52d5df5dca356a016577301399548&token=1082669959&lang=zh_CN&scene=21#wechat_redirect) 。 +Java 8 新特性见这里:[Java8 新特性最佳指南](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484744&idx=1&sn=9db31dca13d327678845054af75efb74&chksm=cea24a83f9d5c3956f4feb9956b068624ab2fdd6c4a75fe52d5df5dca356a016577301399548&token=1082669959&lang=zh_CN&scene=21#wechat_redirect) 。 -*Guide 哥:别人家的特性都用了几年了,我 Java 才出来,哈哈!真实!* +_Guide 哥:别人家的特性都用了几年了,我 Java 才出来,哈哈!真实!_ ## Java9 -发布于 2017 年 9 月 21 日 。作为 Java8 之后 3 年半才发布的新版本,Java 9 带 来了很多重大的变化其中最重要的改动是 Java 平台模块系统的引入,其他还有诸如集合、Stream 流 +发布于 2017 年 9 月 21 日 。作为 Java8 之后 3 年半才发布的新版本,Java 9 带 来了很多重大的变化其中最重要的改动是 Java 平台模块系统的引入,其他还有诸如集合、Stream 流 ### Java 平台模块系统 -Java 平台模块系统,也就是 Project Jigsaw,把模块化开发实践引入到了 Java 平台中。在引入了模块系统之后,JDK 被重新组织成 94 个模块。Java 应用可以通过新增的 jlink 工具,创建出只包含所依赖的 JDK 模块的自定义运行时镜像。这样可以极大的减少 Java 运行时环境的大小。 +Java 平台模块系统是[Jigsaw Project](https://openjdk.java.net/projects/jigsaw/)的一部分,把模块化开发实践引入到了 Java 平台中,可以让我们的代码可重用性更好! -Java 9 模块的重要特征是在其工件(artifact)的根目录中包含了一个描述模块的 module-info.class 文 件。 工件的格式可以是传统的 JAR 文件或是 Java 9 新增的 JMOD 文件。 +什么是模块系统?官方的定义是:A uniquely named, reusable group of related packages, as well as resources (such as images and XML files) and a module descriptor. + +简单来说,你可以将一个模块看作是一组唯一命名、可重用的包、资源和模块描述文件(module-info.java)。 + +任意一个 jar 文件,只要加上一个 模块描述文件(module-info.java),就可以升级为一个模块。 + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/module-structure.png) + +在引入了模块系统之后,JDK 被重新组织成 94 个模块。Java 应用可以通过新增的 jlink 工具,创建出只包含所依赖的 JDK 模块的自定义运行时镜像。这样可以极大的减少 Java 运行时环境的大小。 + +我们可以通过 exports 关键词精准控制哪些类可以对外开放使用,哪些类只能内部使用。 + +```java +module my.module { + //exports 公开指定包的所有公共成员 + exports com.my.package.name; +} + +module my.module { + //exports…to 限制访问的成员范围 + export com.my.package.name to com.specific.package; +} +``` + +Java 9 模块的重要特征是在其工件(artifact)的根目录中包含了一个描述模块的 `module-info.java` 文 件。 工件的格式可以是传统的 JAR 文件或是 Java 9 新增的 JMOD 文件。 + +想要深入了解 Java 9 的模块化,参见: + +- [《Project Jigsaw: Module System Quick-Start Guide》](https://openjdk.java.net/projects/jigsaw/quick-start) +- [《Java 9 Modules: part 1》](https://stacktraceguru.com/java9/module-introduction) ### Jshell jshell 是 Java 9 新增的一个实用工具。为 Java 提供了类似于 Python 的实时命令行交互工具。 -在 Jshell 中可以直接输入表达式并查看其执行结果 +在 Jshell 中可以直接输入表达式并查看其执行结果。 + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/image-20210816083417616.png) + +### 集合增强 + +增加 了 `List.of()`、`Set.of()`、`Map.of()` 和 `Map.ofEntries()`等工厂方法来创建不可变集合(这部分内容有点参考 Guava 的味道) + +```java +List.of("Java", "C++"); +Set.of("Java", "C++"); +Map.of("Java", 1, "C++", 2)`; +``` + +使用 `of()` 创建的集合为不可变集合,不能进行添加、删除、替换、 排序等操作,不然会报 `java.lang.UnsupportedOperationException` 异常。 + +`Collectors` 中增加了新的方法 `filtering()` 和 `flatMapping()`。 + +`Collectors` 的 `filtering()` 方法类似于 `Stream` 类的 `filter()` 方法,都是用于过滤元素。 + +> Java 8 为 `Collectors` 类引入了 `groupingBy` 操作,用于根据特定的属性将对象分组。 + +```java +List list = List.of("x","www", "yy", "zz"); +Map> result = list.stream() + .collect(Collectors.groupingBy(String::length, + Collectors.filtering(s -> !s.contains("z"), + Collectors.toList()))); + +System.out.println(result); // {1=[x], 2=[yy], 3=[www]} +``` + +### Stream & Optional 增强 + +`Stream` 中增加了新的方法 `ofNullable()`、`dropWhile()`、`takeWhile()` 以及 `iterate()` 方法的重载方法。 + +Java 9 中的 `ofNullable()` 方 法允许我们创建一个单元素的 `Stream`,可以包含一个非空元素,也可以创建一个空 `Stream`。 而在 Java 8 中则不可以创建空的 `Stream` 。 -### 集合、Stream 和 Optional +```java +Stream stringStream = Stream.ofNullable("Java"); +System.out.println(stringStream.count());// 1 +Stream nullStream = Stream.ofNullable(null); +System.out.println(nullStream.count());//0 +``` + +`takeWhile()` 方法可以从 `Stream` 中依次获取满足条件的元素,直到不满足条件为止结束获取。 + +```java +List integerList = List.of(11, 33, 66, 8, 9, 13); +integerList.stream().takeWhile(x -> x < 50).forEach(System.out::println);// 11 33 +``` + +`dropWhile()` 方法的效果和 `takeWhile()` 相反。 + +```java +List integerList2 = List.of(11, 33, 66, 8, 9, 13); +integerList2.stream().dropWhile(x -> x < 50).forEach(System.out::println);// 66 8 9 13 +``` + +`iterate()` 方法的新重载方法提供了一个 `Predicate` 参数 (判断条件)来决定什么时候结束迭代 + +```java +public static Stream iterate(final T seed, final UnaryOperator f) { +} +// 新增加的重载方法 +public static Stream iterate(T seed, Predicate hasNext, UnaryOperator next) { -- 增加 了 `List.of()`、`Set.of()`、`Map.of()` 和 `Map.ofEntries()`等工厂方法来创建不可变集合,比如`List.of("Java", "C++");`、`Map.of("Java", 1, "C++", 2)`;(这部分内容有点参考 Guava 的味道) -- `Stream` 中增加了新的方法 `ofNullable`、`dropWhile`、`takeWhile` 和 `iterate` 方法。`Collectors` 中增加了新的方法 `filtering` 和 `flatMapping` -- `Optional` 类中新增了 `ifPresentOrElse`、`or` 和 `stream` 等方法 +} +``` + +两者的使用对比如下,新的 `iterate()` 重载方法更加灵活一些。 + +```java +// 使用原始 iterate() 方法输出数字 1~10 +Stream.iterate(1, i -> i + 1).limit(10).forEach(System.out::println); +// 使用新的 iterate() 重载方法输出数字 1~10 +Stream.iterate(1, i -> i <= 10, i -> i + 1).forEach(System.out::println); +``` + +`Optional` 类中新增了 `ifPresentOrElse()`、`or()` 和 `stream()` 等方法 + +`ifPresentOrElse()` 方法接受两个参数 `Consumer` 和 `Runnable` ,如果 `Optional` 不为空调用 `Consumer` 参数,为空则调用 `Runnable` 参数。 + +```java +public void ifPresentOrElse(Consumer action, Runnable emptyAction) + +Optional objectOptional = Optional.empty(); +objectOptional.ifPresentOrElse(System.out::println, () -> System.out.println("Empty!!!"));// Empty!!! +``` + +`or()` 方法接受一个 `Supplier` 参数 ,如果 `Optional` 为空则返回 `Supplier` 参数指定的 `Optional` 值。 + +```java +public Optional or(Supplier> supplier) + +Optional objectOptional = Optional.empty(); +objectOptional.or(() -> Optional.of("java")).ifPresent(System.out::println);//java +``` + +### String 存储结构变更 + +JDK 8 及之前的版本,`String` 一直是用 `char[]` 存储。在 Java 9 之后,`String` 的实现改用 `byte[]` 数组存储字符串。 ### 进程 API -Java 9 增加了 `ProcessHandle` 接口,可以对原生进程进行管理,尤其适合于管理长时间运行的进程 +Java 9 增加了 `ProcessHandle` 接口,可以对原生进程进行管理,尤其适合于管理长时间运行的进程。 + +```java +System.out.println(ProcessHandle.current().pid()); +System.out.println(ProcessHandle.current().info()); +``` + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/image-20210816104614414.png) ### 平台日志 API 和服务 @@ -36,26 +167,40 @@ Java 9 允许为 JDK 和应用配置同样的日志实现。新增了 `System.Lo ### 反应式流 ( Reactive Streams ) -- 在 Java9 中的 `java.util.concurrent.Flow` 类中新增了反应式流规范的核心接口 -- Flow 中包含了 `Flow.Publisher`、`Flow.Subscriber`、`Flow.Subscription` 和 `Flow.Processor` 等 4 个核心接口。Java 9 还提供了`SubmissionPublisher` 作为`Flow.Publisher` 的一个实现。 +在 Java9 中的 `java.util.concurrent.Flow` 类中新增了反应式流规范的核心接口 。 + +`Flow` 中包含了 `Flow.Publisher`、`Flow.Subscriber`、`Flow.Subscription` 和 `Flow.Processor` 等 4 个核心接口。Java 9 还提供了`SubmissionPublisher` 作为`Flow.Publisher` 的一个实现。 ### 变量句柄 -- 变量句柄是一个变量或一组变量的引用,包括静态域,非静态域,数组元素和堆外数据结构中的组成部分等 -- 变量句柄的含义类似于已有的方法句柄`MethodHandle` -- 由 Java 类`java.lang.invoke.VarHandle` 来表示,可以使用类 `java.lang.invoke.MethodHandles.Lookup` 中的静态工厂方法来创建 `VarHandle` 对 象 +变量句柄是一个变量或一组变量的引用,包括静态域,非静态域,数组元素和堆外数据结构中的组成部分等 + +变量句柄的含义类似于已有的方法句柄 `MethodHandle` ,由 Java 类 `java.lang.invoke.VarHandle` 来表示,可以使用类 `java.lang.invoke.MethodHandles.Lookup` 中的静态工厂方法来创建 `VarHandle` 对象。 + +`VarHandle` 的出现替代了 `java.util.concurrent.atomic` 和 `sun.misc.Unsafe` 的部分操作。并且提供了一系列标准的内存屏障操作,用于更加细粒度的控制内存排序。在安全性、可用性、性能上都要优于现有的 API。 ### 改进方法句柄(Method Handle) -- 方法句柄从 Java7 开始引入,Java9 在类`java.lang.invoke.MethodHandles` 中新增了更多的静态方法来创建不同类型的方法句柄 +方法句柄从 Java7 开始引入,Java9 在类`java.lang.invoke.MethodHandles` 中新增了更多的静态方法来创建不同类型的方法句柄。 + +### 接口私有方法 + +Java 9 允许在接口中使用私有方法。 + +```java +public interface MyInterface { + private void methodPrivate(){ + + } +} +``` -### 其它新特性 +### Java9 其它新特性 -- **接口私有方法** :Java 9 允许在接口中使用私有方法 -- **try-with-resources 增强** :在 try-with-resources 语句中可以使用 effectively-final 变量(什么是 effectively-final 变量,见这篇文章 [https://ilkinulas.github.io/programming/java/2016/03/27/effectively-final-java.html](https://ilkinulas.github.io/programming/java/2016/03/27/effectively-final-java.html)) -- **类 `CompletableFuture` 中增加了几个新的方法(`completeAsync` ,`orTimeout` 等)** -- **Nashorn 引擎的增强** :Nashorn 从 Java8 开始引入的 JavaScript 引擎,Java9 对 Nashorn 做了些增强,实现了一些 ES6 的新特性 -- **I/O 流的新特性** :增加了新的方法来读取和复制 InputStream 中包含的数据 +- **try-with-resources 增强** :在 try-with-resources 语句中可以使用 effectively-final 变量(什么是 effectively-final 变量,见这篇文章:[《Effectively Final Variables in Java》](https://ilkinulas.github.io/programming/java/2016/03/27/effectively-final-java.html) +- 类 `CompletableFuture` 中增加了几个新的方法(`completeAsync` ,`orTimeout` 等) +- **Nashorn 引擎的增强** :Nashorn 从 Java8 开始引入的 JavaScript 引擎,Java9 对 Nashorn 做了些增强,实现了一些 ES6 的新特性(Java 11 中已经被弃用)。 +- **I/O 流的新特性** :增加了新的方法来读取和复制 `InputStream` 中包含的数据 - **改进应用的安全性能** :Java 9 新增了 4 个 SHA- 3 哈希算法,SHA3-224、SHA3-256、SHA3-384 和 S HA3-512 - ...... @@ -63,16 +208,45 @@ Java 9 允许为 JDK 和应用配置同样的日志实现。新增了 `System.Lo 发布于 2018 年 3 月 20 日,最知名的特性应该是 var 关键字(局部变量类型推断)的引入了,其他还有垃圾收集器改善、GC 改进、性能提升、线程管控等一批新特性 -### var 关键字 +### var(局部变量推断) -- **介绍** :提供了 var 关键字声明局部变量:`var list = new ArrayList(); // ArrayList` -- **局限性** :只能用于带有构造器的**局部变量**和 for 循环中 +由于太多 Java 开发者希望 Java 中引入局部变量推断,于是 Java 10 的时候它来了,也算是众望所归了! -_Guide 哥:实际上 Lombok 早就体用了一个类似的关键字,使用它可以简化代码,但是可能会降低程序的易读性、可维护性。一般情况下,我个人都不太推荐使用。_ +Java 10 提供了 var 关键字声明局部变量。 -### 不可变集合 +> Scala 和 Kotlin 中有 val 关键字 ( `final var` 组合关键字),Java10 中并没有引入。 -**list,set,map 提供了静态方法`copyOf()`返回入参集合的一个不可变拷贝(以下为 JDK 的源码)** +Java 10 只引入了 var,而 + +```java +var id = 0; +var codefx = new URL("https://mp.weixin.qq.com/"); +var list = new ArrayList<>(); +var list = List.of(1, 2, 3); +var map = new HashMap(); +var p = Paths.of("src/test/java/Java9FeaturesTest.java"); +var numbers = List.of("a", "b", "c"); +for (var n : list) + System.out.print(n+ " "); +``` + +var 关键字只能用于带有构造器的局部变量和 for 循环中。 + +```java +var count=null; //❌编译不通过,不能声明为 null +var r = () -> Math.random();//❌编译不通过,不能声明为 Lambda表达式 +var array = {1,2,3};//❌编译不通过,不能声明数组 +``` + +var 并不会改变 Java 是一门静态类型语言的事实,编译器负责推断出类型。 + +相关阅读:[《Java 10 新特性之局部变量类型推断》](https://zhuanlan.zhihu.com/p/34911982)。 + +### 集合增强 + +`list`,`set`,`map` 提供了静态方法`copyOf()`返回入参集合的一个不可变拷贝。 + +以下为 JDK 的源码: ```java static List copyOf(Collection coll) { @@ -80,11 +254,26 @@ static List copyOf(Collection coll) { } ``` -**`java.util.stream.Collectors`中新增了静态方法,用于将流中的元素收集为不可变的集合** +使用 `copyOf()` 创建的集合为不可变集合,不能进行添加、删除、替换、 排序等操作,不然会报 `java.lang.UnsupportedOperationException` 异常。 IDEA 也会有相应的提示。 + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/java-guide-blog/image-20210816154125579.png) + +`java.util.stream.Collectors` 中新增了静态方法,用于将流中的元素收集为不可变的集合。 + +```java +var list = new ArrayList<>(); +list.stream().collect(Collectors.toUnmodifiableList()); +list.stream().collect(Collectors.toUnmodifiableSet()); +``` ### Optional -- 新增了`orElseThrow()`方法来在没有值时抛出异常 +新增了`orElseThrow()`方法来在没有值时抛出指定的异常。 + +```java +Optional.ofNullable(cache.getIfPresent(key)) + .orElseThrow(() -> new PrestoException(NOT_FOUND, "Missing entry found for key: " + key)); +``` ### 并行全垃圾回收器 G1 @@ -98,12 +287,12 @@ static List copyOf(Collection coll) { Java 10 在现有的 CDS 功能基础上再次拓展,以允许应用类放置在共享存档中。CDS 特性在原来的 bootstrap 类基础之上,扩展加入了应用类的 CDS (Application Class-Data Sharing) 支持。其原理为:在启动时记录加载类的过程,写入到文本文件中,再次启动时直接读取此启动文本并加载。设想如果应用环境没有大的变化,启动速度就会得到提升 -### 其他特性 +### Java10 其他新特性 - **线程-局部管控**:Java 10 中线程管控引入 JVM 安全点的概念,将允许在不运行全局 JVM 安全点的情况下实现线程回调,由线程本身或者 JVM 线程来执行,同时保持线程处于阻塞状态,这种方式使得停止单个线程变成可能,而不是只能启用或停止所有线程 - - **备用存储装置上的堆分配**:Java 10 中将使得 JVM 能够使用适用于不同类型的存储机制的堆,在可选内存设备上进行堆内存分配 - **统一的垃圾回收接口**:Java 10 中,hotspot/gc 代码实现方面,引入一个干净的 GC 接口,改进不同 GC 源代码的隔离性,多个 GC 之间共享的实现细节代码应该存在于辅助类中。统一垃圾回收接口的主要原因是:让垃圾回收器(GC)这部分代码更加整洁,便于新人上手开发,便于后续排查相关问题。 +- ...... ## Java11 @@ -111,27 +300,36 @@ Java11 于 2018 年 9 月 25 日正式发布,这是很重要的一个版本! ![](https://img-blog.csdnimg.cn/20210603202746605.png) -### 字符串加强 +### String Java 11 增加了一系列的字符串处理方法,如以下所示。 -_Guide 哥:说白点就是多了层封装,JDK 开发组的人没少看市面上常见的工具类框架啊!_ +_Guide:说白点就是多了层封装,JDK 开发组的人没少看市面上常见的工具类框架啊!_ ```java //判断字符串是否为空 " ".isBlank();//true //去除字符串首尾空格 -" Java ".strip();// "Java" +" Java ".strip();// "Java" //去除字符串首部空格 -" Java ".stripLeading(); // "Java " +" Java ".stripLeading(); // "Java " //去除字符串尾部空格 -" Java ".stripTrailing(); // " Java" +" Java ".stripTrailing(); // " Java" //重复字符串多少次 -"Java".repeat(3); // "JavaJavaJava" +"Java".repeat(3); // "JavaJavaJava" //返回由行终止符分隔的字符串集合。 -"A\nB\nC".lines().count(); // 3 -"A\nB\nC".lines().collect(Collectors.toList()); +"A\nB\nC".lines().count(); // 3 +"A\nB\nC".lines().collect(Collectors.toList()); +``` + +### Optional + +新增了`empty()`方法来判断指定的 `Optional` 对象是否为空。 + +```java +var op = Optional.empty(); +System.out.println(op.isEmpty());//判断指定的 Optional 对象是否为空 ``` ### ZGC:可伸缩低延迟垃圾收集器 @@ -147,66 +345,73 @@ ZGC 主要为了满足如下目标进行设计: - 针以及 Load barriers 优化奠定基础 - 当前只支持 Linux/x64 位平台 -ZGC 目前 **处在实验阶段**,只支持 Linux/x64 平台 - -### 标准 HTTP Client 升级 - -Java 11 对 Java 9 中引入并在 Java 10 中进行了更新的 Http Client API 进行了标准化,在前两个版本中进行孵化的同时,Http Client 几乎被完全重写,并且现在完全支持异步非阻塞。 - -并且,Java11 中,Http Client 的包名由 `jdk.incubator.http` 改为`java.net.http`,该 API 通过 `CompleteableFuture` 提供非阻塞请求和响应语义。 +ZGC 目前 **处在实验阶段**,只支持 Linux/x64 平台。 -使用起来也很简单,如下: +与 CMS 中的 ParNew 和 G1 类似,ZGC 也采用标记-复制算法,不过 ZGC 对该算法做了重大改进。 -```java -var request = HttpRequest.newBuilder() - - .uri(URI.create("https://javastack.cn")) +在 ZGC 中出现 Stop The World 的情况会更少! - .GET() +详情可以看 : [《新一代垃圾回收器 ZGC 的探索与实践》](https://tech.meituan.com/2020/08/06/new-zgc-practice-in-meituan.html) - .build(); - -var client = HttpClient.newHttpClient(); +### 标准 HTTP Client 升级 -// 同步 +Java 11 对 Java 9 中引入并在 Java 10 中进行了更新的 Http Client API 进行了标准化,在前两个版本中进行孵化的同时,Http Client 几乎被完全重写,并且现在完全支持异步非阻塞。 -HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); +并且,Java11 中,Http Client 的包名由 `jdk.incubator.http` 改为`java.net.http`,该 API 通过 `CompleteableFuture` 提供非阻塞请求和响应语义。使用起来也很简单,如下: -System.out.println(response.body()); +```java +var request = HttpRequest.newBuilder() + .uri(URI.create("https://javastack.cn")) + .GET() + .build(); +var client = HttpClient.newHttpClient(); + +// 同步 +HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); +System.out.println(response.body()); + +// 异步 +client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(HttpResponse::body) + .thenAccept(System.out::println); +``` -// 异步 +### var(Lambda 参数的局部变量语法) -client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) +从 Java 10 开始,便引入了局部变量类型推断这一关键特性。类型推断允许使用关键字 var 作为局部变量的类型而不是实际类型,编译器根据分配给变量的值推断出类型。 - .thenApply(HttpResponse::body) +Java 10 中对 var 关键字存在几个限制 - .thenAccept(System.out::println); +- 只能用于局部变量上 +- 声明时必须初始化 +- 不能用作方法参数 +- 不能在 Lambda 表达式中使用 +Java11 开始允许开发者在 Lambda 表达式中使用 var 进行参数声明。 +```java +// 下面两者是等价的 +Consumer consumer = (var i) -> System.out.println(i); +Consumer consumer = (String i) -> System.out.println(i); ``` -### 简化启动单个源代码文件的方法 +### 启动单文件源代码程序 -- 增强了 Java 启动器,使其能够运行单一文件的 Java 源代码。此功能允许使用 Java 解释器直接执行 Java 源代码。源代码在内存中编译,然后由解释器执行。唯一的约束在于所有相关的类必须定义在同一个 Java 文件中 -- 对于 Java 初学者并希望尝试简单程序的人特别有用,并且能和 jshell 一起使用 -- 一定能程度上增强了使用 Java 来写脚本程序的能力 +[JEP 330:启动单文件源代码程序(aunch Single-File Source-Code Programs)](https://openjdk.java.net/jeps/330) 可以让我们运行单一文件的 Java 源代码。此功能允许使用 Java 解释器直接执行 Java 源代码。源代码在内存中编译,然后由解释器执行,不需要在磁盘上生成 `.class` 文件了。 -### 用于 Lambda 参数的局部变量语法 +唯一的约束在于所有相关的类必须定义在同一个 Java 文件中。 -- 从 Java 10 开始,便引入了局部变量类型推断这一关键特性。类型推断允许使用关键字 var 作为局部变量的类型而不是实际类型,编译器根据分配给变量的值推断出类型 -- Java 10 中对 var 关键字存在几个限制 - - 只能用于局部变量上 - - 声明时必须初始化 - - 不能用作方法参数 - - 不能在 Lambda 表达式中使用 -- Java11 开始允许开发者在 Lambda 表达式中使用 var 进行参数声明 +对于 Java 初学者并希望尝试简单程序的人特别有用,并且能和 jshell 一起使用 -### 其他特性 +一定能程度上增强了使用 Java 来写脚本程序的能力。 + +### Java11 其他新特性 -- 新的垃圾回收器 Epsilon,一个完全消极的 GC 实现,分配有限的内存资源,最大限度的降低内存占用和内存吞吐延迟时间 -- 低开销的 Heap Profiling:Java 11 中提供一种低开销的 Java 堆分配采样方法,能够得到堆分配的 Java 对象信息,并且能够通过 JVMTI 访问堆信息 -- TLS1.3 协议:Java 11 中包含了传输层安全性(TLS)1.3 规范(RFC 8446)的实现,替换了之前版本中包含的 TLS,包括 TLS 1.2,同时还改进了其他 TLS 功能,例如 OCSP 装订扩展(RFC 6066,RFC 6961),以及会话散列和扩展主密钥扩展(RFC 7627),在安全性和性能方面也做了很多提升 -- 飞行记录器:飞行记录器之前是商业版 JDK 的一项分析工具,但在 Java 11 中,其代码被包含到公开代码库中,这样所有人都能使用该功能了 +- **新的垃圾回收器 Epsilon** :一个完全消极的 GC 实现,分配有限的内存资源,最大限度的降低内存占用和内存吞吐延迟时间 +- **低开销的 Heap Profiling** :Java 11 中提供一种低开销的 Java 堆分配采样方法,能够得到堆分配的 Java 对象信息,并且能够通过 JVMTI 访问堆信息 +- **TLS1.3 协议** :Java 11 中包含了传输层安全性(TLS)1.3 规范(RFC 8446)的实现,替换了之前版本中包含的 TLS,包括 TLS 1.2,同时还改进了其他 TLS 功能,例如 OCSP 装订扩展(RFC 6066,RFC 6961),以及会话散列和扩展主密钥扩展(RFC 7627),在安全性和性能方面也做了很多提升 +- **飞行记录器(Java Flight Recorder)** :飞行记录器之前是商业版 JDK 的一项分析工具,但在 Java 11 中,其代码被包含到公开代码库中,这样所有人都能使用该功能了。 +- ...... ## Java12