侧边栏壁纸
博主头像
梦幻世界博主等级

行动起来,活在当下

  • 累计撰写 23 篇文章
  • 累计创建 2 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

JDK17新特征-Java17对比Java8

梦幻世界
2024-05-31 / 0 评论 / 0 点赞 / 117 阅读 / 25561 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2024-05-31,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

Java17新特性

  • 文本块

  • switch表达式

  • record关键字

  • sealed classes密封类

  • instanceof模式匹配

  • Helpful NullPointerExceptions

  • 日期周期格式化

  • 精简数字格式化支持

  • Stream.toList()简化

文本块

在Java17之前的版本里,如果我们需要定义一个字符串,比如一个JSON数据我们需要 双引号需要进行转义、为了字符串的可读性需要通过+号连接、如果需要将JSON复制到代码中需要做大量的格式调整。

通过Java 17中的文本块语法,类似的字符串处理则会方便很多;通过三个双引号可以定义一个文本块,并且结束的三个双引号不能和开始的在同一行。

private static void textTest() {
    // java8
    String lowVersion  = "{\n" +
            "  \"name\": \"yxy\",\n" +
            "  \"age\": 18\n" +
            "}";
    System.out.println(lowVersion);
    // java17
    String highVersion = """
            {
                "name":"yxy",
                "age":18
            }
            """;
    System.out.println(highVersion);
}

switch表达式

Java 17版本中switch表达式将允许switch有返回值,并且可以直接作为结果赋值给一个变量,等等一系列的变化。

  1. 省略break也可以防止穿透:可以通过switch表达式来进行简化,将冒号(:)替换为箭头(->),并且switch表达式默认不会失败,所以不需要break。

  2. switch表达式也可以返回一个值,也可以直接省略赋值动作直接打印

  3. 用yield返回结果

  4. 如果你想在case里想做不止一件事,比如在返回之前先进行一些计算或者打印操作,可以通过大括号来作为case块,最后的返回值使用关键字yield进行返回。

private static void switchTest() {
    FruitEnum fruitEnum = FruitEnum.PEAR;
    // 防止穿透
    switch (fruitEnum) {
        case APPLE, PEAR -> System.out.println("apple and pear");
        case MANGO -> System.out.println("mango");
        default -> System.out.println("error");
    }
    // 返回一个值
    String text = switch (fruitEnum) {
        case APPLE, PEAR -> "apple and pear";
        case MANGO -> "mango";
        default -> "error";
    };
    System.out.println(text);
    // yield返回结果
    System.out.println(switch (fruitEnum) {
        case APPLE, PEAR :
            yield "apple and pear";
        case MANGO:
            yield "mango";
        default:
            yield "error";
    });
    // 代码块
    String text2 = switch (fruitEnum) {
        case APPLE, PEAR -> {
            System.out.println("one");
            yield "apple and pear";
        }
        case MANGO -> {
            System.out.println("two");
            yield "mango";
        }
        default -> "error";
    };
    System.out.println(text2);
}

record关键字

record用于创建不可变的数据类。在这之前如果你需要创建一个存放数据的类,通常需要先创建一个Class,然后生成构造方法、getter、setter、hashCode、equals和toString等这些方法,或者使用Lombok来简化这些操作。

我们来通过Person类做一些测试,比如创建两个对象,对他们进行比较,打印这些操作。

假设有一些场景我们只需要对Person的name和age属性进行打印,在有record之后将会变得非常容易。

简单应用

private static void personRecordTest() {
    // java8
    Person person1 = new Person("yxy", 18, "北京");
    Person person2 = new Person("yxy", 18, "北京");
    System.out.println(person1);
    System.out.println(person2);
    System.out.println(person1.equals(person2));
    // java11 使用record定义
    record PersonRecord(String name, int age){}
    PersonRecord personRecord = new PersonRecord(person1.getName(), person1.getAge());
    PersonRecord personRecord1 = new PersonRecord(person2.getName(), person2.getAge());
    System.out.println(personRecord);
    System.out.println(personRecord1);
    System.out.println(personRecord1.equals(personRecord));
}

record同样也有构造方法,可以在构造方法中对数据进行一些验证操作。

private static void personRecordTest() {
    // java8
    Person person1 = new Person("yxy", 18, "北京");
    Person person2 = new Person("yxy", 18, "北京");
    System.out.println(person1);
    System.out.println(person2);
    System.out.println(person1.equals(person2));
    // java11 使用record定义
    record PersonRecord(String name, int age){
        // 构造方法校验属性value
        PersonRecord {
            if (age < 20) {
                throw new RuntimeException("error");
            }
        }
    }
    PersonRecord personRecord = new PersonRecord(person1.getName(), person1.getAge());
    PersonRecord personRecord1 = new PersonRecord(person2.getName(), person2.getAge());
    System.out.println(personRecord);
    System.out.println(personRecord1);
    System.out.println(personRecord1.equals(personRecord));
}

record类

public record PersonRecord(String name, int age) {

}

private static void personRecordTest2() {
    PersonRecord personRecord = new PersonRecord("yxy", 20);
    // .name()和getName()的效果是一样的
    System.out.println(personRecord.name());
    System.out.println(personRecord.age());
    System.out.println(personRecord);
}
  1. getName()变成了name,但是功能一样的,只是命名方式变了

  2. 因为是不可变数据类型,没有set方法

  3. 自动toString,虽然最后结果不一样,但是可以正常看,不是地址值

  4. 自动实现equals,如果不覆盖这个方法,两个对象比较时候会比较指向的对象是不是同一个对象,这个只是比了里面的值是否相等。

record中可以覆盖构造方法、创建静态方法、定义自己的方法

public record PersonRecord(String name, int age) {
    // 重写构造方式
    public PersonRecord(String name, int age) {
        if (age > 18) {
            throw new RuntimeException("error");
        }
        this.name = name;
        this.age = 1;
    }

    //额外定义的方法
    public String nameToUppercase() {
        return this.name.toUpperCase();
    }

    //静态方法
    public static String nameAddPassword(PersonRecord user1) {
        return user1.name + user1.age;
    }
}


private static void personRecordTest2() {
    PersonRecord personRecord = new PersonRecord("yxy", 10);
    // .name()和getName()的效果是一样的
    System.out.println(personRecord.name());
    System.out.println(personRecord.age());
    System.out.println(personRecord);

    System.out.println(personRecord.nameToUppercase());
    System.out.println(PersonRecord.nameAddPassword(personRecord));
}

密封类 sealed class

简介

  • 密封类(class)和接口(interface)限制哪些其他类或接口可以扩展(extends)或实现(implements)它们。

  • 密封类JDK 15 中作为预览功能提供,并在JDK 16 中作为预览功能进行了改进。

  • 现在在 JDK 17 中,密封类被最终确定,与 JDK 16 没有任何变化。

  • 允许类(class)或接口(interface)的作者控制负责实现它的代码。

  • 提供比访问修饰符更具声明性的方式来限制超类的使用。

  • 支持模式匹配(例如switch模式匹配中的案例)

语法

sealed class 父类 permits 子类1,子类2 ....

表示父类是密封类,指定可以被继承的有子类1,子类2 ...

  1. permits指定的子类必须在父类的附近

    1. 在同一个模块中(module jdk9新增),(父类在一个命名的模块中)

    2. 或在同一包中(package),(父类在一个未命名的模块)

  2. 子类在大小和数量上都很小时,可以同父类定义在一个java文件中

  3. permits指定的子类必须直接继承该父类

  4. permits指定的子类在定义时必须使用以下三种修饰符中的一种

    1. final:表示该子类是最终的,不能被继承

    2. sealed:表示该子类是密封类,可以被指定的其他类继承

    3. non-sealed:表示该子类是非密封类,可以被任意其他类继承

抽象类Animal 由sealed修饰,只能由permits指定的子类或接口来继承或实现。

package cn.test.sealed.domain;

public abstract sealed class Animal permits Cat, Dog, Pig {

    public abstract void eat();
}

抽象类Animal 由sealed修饰,说明它的子类会受到限制,只能是permits子句中的子类的其中一个。final表示这个子类不能再被继承了。

package cn.test.sealed.domain;


public final class Dog extends Animal {

    @Override
    public void eat() {
        System.out.println("dog eat");
    }
}

类Cat由sealed修饰,只能由permits指定的子类或接口来继承或实现。

package cn.test.sealed.domain;


public sealed class Cat extends Animal permits BlackCat {


    @Override
    public void eat() {
        System.out.println("cat eat");
    }
}
package cn.test.sealed.domain;


public final class BlackCat extends Cat {

    @Override
    public void eat() {
        System.out.println("black cat eat");
    }
}

抽象类Animal 由sealed修饰,说明它的子类会受到限制,只能是permits子句中的子类的其中一个。non-sealed表示这个子类没有密封限制,随便继承

package cn.test.sealed.domain;


public non-sealed class Pig extends Animal {

    @Override
    public void eat() {
        System.out.println("pig eat");
    }
}
package cn.test.sealed.domain;


public class BigPig extends Pig{
}

调用

private static void sealedClassTest() {
    Animal cat = new Cat();
    cat.eat();
    Animal blackCat = new BlackCat();
    blackCat.eat();
    Animal pig = new Pig();
    pig.eat();
    Animal bigPig = new BigPig();
    bigPig.eat();
    Animal dog= new Dog();
    dog.eat();
}

接口Animal 由sealed修饰,只能由permits指定的子类或接口来继承或实现。

package cn.test.sealed.interfaces;


public sealed interface Animal permits Cat, Dog, Pig  {

    void eat();
}

接口Animal由sealed修饰,说明它的子类会受到限制,只能是permits子句中的子类的其中一个。final表示这个子类不能再被继承了。

package cn.test.sealed.interfaces;

public final class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("impl dog eat");
    }
}

接口Animal由sealed修饰,说明它的子类会受到限制,只能是permits子句中的子类的其中一个。non-sealed表示这个子类没有密封限制,随便继承

package cn.test.sealed.interfaces;

public non-sealed class Cat implements Animal {
    @Override
    public void eat() {
        System.out.println("impl cat eat");
    }
}

接口Pig由sealed修饰,只能由permits指定的子类或接口来继承或实现。

package cn.test.sealed.interfaces;

public sealed class Pig implements Animal permits BlackPig, White {
    @Override
    public void eat() {
        System.out.println("impl pig eat");
    }
}

接口Pig由sealed修饰,说明它的子类会受到限制,只能是permits子句中的子类的其中一个。non-sealed表示这个子类没有密封限制,随便继承

package cn.test.sealed.interfaces;

public non-sealed class White extends Pig{
}

接口Pig 由sealed修饰,说明它的子类会受到限制,只能是permits子句中的子类的其中一个。final表示这个子类不能再被继承了。

package cn.test.sealed.interfaces;

public final class BlackPig extends Pig {

    @Override
    public void eat() {
        System.out.println("impl black pig eat");
    }
}

调用

private static void sealedInterfaceTest() {
    cn.itbox.sealed.interfaces.Animal dog = new cn.itbox.sealed.interfaces.Dog();
    dog.eat();
    cn.itbox.sealed.interfaces.Animal cat = new cn.itbox.sealed.interfaces.Cat();
    cat.eat();
    cn.itbox.sealed.interfaces.Animal pig = new cn.itbox.sealed.interfaces.Pig();
    pig.eat();
    cn.itbox.sealed.interfaces.Animal blackPig = new cn.itbox.sealed.interfaces.BlackPig();
    blackPig.eat();
    cn.itbox.sealed.interfaces.Animal whitePig = new cn.itbox.sealed.interfaces.White();
    whitePig.eat();
}

JDK中的密封类

ConstantDesc是JDK12 中出现的一组API。定义了一些JVM中已知的符号引用,常量池中的常量描述符

JDK17 使用密封类对已知的继承体系做了优化

public sealed interface ConstantDesc
        permits ClassDesc,
                MethodHandleDesc,
                MethodTypeDesc,
                Double,
                DynamicConstantDesc,
                Float,
                Integer,
                Long,
                String {
                ....
                }

swich对sealed的支持

Todo

instanceof模式匹配

通常我们使用instanceof时,一般发生在需要对一个变量的类型进行判断,如果符合指定的类型,则强制类型转换为一个新变量。

Object o = new RedApple();
if (o instanceof RedApple) {
    RedApple redApple = (RedApple) o;
    System.out.println(redApple.getName());
}

在使用instanceof的模式匹配后,上面的代码可进行简写。

if (o instanceof RedApple redApple) {
    System.out.println(redApple.getName());
}

可以将类型转换和变量声明都在if中处理。同时,可以直接在if中使用这个变量。

if (o instanceof RedApple redApple && redApple.getName().equals("apple")) {
    System.out.println(redApple.getName());
}

因为只有当instanceof的结果为true时,才会定义变量furit,所以这里可以使用&&,但是改为||就会编译报错。

Helpful NullPointerExceptions

Helpful NullPointerExceptions可以在我们遇到NullPointerExceptions时节省一些分析时间。

如下的代码会导致一个NPE

String str = null;
int length = str.length();

在Java 11中,输出将显示NullPointerException发生的行号,但不知道哪个方法调用时产生的null,必须通过调试的方式找到。

Exception in thread "main" java.lang.NullPointerException
        at com.heiz.java17.HelpfulNullPointerExceptionsDemo.main(HelpfulNullPointerExceptionsDemo.java:13)

在Java 17中,则会准确显示发生NullPointerException的位置

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "str" is null
        at cn.itbox.controller.TestController.main(TestController.java:39)

日期周期格式化

在Java 17中添加了一个新的模式B,用于格式化DateTime,它根据Unicode标准指示一天时间段。

private static void dataTimeFormatBTest() {
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("B");
    System.out.println(dtf.format(LocalTime.of(8, 0)));
    System.out.println(dtf.format(LocalTime.of(13, 0)));
    System.out.println(dtf.format(LocalTime.of(20, 0)));
    System.out.println(dtf.format(LocalTime.of(23, 0)));
    System.out.println(dtf.format(LocalTime.of(0, 0)));
}
上午
下午
晚上
晚上
午夜

精简数字格式化支持

在NumberFormat中添加了一个工厂方法,可以根据Unicode标准以紧凑的、人类可读的形式格式化数字。

SHORT格式如下所示:

// NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.CHINESE, NumberFormat.Style.SHORT);
NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.SHORT);
System.out.println(fmt.format(1000));
System.out.println(fmt.format(100000));
System.out.println(fmt.format(1000000));

输出:

1K
100K
1M

LONG格式如下所示:

fmt = NumberFormat.getCompactNumberInstance(Locale.CHINESE, NumberFormat.Style.LONG);
fmt = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.LONG);
System.out.println(fmt.format(1000));
System.out.println(fmt.format(100000));
System.out.println(fmt.format(1000000));

输出:

1 thousand
100 thousand
1 million

Stream.toList()

如果需要将Stream转换成List,需要通过调用collect方法使用Collectors.toList(),代码非常冗长。

在Java 17中将会变得简单,可以直接调用toList()。

private static void streamListTest() {
    // java8
    Stream<Integer> integerStream = Stream.of(1, 2, 3);
    List<Integer> collect = integerStream.collect(Collectors.toList());
    for (Integer i : collect) {
        System.out.println(i);
    }

    // java11
    Stream<Integer> integerStream2 = Stream.of(1, 2, 3);
    List<Integer> list = integerStream2.toList();
    for (Integer i : list) {
        System.out.println(i);
    }
}

代码参考

https://gitee.com/yixiuyu/java17.git

Java17与Java8对比

基本信息

Java 17与Java 8是Java版本中的两个重要里程碑。Java 8是Java版本中的一次重大更新,于2014年发布,引入了很多新的特性和功能,包括Lambda表达式、Stream API、函数式接口等。Java 17是Java SE 17版本,于2021年9月发布,是Java SE 16的长期支持(LTS)版本。Java 17中也有一些新的特性和改进,我们将在后文中详细讨论。

特征

Java 17

Java 8

引入

2021年9月14日

2014年3月

垃圾收集器

ZGC(新型垃圾收集器)

G1收集器

其他垃圾收集器

Shenandoah GC,G1 GC,Parallel GC,Serial GC

Parallel GC,Serial GC

垃圾回收策略

全堆回收和增量模式

复制模式

应用程序类数据共享(AppCDS)

支持

不支持

JFR事件流

使用异步处理提高性能

未支持

条件性实例化卡片

支持

支持

嵌入式C / C ++库

JDK不包括C / C ++编译器

JDK不包括C / C ++编译器

算法升级

SHA-3,SM3 / SM4,Ed448,RSASSA-PSS,X25519 / X448

SHA-1,RC4,DES,MD5,DSA,DH

性能比较

Java 17与Java 8在性能方面的比较非常重要。Java 8引入了一些性能改进,例如优化了字符串连接和数组排序等操作。Java 17在性能方面也有一些新的改进,例如:

  • 改进了JIT编译器,提高了应用程序的性能。

  • 改进了垃圾回收器,提高了垃圾回收的效率和吞吐量。

  • 引入了C++风格的内存管理,包括对堆内存分配的优化和对垃圾回收的改进。

  • 这些改进都可以提高Java应用程序的性能和响应速度。

语言特性比较

Java 8引入了一些新的语言特性,例如Lambda表达式和函数式接口。这些特性让Java程序员能够使用函数式编程的方式编写代码,从而使得代码更加简洁、易读、易维护。Java 17在语言特性方面也有一些新的改进,例如:

  • 引入了Sealed类,这是一种新的类修饰符,用于限制类的继承。这样可以使得代码更加安全、可维护。

  • 引入了Pattern Matching for Switch语法,这是一种新的switch语法,可以用于模式匹配。这样可以使得代码更加简洁、易读、易维护。

  • 引入了Record类,这是一种新的数据类,可以用于定义只有属性和访问器的简单数据对象。这样可以使得代码更加简洁、易读、易维护。

这些改进都可以使得Java程序员能够使用更加先进、更加高效的语言特性编写代码。

应用场景比较

Java 8和Java 17都可以用于不同的应用场景,但是它们在一些方面有所不同。Java 8适用于开发中小型应用程序和Web应用程序,例如Web服务、企业级应用程序和桌面应用程序等。Java 8也可以用于开发大型应用程序,但是在大型应用程序中可能会出现一些性能问题。Java 17则更适合用于开发大型应用程序和高性能应用程序,例如高性能计算、云计算、大数据处理等。

参考

Java 17 新特性概览(重要)

Java 17的这些新特性,Java迈入新时代

10 w+字总结!Java 8---Java 17 特性详解

Java 17 VS Java 8: 新旧对决,这些Java 17新特性你不容错过_java17-CSDN博客

Java17新特性及代码示例:还在使用Java8? 这5个Java17新功能,你会喜欢的

jdk17新特性—— 密封类(Sealed Classes)_jdk17密封类-CSDN博客

JDK 17 sealed classes密封类 - 掘金

jdk17新特性—— 密封类(Sealed Classes)_jdk17密封类-CSDN博客

新一代垃圾回收器ZGC的探索与实践

12 张图带你彻底理解 ZGC

Java垃圾回收CMS、G1、ZGC - 别动我的猫 - 博客园

0

评论区