从JDK8到JDK17

从JDK8到JDK17

JDK9

从Java8到Java17(一)
JDK9 新特性

Java 9 新特性

模块系统

JDK9引入了一个新的特性叫做JPMS(Java Platform Module System),也可以叫做Project Jigsaw。模块化的本质就是将一个大型的项目拆分成为一个一个的模块,每个模块都是独立的单元,并且不同的模块之间可以互相引用和调用。

在module中会有元数据来描述该模块的信息和该模块与其他模块之间的关系。这些模块组合起来,构成了最后的运行程序。

class是字段和方法的集合,packageclass的集合,而modulepackage的集合

没有怎么看明白。。。有时间再补

JDK9的新特性:JPMS模块化

多版本兼容jar

JDK9 新特性

接口私有方法

可以在接口中写私有方法了,私有方法不需要覆写,别的实现类也无法调用。个人感觉主要是给默认方法使用的,避免重复代码。

快速创建只读集合

Collection下的各个类均可以使用of方法快速创建只读集合

利用该方法创建出来的集合,不能进行增、删、改操作,均会抛出异常。

示例:

Set<String> set = Set.of("a", "b", "c");
Map<String, String> map = Map.of("k1", "v1", "k2", "v2", "k3", "v3");
List<String> list = List.of("A", "B", "C");

String类底层数据结构变更

String、StringBuffer、StringBuilder类,

final char[] value ==> final byte[] value

限制单独使用 _ 特殊字符

​ 在JDK8之前可以使用_单独的下划线作为标识符。

String _ = "Hello";
System.out.println(_);

输出:

Hello

JDK9中则编译不通过。

try-with-resource改进

// 可以在try外进行初始化,在括号内引用,即可实现资源自动关闭
InputStreamReader reader = new InputStreamReader(System.in);
OutputStreamWriter writer = new OutputStreamWriter(System.out);
// 多资源用分号隔开
try (reader; writer) {
    // ...
} catch (IOException e) {
    // ...
}

Stream API 增强

takeWhile()

takeWhile可以用于从 Stream 中获取一部分数据,接受一个 Predicate 来进行选择,在有序的 Stream 中,takeWhile 返回从头开始的尽可能多的元素。

List<Integer> list = Arrays.asList(45,43,76,87,42,77,90,73,67,88);
list.stream().takeWhile(x -> x < 80 ).forEach(System.out::println);

返回结果:

45
43
76

从返回结果可以看出,takeWhile将会按照list集合有序的从45开始到第一个不符合条件为止的所有结果。

看起来takeWhilefilter功能很类似,但还是有一些区别的。

Stream.of(1,2,3,4,5,6,7,8,9,10,9,8,7,6,5,4,3,2,1)
    .filter(i -> i < 4 )
    .forEach(System.out::print);

输出:

123321
Stream.of(1,2,3,4,5,6,7,8,9,10,9,8,7,6,5,4,3,2,1)
    .takeWhile(i -> i < 4 )
    .forEach(System.out::print);

输出:

123

由此可以看出

filter将从流中删除不满足条件的所有项目,即允许所有符合条件的元素通过。

takeWhile将在第一次出现不满足条件的项目时中止流(一定要注意这个,否则容易写bug),同样允许符合条件的元素通过,但是一旦遇到第一个不满足条件的,则后续的都不允许通过了,不处理了。

dropWhile()

dropWhile 的方法刚好与takeWhile想法,返回剩余的元素。

List<Integer> list = Arrays.asList(45,43,76,87,42,77,90,73,67,88);
list.stream().dropWhile((x) -> x < 80 ).forEach(System.out::println);	

输出:

87
42
77
90
73
67
88

从返回结果可以看出,dropWhile方法刚好和takeWhile方法形成互补,按照list集合有序的返回从第一个不满足条件元素开始到最后为止的所有结果。

ofNullable()

JDK8Stream 不能完全为null,否则会报空指针异常。而在JDK9ofNullable 方法允许创建一个为空的 Stream

// JDK8 NullPointerException
// Stream<Object> stream1 = Stream.of(null);
// System.out.println(stream1.count());

// 不报异常  允许这样写
        Stream<String> stringStream = Stream.of("AA", "BB", null);
        System.out.println(stringStream.count());
        
// 不报异常  允许这样写
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add(null);
        System.out.println(list.stream().count());
        
// ofNullable()允许值为 null
        Stream<Object> stream = Stream.ofNullable(null);
        System.out.println(stream.count());
        Stream<String> stream2 = Stream.ofNullable("Hello World");
        System.out.println(stream2.count());

输出:

3
2
0
1

iterate()重载方法

// JDK8 使用iterate方法,需配合limit截止。
Stream.iterate(1, (x) -> x + 1).limit(10).forEach(System.out::print);

// JDK9 使用iterate的重载方法可以直接使用Predicate来截止。
Stream.iterate(1,(x) -> x <= 10, (x) -> x + 1).forEach(System.out::print);

Optional类优化

Optional 类是在JDK8中新增的类,主要是为了解决空指针异常。在JDK9中对这个类进行了改进,主要是新增了三个方法:stream()ifPresentOrElse()or()

stream()

stream方法将Optional转为一个 Stream,如果Optional 没有值就返回一个 Stream.empty

   // stream 方法源码
	public Stream<T> stream() {
        if (!isPresent()) {
            return Stream.empty();
        } else {
            return Stream.of(value);
        }
    }

示例:

List<String> list = List.of("A", "B", "C", "D", "E", "F");
Optional<List<String>> optional = Optional.of(list);
optional.stream().forEach(System.out::println);
Optional<Object> optional1 = Optional.empty();
System.out.println(optional.stream().count());

输出:

[A, B, C, D, E, F]
1

ifPresentOrElse()

    // ifPresentOrElse 方法源码
	public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
        if (value != null) {
            action.accept(value);
        } else {
            emptyAction.run();
        }
    }

接收两个参数,一个是Consumer, 另一个是Runnable,当value不为null时,则执行Consumeraccept方法,否则执行Runnablerun方法,此处并没有使用多线程。

示例:

// 如果optional包含值,执行action.accept方法。
Optional<Integer> optional = Optional.of(1);
optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() ->
System.out.println("没有值."));

optional = Optional.empty();
// 如果optional不包含值,执行emptyAction.run方法。
optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() ->
        System.out.println("没有值."));

输出:

Value: 1
没有值.

or()

如果Optional有值,返回 Optional 指定的值,否则返回一个预设的值。

// or 方法源码,注意这里的返回值类型是一个Optional    
public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) {
        Objects.requireNonNull(supplier);
        if (isPresent()) {
            return this;
        } else {
            @SuppressWarnings("unchecked")
            Optional<T> r = (Optional<T>) supplier.get();
            return Objects.requireNonNull(r);
        }
    }

JDK10

JDK10新特性

局部变量类型推断

JDK10 可以使用var作为局部变量类型推断标识符,此符号仅适用于局部变量,增强for循环的索引,以及传统for循环的本地变量;

它不能使用于方法形式参数,构造函数形式参数,方法返回类型,字段,catch形式参数或任何其他类型的变量声明。

并行Full GC 的G1

JDK10 通过并行Full GC,改善G1的延迟。G1垃圾收集器在JDK 9中是默认的。以前的默认值并行收集器中有一个并行的Full GC。为了尽量减少对使用GC用户的影响,G1Full GC也应该并行。

G1垃圾收集器的设计目的是避免Full收集,但是当集合不能足够快地回收内存时,就会出现完全GC。目前对G1Full GC的实现使用了单线程标记-清除-压缩算法。JDK10 使用并行化标记-清除-压缩算法,并使用YoungMixed收集器相同的线程数量。线程的数量可以由-XX:ParallelGCThreads选项来控制,但是这也会影响用YoungMixed收集器的线程数量。

JDK11

JDK11新特性

从Java8到Java17(四) - 掘金 (juejin.cn)

Lambda表达式中支持var变量

示例:

 Arrays.asList("Java", "Python", "Ruby")
            .forEach((var s) -> {
                System.out.println("Hello, " + s);
            });

全新的HTTP client API

这个API首次出现在JDK9之中,当时并非一个稳定版本,在JDK11中正式发布。原来JDKHTTP客户端真的难用,这也给了很多像okhttprestTemplatefeign这样的第三方库极大的发挥空间,几乎是没人用原生的HTTP客户端的。但现在不一样了,确实感觉是新时代的API了。

示例:

// 创建client
HttpClient client = HttpClient.newBuilder()
        .version(Version.HTTP_1_1)
        .followRedirects(Redirect.NORMAL)
        .connectTimeout(Duration.ofSeconds(20))
        .proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80)))
        .authenticator(Authenticator.getDefault())
        .build
// 构建request 
HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://foo.com/"))
        .timeout(Duration.ofMinutes(2))
        .header("Content-Type", "application/json")
        .POST(BodyPublishers.ofFile(Paths.get("file.json")))
        .build();
// 同步请求
   HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
   System.out.println(response.statusCode());
   System.out.println(response.body()); 

// 异步请求
   client.sendAsync(request, BodyHandlers.ofString())
        .thenApply(HttpResponse::body)
        .thenAccept(System.out::println);

新的String方法

  • repeat:重复生成字符串
  • isBlank:不用在引入第三方库就可以实现字符串判空了
  • strip:去除字符串两边的空格,支持全角和半角,之前的trim只支持半角
  • lines:能根据一段字符串中的终止符提取出行为单位的流

读写文件

Files类增加了writeStringreadString两个静态方法,可以直接把String写入文件,或者把整个文件读出为一个String,这两个方法可以大大简化读取配置文件之类的问题。

Files.writeString(
    Path.of("./", "tmp.txt"), // 路径
    "hello, jdk11 files api", // 内容
    StandardCharsets.UTF_8); // 编码
String s = Files.readString(
    Paths.get("./tmp.txt"), // 路径
    StandardCharsets.UTF_8); // 编码

ZGC(试验性的功能)

JDK12

JDK12的五大重要新特性 - 知乎 (zhihu.com)

从Java8到Java17(五) - 掘金 (juejin.cn)

String新增方法

  • indent:给字符串做缩进,接受一个int型的输入
  • transform:接受一个转换函数,实现字符串的转换

示例:

var result = "foo"
  .transform(input -> input + " bar")
  .transform(String::toUpperCase);
  
//output: FOO BAR

全新的switch表达式

JDK8的写法:

switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        System.out.println(6);
        break;
    case TUESDAY:
        System.out.println(7);
        break;
    case THURSDAY:
    case SATURDAY:
        System.out.println(8);
        break;
    case WEDNESDAY:
        System.out.println(9);
        break;
}

// 假如忘记在每个case中写break,那就都会走到default中

新的写法;

switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
    case TUESDAY                -> System.out.println(7);
    case THURSDAY, SATURDAY     -> System.out.println(8);
    case WEDNESDAY              -> System.out.println(9);
}

// 还可以有返回值
int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY                -> 7;
    case THURSDAY, SATURDAY     -> 8;
    case WEDNESDAY              -> 9;
};

引入了->,这里不是lambda,是会执行后面的语句的意思。

NumberFormat增加了对以紧凑格式格式化数字的支持

NumberFormat增加了以紧凑格式格式化数字的支持。 紧凑的数字格式是指数字的简短形式或易于理解的形式。 例如,在en_US语言环境中,根据NumberFormat.Style指定的样式,可以将1000格式化为1K,将1000000格式化为1M

示例:

NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.US,NumberFormat.Style.SHORT);
String result = fmt.format(1000); // 输出结果为 1k

JDK13

JDK13的六大重要新特性 - 知乎 (zhihu.com)

从Java8到Java17(六) - 掘金 (juejin.cn)

字符串块(文本块)

旧的:

String html = "<html>\n" +
              "    <body>\n" +
              "        <p>Hello, world</p>\n" +
              "    </body>\n" +
              "</html>\n";

文本块:

String html = """
              <html>
                  <body>
                      <p>Hello, world</p>
                  </body>
              </html>
              """;

JDK14

JDK14新特性详解

从Java8到Java17(六) - 掘金 (juejin.cn)

JDK14新特性介绍 - 简书 (jianshu.com)

instanceof增加模式匹配

旧的:

if (obj instanceof String) {
    String s = (String) obj;   
    ...
}

新的:

if (obj instanceof String) {
    String s = obj;   
    ...
}

注意点:

  1. 需要确定的类型判断才能使用
  2. 作用域的范围需要注意

Record

官方提供的类似lombok插件的功能,Record类会生成get/set/hashcode/equals和构造方法,没有多余的注解和代码。

示例:

public record EmployeeRecord(Long id, 
		String firstName, 
		String lastName, 
		String email, 
		int age) {
	
}

这就是一个Record类,甚至可以不用写class关键字。不仅可以是一个单独的类文件,也可以是一个成员类

public class EmployeeRecord { 
    public record User(long id, 
                       String name, 
                       int age) {} 
 }

还可以出现在方法里:

public class EmployeeRecord { 
        public void test() { 
            record Mail (long id, String content){} 
            Mail mail = new Mail(10, "content"); 
           } 
        }

注意点:

  1. 构造函数 --- 默认record类具有全部属性的构造函数,不具备无参构造
  2. 属性 --- 属性最好在类声明时就写在括号中,如果想在body中单独声明,仅支持static属性,不建议这样
  3. 不能被继承

JEP 363:删除并发标记扫描(CMS)垃圾回收器

JDK15

JDK 15带来的14个新特性介绍 - 知乎 (zhihu.com)

从Java8到Java17(八) - 掘金 (juejin.cn)

密封类

package com.example.geometry;
// 只允许指定的子类继承
public abstract sealed class Shape 
    permits com.example.polar.Circle,
            com.example.quad.Rectangle,
            com.example.quad.simple.Square { ... }

主要作用:

  • 让类的作者可以精确的控制子类,保持不被滥用
  • 扩展了原有的权限修饰符,限制父类的使用不再一刀切的开放或者私有
  • 为未来的模式匹配打好基础,指定有限子类之后使模式匹配的分析更简单

其实第三点才是重点,就是为了switch模式匹配开发的。

注意:

子类声明必须为 final

JDK17

JDK16的新特性 - 腾讯云开发者社区-腾讯云 (tencent.com)

一文总结JDK 17发布的新特性-阿里云开发者社区 (aliyun.com)

posted @ 2022-10-20 19:20  ws_hawk  阅读(444)  评论(0编辑  收藏  举报