java102018.3.21发布,是一个non-LTS版本,本文将介绍该版本带来的主要新特性。

java10主要新特性一览

  • 局部变量类型推断
  • 容器类新增copyOf方法
  • Reader类和InputStream类中新增transferTo方法
  • IO流类系添加带Charset类参数的方法
  • java.util.Optional类新增重载的无参orElseThrow()方法
  • G1引入并行full gc
  • 删除javah工具

局部变量类型推断

长期以来,java声明变量每次都要写两遍变量类型,第一次用于声明变量类型,第二次用于构造器。例如:

1
ArrayList<String> list2 = new ArrayList<>();

再或者遇到返回值类型带复杂泛型结构时,变量类型的编写复杂且较长,例如:

1
2
LinkedHashSet<Map.Entry<String, String>> set = new LinkedHashSet<>();
Iterator<Map.Entry<String, String>> iterator = set.iterator();

虽然我们可以直接忽略掉泛型Iterator i = set.iterator();,但是这样编译器会报黄色警告,看着很不舒服。

java10新增了var语法来进行局部变量类型推断,在定义局部变量、接收复杂泛型结构返回值及for循环中可以使用。其中在定义局部变量时必须立刻进行赋值,且等号右边的类型一定要明确;而且不能同时定义多个局部变量。具体使用示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.sunchaser.sparrow.javase.java10;

import java.util.*;

/**
* 局部变量类型推断
*
* @author sunchaser admin@lilu.org.cn
* @since JDK10 2022/2/11
*/
public class LocalVariableTypeInference {
public static void main(String[] args) {
// java7之前
ArrayList<String> list1 = new ArrayList<String>();

// java7类型推断,根据左边类型推断右边
ArrayList<String> list2 = new ArrayList<>();

// 返回值包含复杂泛型结构
LinkedHashSet<Map.Entry<String, String>> set = new LinkedHashSet<>();
Iterator<Map.Entry<String, String>> iterator = set.iterator();
// Iterator i = set.iterator(); // 编译器报黄色警告

// java10类型推断
// 声明变量时,根据等号右边的类型推断左边
var list3 = new ArrayList<Integer>();
var str = "var string";
var num = 1;
var d = 1.0;

System.out.println(str);
System.out.println(num);
System.out.println(d);

// 接收复杂泛型结构的返回值
var iter = set.iterator();

// forEach循环中使用
list2.add("java");
list2.add("go");
list2.add("python");
for (var s : list2) {
System.out.println(s);
System.out.println(s.getClass());
}

// 普通for循环中使用
list3.add(1);
list3.add(3);
list3.add(5);
for (var i = 0; i < list3.size(); i++) {
System.out.println(list3.get(i));
System.out.println(list3.get(i).getClass());
}
}
}

本质上,var只是一个语法糖,编译器在编译期进行类型推断,然后写入.class字节码文件中。上述示例代码对应的字节码文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.sunchaser.sparrow.javase.java10;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map.Entry;

public class LocalVariableTypeInference {
public LocalVariableTypeInference() {
}

public static void main(String[] args) {
new ArrayList();
ArrayList<String> list2 = new ArrayList();
LinkedHashSet<Entry<String, String>> set = new LinkedHashSet();
Iterator<Entry<String, String>> iterator = set.iterator();
ArrayList<Integer> list3 = new ArrayList();
String str = "var string";
int num = 1;
double d = 1.0D;
System.out.println(str);
System.out.println(num);
System.out.println(d);
Iterator<Entry<String, String>> iter = set.iterator();
list2.add("java");
list2.add("go");
list2.add("python");
Iterator var11 = list2.iterator();

while(var11.hasNext()) {
String s = (String)var11.next();
System.out.println(s);
System.out.println(s.getClass());
}

list3.add(1);
list3.add(3);
list3.add(5);

for(int i = 0; i < list3.size(); ++i) {
System.out.println(list3.get(i));
System.out.println(((Integer)list3.get(i)).getClass());
}

}
}

容器类新增copyOf方法

java10List/Set/Map容器接口中新增了一个copyOf静态方法,该方法将一个容器复制为另一个新的不可变容器。

代码示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.sunchaser.sparrow.javase.java10;

import java.util.*;

/**
* List Map Set 容器类新增copyOf方法
*
* @author sunchaser admin@lilu.org.cn
* @since JDK10 2022/2/15
*/
public class CollectionCopyOf {
public static void main(String[] args) {
copyOfList();

copyOfSet();

copyOfMap();
}

private static void copyOfMap() {
Map<String, String> map = new HashMap<>();
map.put("java", "1");
map.put("python", "2");
map.put("go", "3");
Map<String, String> copyOfMap = Map.copyOf(map);
for (Map.Entry<String, String> entry : copyOfMap.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
// 不可变Map,无法修改
// copyOfMap.put("php", "0");
}

private static void copyOfSet() {
Set<String> set = new HashSet<>();
set.add("java");
set.add("python");
set.add("go");
Set<String> copyOfSet = Set.copyOf(set);
for (String s : copyOfSet) {
System.out.println(s);
}
// 不可变Set,无法修改
// copyOfSet.add("php");
}

private static void copyOfList() {
List<String> list = new ArrayList<>();
list.add("java");
list.add("python");
list.add("go");
List<String> copyOfList = List.copyOf(list);
for (String s : copyOfList) {
System.out.println(s);
}
// 不可变List,无法修改
// copyOfList.add("php");
}
}

查看copyOf源码可知,实际上内部调用的是java9新增的of方法得到一个不可变容器。例如List.copyOf()源码如下:

1
2
3
4
5
6
7
static <E> List<E> copyOf(Collection<? extends E> coll) {
if (coll instanceof ImmutableCollections.AbstractImmutableList) {
return (List<E>)coll;
} else {
return (List<E>)List.of(coll.toArray());
}
}

Reader类和InputStream类中新增transferTo方法

java10之前如果需要复制文件,需要自己定义数组,循环读取输入流并写到输出流中,代码比较麻烦:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static final String FILE = Objects.requireNonNull(TransferTo.class.getResource("/")).getFile();

private static void copyFileBeforeJava10() {
try {
// 复制source.txt文件到target1.txt
FileReader fr = new FileReader(FILE + "source.txt");
FileWriter fw = new FileWriter(FILE + "target1.txt");
char[] chs = new char[1024 * 8];
int len;
while ((len = fr.read(chs)) != -1) {
fw.write(chs, 0, len);
}
fr.close();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}

java10InputStream类和Reader类中提供了transferTo方法,将输入流读取的数据使用使用字符输出流写出。可用于快速实现文件复制操作。代码示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static final String FILE = Objects.requireNonNull(TransferTo.class.getResource("/")).getFile();

private static void copyFileInJava10() {
try {
// 复制source.txt文件到target2.txt
FileReader fr = new FileReader(FILE + "source.txt");
FileWriter fw = new FileWriter(FILE + "target2.txt");

fr.transferTo(fw);

fr.close();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}

查看java.io.Reader#transferTo方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
public long transferTo(Writer out) throws IOException {
Objects.requireNonNull(out, "out");
long transferred = 0;
char[] buffer = new char[TRANSFER_BUFFER_SIZE];
int nRead;
while ((nRead = read(buffer, 0, TRANSFER_BUFFER_SIZE)) >= 0) {
out.write(buffer, 0, nRead);
transferred += nRead;
}
return transferred;
}

实际上就是对之前版本写法的封装,更加方便开发者。

IO流类系添加带Charset类参数的方法

java10中给PrintStreamPrintWriterScanner等类添加了带java.nio.charset.Charset参数的构造器,通过java.nio.charset.Charset可以指定IO流操作文件时的编码。

PrintStream往指定文件中写字符为例,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.sunchaser.sparrow.javase.java10;

import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;

/**
* IO流类系添加Charset参数
*
* @author sunchaser admin@lilu.org.cn
* @since JDK10 2022/2/15
*/
public class IOCharset {
public static void main(String[] args) {
try {
PrintStream ps = new PrintStream("/Users/sunchaser/workspace/idea-projects/sunchaser-sparrow/java-se/java10/src/main/resources/ps.txt", StandardCharsets.UTF_8);
ps.println("io charset 参数");
ps.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

Tips: Charset是一个抽象类,我们无法直接创建其对象,可以使用java.nio.charset.StandardCharsets类当中预定义好的常见一些编码格式,或者使用Charset.forName("编码")静态方法得到其子类实例。

java.util.Optional类新增重载的无参orElseThrow()方法

java10中新增了重载的无参orElseThrow()方法,当value非空时,返回value;否则抛出NoSuchElementException("No value present")异常。

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.sunchaser.sparrow.javase.java10;

import java.util.Optional;

/**
* java10中java.util.Optional类新增orElseThrow()无参方法
*
* @author sunchaser admin@lilu.org.cn
* @since JDK10 2022/2/17
*/
public class OptionalMethod {
public static void main(String[] args) {
Optional<Object> empty = Optional.empty();
Optional<String> op = Optional.of("java");
// Exception in thread "main" java.util.NoSuchElementException: No value present
// Object orElseThrow = empty.orElseThrow();
String elseThrow = op.orElseThrow();
System.out.println(elseThrow);// java
}
}

G1引入并行full gc

G1收集器是被设计用来避免虚拟机进行full gc的,当并发收集器回收内存太慢时虚拟机就会进行一次full gcG1收集器从java7开始被引入,在java9中是默认的垃圾回收器,但是此时G1full gc采用的是单线程标记-清除-整理算法,比较影响性能。所以在java10中,引入了并行标记-清除-整理算法,使用的线程数与YoungMixed收集器一致,线程数量可以通过-XX:ParallelGCThreads参数来配置,这也会影响YoungMixed收集器使用的线程数。

删除javah工具

javah用于生成c语言的头文件。从java8开始,javah的功能已经集成到javac中,所以删除javah工具。

1
javac -h /path/ 文件名.java