目录
- 传送门
- 一、局部变量类型推断
- 1、概念
- 1.1、简单局部变量推断
- 1.2、自定义对象的推断
- 2、可以使用的场景
- 3、不能使用的场景
- 4、注意事项
- 二、垃圾回收器的优化
- 1、前置知识
- 1.1、10种垃圾回收器
- 1.2、分代与分区
- 1.3、10种垃圾回收器小总结
- 2、优化点
- 三、新增API
- 1、集合新方法copyOf
- 2、ByteArrayOutputStream新增toString方法
- 3、IO流很多类新增带Charset参数的构造方法
- 4、InputStream和Reader新增transferTo方法
- 5、Formatter和Scanner新增带Charset参数的构造方法
传送门
JDK8新特性
JDK9新特性
JDK10新特性
JDK11新特性
JDK12新特性
JDK13新特性
JDK14新特性
JDK15新特性
JDK16新特性
JDK17新特性
JDK18新特性
JDK19新特性
JDK20新特性
JDK21新特性
JDK10提供了超过109项新功能特性,并且JDK10的安装不用再额外安装一次jre了,之前JDK9以及之前都需要额外安装一次jre。
一、局部变量类型推断
1、概念
JDK10 可以使用 var 进行 局部变量类型推断。
1.1、简单局部变量推断
var str = "abc"; // 推断为 字符串类型
var l = 10L; // 推断为long 类型
var flag = true; // 推断为 boolean 类型
var list = new ArrayList<String>(); // 推断为 ArrayList<String>
var stream = list.stream(); // 推断为 Stream<String>
1.2、自定义对象的推断
package com.zt.jdk10NewFeatures;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
public class Jdk10NewFeatures {
// 局部变量类型推断
// 1、只针对局部变量
// 2、var是保留类型不是关键字,意味着我们可以用var来定义变量名
// 3、var不容许赋值为null,例如 var x = null; 会报错
public static void main(String[] args) {
var i = 10;
System.out.println(i);// 10
var str = "zt";
System.out.println(str);// zt
var list = new ArrayList<>();
list.add(10);
list.forEach(System.out::println);// 10
var set = new HashSet<>();
set.add(10);
set.forEach(System.out::println);// 10
var map = new HashMap<>();
map.put("JDK10",10);
map.forEach((k,v) -> {
System.out.println("key:" + k + ",value:" + v);// key:JDK10,value:10
});
var user = new User("zt",18);
System.out.println(user);// User{username='zt', age=18}
}
// 内部类User
static class User {
private String username;
private Integer age;
public User() {
}
public User(String username, Integer age) {
this.username = username;
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age=" + age +
'}';
}
}
}
2、可以使用的场景
- 局部变量
- 循环内
public class demo01var {
// var x = 10; // 成员变量不能使用var
public static void main(String[] args) {
var a = 1;
var str = "abc";
var flag = true;
var list = new ArrayList<String>();
list.add("aa");
var stream = list.stream();
for (var s : list) {
System.out.println(s);
}
for (var i = 0; i < 10; i++) {
var x = 5;
}
}
}
3、不能使用的场景
- 成员变量
- 方法参数
- 方法返回类型
public class demo01var {
// var x = 10; // 成员变量不能使用var
// 参数不能使用var
// public static void test01(var a) {}
/* 方法返回类型不能使用var
public static var test02() {
return true;
}*/
}
4、注意事项
var 并不是一个关键字,可以作为标识符,这意味着可以将一个变量、方法、包名写成 var 。不过一般情况下
不会有人这么写的,因为这本身就违反了普遍的命名规范。
// var 并不是一个关键字,而是一个保留的类型名称,这意味着可以将一个变量、方法、包名写成 `var`。
public static void test03() {
var var = 10; System.out.println("var = " + var);
}
var 声明变量的时候必须赋值、不能用于声明多个变量的情况。
// var 不能用来声明没有赋值的变量、不能用于声明多个变量的情况。
public static void test04() {
// var x = null;
// 不行,推断不出到底是什么类型
int x = 1, y = 2; // 可以
// var m = 1, n = 2; // 不行
}
二、垃圾回收器的优化
1、前置知识
1.1、10种垃圾回收器
1.2、分代与分区
1.3、10种垃圾回收器小总结
- 1、Serial是串行,现在cpu都多核了,所以一般不用;
- 2、parNew是多线程,一般用于年轻代,和CMS(老年代)一起搭配使用;
- 3、CMS只用于老年代收集,它的STW非常短,一般的可达性算法分析,会从gc root 根到末端,它一开始初始标记只标第一段,比如根到引用A,引用A后面其实还有更深更多的引用在第一阶段不去标记,这样会让初始标记的stw非常短,第二阶段再去标记A后面更多更深的引用,但是第二阶段就不用STW了。缺点就是第二阶段剩余标记的时候,实时性没有那么强。第三阶段重新标记,就是重新标记二阶段新产生的对象引用,这阶段也会stw(比第一阶段长一点,这次是完整的根可达性,不过也比第一+第二阶段总时长短很多了),第四阶段就是并发清理,这个不会stw。
CMS缺点:标记过程中会和用户进程抢资源;无法处理浮动垃圾(第四阶段也会有实时性问题,无法及时清理新产生的);使用的标记清楚算法,会产生大量的内存碎片;会出现CMS一边收集一边回收,没回收完老年代空间不够了触发了新的Full gc,会导致并发模式失败的现象,此时会用其他垃圾回收器了; - 4、G1一般用于堆内存10多个G以上的大型公司上,一般公司用的少,年轻代和老年代内存不连续,是堆内存里面一个格子一个格子(见上面G1截图),工作原理和CMS惊人相似,前3阶段标记处理和 CMS一样,4阶段是筛选回收,前3阶段的时候要对每个格子回收时间和成本做计算和排序,4阶段回收的是部分,是成本低的那种先回收了,这个也是G1的王牌标志和牛逼之处。年轻代和老年代都是复制算法。比如E格子和S格子都满了,就会找相近的格子复制有效对象进入形成新的S格子,E格子和S格子就清空了,而且由于格子是一片连续的内存,所以还没有内存碎片问题。而且会发现有多个E格子,所以说一个E格子满了,不会马上年轻代gc,而且会计算设置好的gc时间值,如果小于这个时间,就继续第二个E格子,啥时候E格子满了,gc时间和设置好时间差不多了,就会真正的去年轻代gc。
截图可以看出早期都是配对的。单线程1GB内存以后就效率非常差了。
- Serial 和Serial Old(单线程的,分代模型);
- Parallel Scavenge 和 Parallel Old(多线程的,分代模型,jdk8默认的) ;
- ParNew 和 CMS(前面多线程耗时太多,减少耗时的, ParNew出现也是为了和CMS搭配,分代模型);
上面都是基于分代,分代(年轻代、老年代)的严重缺点还是会随着堆内存增加而stw时间增加;所以后面出了分区模型
- G1(就是上面格子的那种,一个格子就是一个片区,一块连续的内存空间。标记清除的算法。)
- ZGC(和其他9种gc不一样,是划时代的gc)
- Shenandoah
- Eplison 这个不是真正的去垃圾回收的,只是为了调试用的。
java -XX:+PrintCommandLineFlags -version ---- 查看默认gc,jdk8的默认是 Parallel Scavenge 和 Parallel Old 这一对 , jdk9默认就是g1了
2、优化点
G1 是设计来作为一种低延时的垃圾回收器。G1收集器还可以进行非常精确地对停顿进行控制。从JDK7开始启用G1垃圾回收器,在JDK9中G1成为默认垃圾回收策略。截止到java 9,G1的Full GC采用的是单线程算法。也就是说G1在发生Full GC时会严重影响性能。
JDK10又对G1进行了提升,G1 引入并行 Full GC算法,在发生Full GC时可以使用多个线程进行并行回收。能为用户提供更好的体验。
三、新增API
1、集合新方法copyOf
JDK10 给 java.util 包下的List、Set、Map新增加了一个静态方法 copyOf 。copyof方法将元素放到一个不可修改的集合并返回,是按照之前集合的迭代顺序拷贝的,元素顺序和之前集合一样。
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
List<Integer> temp = List.copyOf(list);
temp.forEach(System.out::println);// 1 2 3
temp.add(4);// 不能修改,会报错,如下图
}
不可修改这个类似JDK9新特性 集合新增的of方法。
2、ByteArrayOutputStream新增toString方法
JDK10给 java.io.ByteArrayOutputStream 新增重载 toString(Charset charset) 方法,通过指定的字符集编码字节,将缓冲区的内容转换为字符串。
通过ByteArrayOutputStream新增的toString(Charset), 可以将字节数组输出流中的数据按照指定的编码转成字符串。可以更灵活的解决乱码问题。
String str = "蓝影铁哥";
ByteArrayInputStream bis = new ByteArrayInputStream(str.getBytes("GBK"));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int b;
while ((b = bis.read()) != -1) {
bos.write(b);
}
//默认utf-8
System.out.println(bos.toString());// ��Ӱ����
System.out.println(bos.toString("GBK"));// 蓝影铁哥
3、IO流很多类新增带Charset参数的构造方法
在JDK10中给IO流中的很多类都添加了带Charset参数的方法,比如PrintStream、PrintWriter。通过Charset可以指定编码来操作文本。Charset是一个抽象类,我们不能直接创建对象,需要使用Charset的静态方法forName(“编码”),返回Charset的子类实例。
例如:PrintStream
// IO流很多类新增带Charset参数的构造方法
public static void main(String[] args) throws Exception {
String str = "蓝影铁哥";
var p = new PrintWriter("d:/fe.txt","utf-8");// 将字符串输出到d盘的fe.txt文件中
p.println(str);
p.flush();
p.close();
}
这个文本内容的编码就是代码中的utf-8编码,如果代码改成gbk,这文本内容编码也会变成gbk。
4、InputStream和Reader新增transferTo方法
JDK10 给 InputStream 和 Reader 类中新增了 transferTo 方法, transferTo 方法的作用是将输入流读取的数据使用字符输出流写出。可用于复制文件等操作。
例如 Reader:
从这个Reader中读取所有字符串,并按照所读取的顺序将字符串写入给指定的Writer。
// InputStream和Reader新增了transferTo方法
public static void main(String[] args) throws Exception {
var reader = new BufferedReader(new InputStreamReader(new FileInputStream("d:/fe.txt")));
var p = new PrintWriter(new File("d:/feNew.txt"));
reader.transferTo(p);// 从这个Reader中读取所有字符串,并按照所读取的顺序将字符串写入给指定的Writer。
p.flush();
p.close();
reader.close();
}
5、Formatter和Scanner新增带Charset参数的构造方法
在JDK10中给java.util.Formatter、java.util.Scanner添加了带Charset参数的构造方法。
例如:Scanner
// Formatter和Scanner新增了带Charset参数的构造方法
public static void main(String[] args) throws Exception {
var scanner = new Scanner(new FileInputStream(new File("d:/fe.txt")),"utf-8");// 之前写入的一行文字: 蓝影铁哥
while (scanner.hasNext()){
System.out.println(scanner.nextLine());// 蓝影铁哥
}
}