JDK10
1 局部变量类型推断
1.1 JDK10之前定义变量存在的问题
- 很多人抱怨Java是一种强类型,需要引入大量的样板代码。很明显类型声明往往被认为不是必要的。
- JDK10之前的Java代码总中,声明一个变量是非常繁琐的:
package com.sunxiaping;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
* @author 许大仙
* @version 1.0
* @since 2020-12-01 10:08
*/
public class VariableDemo {
/**
* 每次定义变量都需要在变量名的左边声明类型,很麻烦。
*/
@Test
public void test() {
String str = "abc";
int i = 10;
Boolean flag = false;
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
}
}
- 许多流行的编程语言都已经支持某种形式的局部变量类型推断,如JS中的var类型。
1.2 JDK10局部变量类型推断的使用
- JDK10可以使用var进行局部变量类型推断:
package com.sunxiaping;
import org.junit.Test;
import java.util.ArrayList;
/**
* @author 许大仙
* @version 1.0
* @since 2020-12-02 11:21
*/
public class VariableDemo {
@Test
public void test(){
var str = "abc";
var i = 10;
var flag = false;
var list = new ArrayList<>();
var stream = list.stream();
}
}
1.3 局部变量类型推断的场景
- 1️⃣局部变量。
package com.sunxiaping;
import org.junit.Test;
/**
* 局部变量类型推断使用场景
*
* @author 许大仙
* @version 1.0
* @since 2020-12-02 11:21
*/
public class VariableDemo {
/**
* 局部变量类型推断使用场景之:局部变量
*/
@Test
public void test2() {
var x = "abc";
System.out.println("x = " + x);
}
}
- 2️⃣循环内。
package com.sunxiaping;
import org.junit.Test;
/**
* 局部变量类型推断使用场景
*
* @author 许大仙
* @version 1.0
* @since 2020-12-02 11:21
*/
public class VariableDemo {
/**
* 局部变量类型推断使用场景之:循环内
*/
@Test
public void test2() {
for (var x = 0; x < 100; x++) {
var str = "abc";
System.out.println("x = " + str + x);
}
}
}
1.4 局部变量类型推断不能使用的场景
- 1️⃣成员变量。
- 2️⃣方法参数。
- 3️⃣方法返回值。
1.5 局部变量类型推断的注意事项
- 1️⃣var不是关键字,只是一个保留字,意味着var可以作为类名、变量名使用,但是不建议。
- 2️⃣var定义变量时必须立刻赋值,不然Java编译器怎么推断类型呢。
- 3️⃣var不能同时定义多个变量。
2 将JDK的多个代码仓库合并到一个存储库中
2.1 该特性的动机
- 多年以来,JDK的完整代码库已经被分解到许多的仓库中。在JDK9中,JDK的源码被分为8个仓库:root、corba、hotspot、jaxp、jaxws、jdk、langtools和nashorn。JDK的源码分成多个仓库这种情况在支持各种所需的源代码管理操作方面做得很差。特别是,不可能在相互依赖的变更集的存储库之间执行原子提交。比如:如果今天用于单个错误修复或者RFE的代码跨越了jdk和hotspot仓库,则无法在托管这两个不同仓库的目录中原子的对两个仓库进行更改。跨越多个存储库的更改是很常见的。
2.2 好处
- 在JDK10中这些将被合并为一个,使得跨相互依赖的变更集的存储库运行atomic commit(原子提交)成为可能。
3 垃圾回收器接口
3.1 该特性的动机
- 当前,垃圾回收器的代码分散,不方便新增新的垃圾回收器,也不利于移除现有垃圾回收器。
- 通过引入一个干净的垃圾回收器接口来改善不同垃圾回收器的源代码的隔离。
3.2 该新特性的目标
- JVM内部GC代码的更好模块化。
- 使得在不影响代码库的情况下向JVM添加新的GC变得更加简单。
- 使得从JVM构建中移除GC更容易。
4 G1引入并行Full GC
4.1 该特性的动机
- G1是设计来作为一种低延时的垃圾回收器。G1收集器还可以进行非常准确的对停顿进行控制。从JDK7开始启用垃圾回收器,在JDK9中G1成为默认垃圾回收策略。截止到Java9,G1的Full GC采用的是单线程的算法,也就是说G1在发生Full GC时会严重影响性能。
4.2 该特性的效果
- JDK10对G1进行了提升,G1引入并行Full GC算法,在发生Full GC时可以使用多个线程进行并行回收,能为用户提供更好的体验。
5 应用程序类数据共享
5.1 该特性的动机
- JDK5中引入的类数据共享,将一组类预处理为共享的存档文件,然后可以在运行时对其进行内存映射以减少启动时间。当多个JVM共享同一个存档文件时,它还可以减少动态内存占用。
- JDK5仅允许引导类加载器加载归档的类。JDK10对应用程序类数据共享进行了扩展,允许应用程序类加载器、内置平台类加载器和自定义类加载器加载已归档的类。
5.2 该特性的目标
- 通过在不同的Java进程之间共享通用的类元数据以便减少占用空间。
- 缩短程序启动时间。
6 线程本地握手
6.1 该特性的动机
- Safepoint是Hotspot JVM中一种让应用程序所有线程停止的机制。为了要做一些非常之安全的事情,需要让所有线程都停下来它才好做。比如菜市场,人来人往,有人忽然要清点人数,这时候,最好就是大家都原地不动,这样好统计。Safepoint起到的就是这样一个作用。
- JVM会设置一个全局的状态值。应用线程去观察这个值,一旦发现JVM把此值都设置为“大家都停下来”,此时每个应用程序就会得到这个通知,然后各自选择一个point(点)停了下来,这个point就叫Safepoint。待所有的应用线程都停了下来,JVM就开始做了一些安全级别非常高的事情了。
- 比如:垃圾清理暂停,类的热更新,偏向锁的取消,各种debug的操作,然后,让所有线程都到就近的Safepoint停下来本来就需要较长的时间,而且让所有线程都停止下来显得非常粗暴。
- 为此,JDK10引入了一种可以不同stop all threads的方式,就是Thread Local Handshake(线程本地握手)。
6.2 该特性的效果
- 线程本地握手是在JVM内部相当低级别的更改,修改安全点机制,允许在不运行全局虚拟机安全点的情况下实现线程回调,使得部分回调操作只需要停掉单个线程,而不是停止所有线程。
7 基于Java的实验性JIT编译器
7.1 该特性的动机
- Java编译器指的是JDK自带的javac指令。javac指令可以将Java源程序编译成.class字节码文件。字节码无法直接运行,但是可以在不同平台JVM中的解释器解释执行。由于一个java指令可能被翻译为十几或几十个对等的微处理器指令,这种模式执行的速度相当缓慢。
7.2 该特性的目的
- 由于解释器效率低下,JVM中增加了JIT编译器,会在运行的时候有选择的将运行次数较多的方法编译为二进制代码,直接运行底层硬件上。花费少许的编译时间来节省稍后相当长的执行时间,JIT这种设计的确增加不少效率,但是它并没有达到最顶尖的效能,因为某些极少执行的java指令在JIT编译的时候额外花费的时间可能比解释器解释执行的时间还长,针对这些指令而言,整体花费的时间并没有减少。
- Graal是基于Java的JIT编译器,这项JEP将Graal编译器研究项目引入到JDK中。为了让JVM性能和当前C++所写版本匹敌提供基础。
8 删除javah工具
8.1 什么是javah?
- javah是用于生成C语言的头文件。
8.2 该特性的动机
- 从JDK8开始,javah的功能已经集成到了javac中,并去掉了javah工具。
javac -h . 文件名.java
9 JDK10新增API
9.1 集合新增copyof方法
-
JDK10给java.util包下的List、Set和Map新增了一个静态方法copyof,copyof方法将元素放到一个不可修改的集合并返回。
-
示例:
package com.sunxiaping;
import java.util.ArrayList;
import java.util.List;
/**
* @author 许大仙
* @version 1.0
* @since 2020-12-02 15:20
*/
public class ListDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
//copyof会将集合中的元素复制到一个新集合中,这个新集合是不可变的集合,不能进行增删改
List<String> copyOfList = List.copyOf(list);
System.out.println("copyOfList = " + copyOfList);
// copyOfList.add("ee"); //java.lang.UnsupportedOperationException
}
}
9.2 InputStream和Reader新增transferTo方法
-
JDK10给InputStream和Reader类中新增了transferTo方法,transferTo方法的作用是将输入流读数的数据使用输出流写出,常用于复制文件等操作。
-
示例:
package com.sunxiaping;
import java.io.*;
/**
* @author 许大仙
* @version 1.0
* @since 2020-12-02 15:31
*/
public class InputStreamDemo {
public static void main(String[] args) {
try (InputStream inputStream = new FileInputStream("G:\\test.txt");
OutputStream outputStream = new FileOutputStream("G:\\test1.txt")) {
inputStream.transferTo(outputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
}
9.3 IO流添加Charset参数的方法
-
IO流中的很多类都添加了Charset参数,比如PrintStream、PrintWriter、Scanner等,通过Charset可以指定IO流操作文本时的编码。
-
示例:
package com.sunxiaping;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.Charset;
/**
* @author 许大仙
* @version 1.0
* @since 2020-12-02 15:45
*/
public class PrintStreamDemo {
public static void main(String[] args) throws IOException {
PrintStream printStream = new PrintStream("G://test.txt", Charset.forName("UTF-8"));
printStream.print("你好啊");
printStream.close();
}
}
9.4 ByteArrayOutputStream新增toString方法
-
JDK10给ByteArrayOutputStream新增了重载的toString(Charset charset)方法,通过指定的字符集编码,将缓冲区的内容给转换为字符串。
-
示例:
package com.sunxiaping;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* @author 许大仙
* @version 1.0
* @since 2020-12-02 16:09
*/
public class Demo {
public static void main(String[] args) throws IOException {
String str = "你好";
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = bais.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
System.out.println(baos.toString(StandardCharsets.UTF_8));
bais.close();
baos.close();
}
}