Java新特性梳理——Java10

14 阅读5分钟

概述

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 方法的作用通过一个集合返回的是一个只读集合,如果参数本来就是只读集合,那么返回的就是参数,如果参数不是只读集合,就再创造一个只读集合返回。