概述
2018 年 3 月 21 日, Oracle 官方宣布 Java 10 正式发布。
Java 9 和 Java 10 都不是 LTS(Long-Term-Support)版本。和过去的 Java 大版本升级不同,这两个只有半年左右的开发和维护时间。而 Java 11 也是就是 18.9,才是 Java 之后的第一个长期支持版本。
Java 10 一共定义了 109 个新特性,其中包含 JEP。对程序员来说,真正的新特性也就一个,还有一些新的 API 和JVM 规范以及 Java 语言规范上的改动。
Java 10 的 12 个 JEP(JDK Enhancement Proposal 特性加强协议),可参阅官方文档openjdk.java.net/projects/jd…
具体的新增特性:
- 286:局部变量类型推断。
- 296:JDK 库合并。
- 304:统一的垃圾回收接口。
- 307:为 G1 提供并行的 Full GC。
- 310:应用程序类数据共享。
- 312:ThreadLocal 握手交互。
- 313:移除 JDK 中附带的 javah 工具。
- 314:使用附加的 Unicode 语言标记拓展。
- 316:能将对内存占用分配给用户指定的备用内存设备。
- 317:使用基于 Java 的 JIT 编译器。
- 319:根证书。
- 322:基于时间的发布版本。
语法层次变化
局部变量的显示类型声明,常常认为是不必须的,给一个好听的名字经常可以很清楚的表达出下面应该怎样继续。减少了啰嗦和形式的代码,避免了信息的冗余,而且对齐了变量名,容易阅读。
作为 Java 程序员,在声明一个变量时,我们一般都是书写两次变量类型,第一次用于声明变量类型,第二次用于构造器。
List<String> list = new ArrayList<>();
变量的声明类型书写复杂且较长,尤其是加上泛型的使用:
Iterator<Map.Entry<Integer,Student>> iterator = set.iterator();
我们也经常声明一种变量,它只被使用一次,并且是在下一行代码中:
URL url = new URL("http://www.boge.com");
URLConnection connection = url.openConnection();
Reader reader = new BufferedReader(new InputStreamReader(connection.getInputStream));
局部变量推断有点类似 JavaScript 中的弱变量类型的写法,通过后面的数据来推断前面的数据类型,数据类型的声明统一为 var:
public static void main(String[] args) {
var n = 100;
var list = new ArrayList<String>();
for (var i : list) {
// ...
}
for (var i = 0; i < 10; i++) {
// ...
}
}
变量的声明不能使用类型推断,因为无法推断:
// 根据右边的数据推断类型,不赋值压根没给推断的机会,这是错的。
var userName;
初始值为 null 的情况也是不行的。因为同样没有办法推断:
// null值无法推断数据类型,这是不能使用类型推断
var userName = null;
无法使用 var 来声明 Lambda 表达式或者方法的引用:
// 这个是可以的
Supplier<Double> supplier = ()-> Math.random();
// 这个是不行的 lambda表达式需要显式数据类型,不然无法明确是哪个接口
var supplier2 = () -> Math.random();
// 这是不可以的,无法明确接口类型
var consumer3 = System.out::println;
静态初始化数组不可以使用类型推断:
var arr2 = {"a12", "222", "333", "444", "555"};
类型推断仅仅是局部变量,成员变量不可以使用类型推断:
public class Student {
// 成员变量不能使用类型推断
var name = "小明";
public void testMethod1(){
// 局部变量可以使用类型推断
var localname = "小明";
}
}
其他不可用的场景:
// 情况1 没有初始化的局部变量声明
var s; var x = null;
// 情况2 方法的返回值类型
public var test1() {}
// 情况3 方法的参数类型
public void test2(var a, var b) {}
// 情况4 构造器的参数类型
public Person(var name, var age) {}
// 情况5 属性
class Person {
var name;
}
// 情况6 catch块
try {
} catch (var e) {
}
注意点:
- var 不是一个关键字:
我们无需担心变量名或者方法名会与 var 发生冲突,因为 var 实际上不是一个关键字,而是一个类型名,只有在编译器需要知道类型的地方法才会用到它。除此之外,他就是一个普通的合法标识符。也就是说,除了不能用它做类名,其他都可以。但是又有哪个傻瓜非要用 var 做类名呢?
- 这毕竟不是 JavaScript:
var 并不会改变 Java 是一门静态语言的事实,编译器负责推断出类型,并把结果写入字节码,也就是说,数据类型还是在字节码中的,Java 还是属于强类型的编程语言,开发人员没有明确写出来而已。而 JavaScript 是弱类型解释型的脚本语言,和这里的类型推断是两回事。
API层次变化
copyOf方法
在 JDK 10 中给集合新增了一个 copyOf
方法。用来创建只读集合:
// Java 9 中新增创建只读的方法
var s1 = List.of("Python", "JAVA", "Golang");
// Java 10 中新增的创建只读集合的方法
var s2 = List.copyOf(s1);
// 判断两个集合在内存上是否是同一个,结果为 true
System.out.println(s1 == s2);
// 创建一个普通集合
var s3 = new ArrayList<String>();
// 通过 copyOf 方法创建一个只读集合
var s4 = List.copyOf(s3);
// 判断两个集合在内存上是否是同一个,结果为 false
System.out.println(s3 == s4);
copyOf
方法的作用通过一个集合返回的是一个只读集合,如果参数本来就是只读集合,那么返回的就是参数,如果参数不是只读集合,就再创造一个只读集合返回。