JDK14(Java14) 新特性学习笔记(尚硅谷宋红康康师傅2020)

简介: JDK14(Java14) 新特性学习笔记(尚硅谷宋红康康师傅2020)

资料&笔记

相关概念

  • “孵化器模块”:将尚未定稿的API和工具先交给开发者使用,以获得反馈,并用这些反馈进一步改进Java平台的质量。
  • “预览特性”:是规格已经成型、实现已经确定,但还未最终定稿的功能。它们出现在Java中的目的是收集在真实世界中使用后的反馈信息,促进这些功能的最终定稿。这些特性可能会随时改变,根据反馈结果,这些特性甚至可能会被移除,但通常所有预览特性最后都会在Java中固定下来。

instanceof 模式匹配

  • instanceof 在新特性中,将类型判断和强制类型转换合二为一,减少了Java程序中显式强制转换的数量

原先模式匹配的使用

  • 需要先进行类型判断,类型判断为 true 后再手动显示进行强制类型转换
@Test
public void test01() {
    Object obj = new String("hello world");
    if (obj instanceof String) { // 判断是否为String类型
        String string = (String) obj; // 进行强制类型转换
        System.out.println(string.toUpperCase());
    } else {
        System.out.println("这不是一个字符串类型...");
    }
}

null 与任何类型进行 instanceof 判断,结果都为 false

新特性中模式匹配的使用

  • 新特性中,将类型判断和强制类型转换合二为一,我们不用再手动显示进行强制类型转换
@Test
public void test02() {
    Object obj = new String("hello world");
    // 判断是否为String类型,如果是,就将obj强制转换为String类型
    // 强制转换后使用string变量接收强制转换后的值
    // 将类型判断和强制类型转换合二为一,我们不用再手动显示进行强制类型转换
    if (obj instanceof String string) {
        System.out.println(string.toUpperCase());
    } else {
        System.out.println("这不是一个字符串类型...");
    }
}
  • 注意:用于接收强制类型转换后的值的变量,只能在 if 的代码块中进行使用,该变量为 if 代码块的局部变量

instanceof 使用小技巧

class Monitor {
    private String model;
    private double price;
    // public boolean equals(Object o) {
    //     if (o instanceof Monitor monitor) {
    //         if (monitor.model.equals(model) && monitor.price == price) return true;
    //     }
    //     return false;
    // }
    // 简写
    public boolean equals(Object o) {
        // 先判断是否为Monitor类型,是就进行后面的判断,否则直接返回false
        // 在后面的判断中可以继续使用 instanceof 判断类型为真的情况下强制类型转换的变量
        return o instanceof Monitor monitor && monitor.model.equals(model) && monitor.price == price;
    }
}

NullPointerException

  • 该特性改进了NullPointerException的可读性,能更准确地给出null变量的信息
  • 该特性可以帮助开发者提高生产力,以及改进各种开发工具和调试工具的质量。
  • 在JDK14中,新特性可以更好地提示哪个地方出现的空指针,需要通过-XX:+ShowCodeDetailsInExceptionMessages开启
  • 这个增强特性不仅适用于方法调用,只要会导致 NullPointerException 的地方也都适用,包括字段的访问、数组的访问和赋值。
  • 在后面的版本中,这个特性默认启用
@Test
public void test03() {
    String str = null;
    str.toLowerCase();
}
// JDK 11 报错描述
java.lang.NullPointerException
at JDK14Test.test03(JDK14Test.java:45)
// JDK 17 报错描述
java.lang.NullPointerException: 
Cannot invoke "String.toLowerCase()" because "str" is null
at JDK14Test.test03(JDK14Test.java:45)

Record

  • 没有Record,开发人员想要创建纯数据载体类(plain data carriers)通常都必须编写大量低价值、重复的、容易出错的代码。
  • 如:构造函数、getter/setter、equals()、hashCode()以及toString()等。
  • 为了避免这种重复代码,Java 14推出 Record
  • 使用Record来减少类声明语法,效果类似 lombok 的 @Data 注解,Kotlin中的data class。它们的共同点是类的部分或全部状态可以直接在类头中描述,并且这个类中只包含了纯数据而已。

原先的数据载体类(JavaBean)

public class User {
    private String name;
    private Integer age;
    public User() {
    }
    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        if (!name.equals(user.name)) return false;
        return age.equals(user.age);
    }
    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age.hashCode();
        return result;
    }
    @Override
    public String toString() {
        return "User{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}

使用 Record 定义数据载体类

IDEA 开启 Record


Record 定义数据载体类

// 相当于上述的User类的代码
// public record Person(String name, Integer age) {} 
// 相当于有了构造器 public Person(String name, Integer age) {}
public record Person(String name, Integer age) {
}

Record 类的使用

@Test
public void test04() {
    Person person = new Person("张三", 23);
    System.out.println(person);
}

Record 类编译后的字节码

// 编译后的字节码文件 JDK17
public record Person(String name, Integer age) {
    // 构造器
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    // 相当于 getName()
    public String name() {
        return this.name;
    }
    // 相当于 getAge()
    public Integer age() {
        return this.age;
    }
}

Record 类是否重写方法验证

@Test
public void test04() {
    Person person1 = new Person("张三", 23);
    Person person2 = new Person("张三", 23);
    // 重写了 toString 方法
    System.out.println(person1);
    System.out.println(person2);
    // 验证是否重写 equals 方法
    // true -> 重写了 equals 方法
    System.out.println(person1.equals(person2));
    // 验证是否重写 hashCode 方法
    // 重写了 hashCode 方法
    System.out.println(person1.hashCode()); // 24021582
    System.out.println(person2.hashCode()); // 24021582
    HashSet<Person> set = new HashSet<>();
    set.add(person1);
    set.add(person2);
    System.out.println(set.size()); // 1
    // name() age()
    // 相当于 getName() getAge()
    System.out.println(person1.name()); // 张三
    System.out.println(person2.age()); // 23
}

Record 注意点

  • 在 Record 类中无空参构造器,除非定义 Record 时参数列表为空
  • 当你用record 声明一个类时,该类将自动拥有以下功能:
  • 获取成员变量的简单方法,以上面代码为例 name() 和 partner() 。注意区别于我们平常getter的写法。
  • 一个 equals 方法的实现,执行比较时会比较该类的所有成员属性
  • 重写 equals 当然要重写 hashCode
  • 一个可以打印该类所有成员属性的 toString 方法。
  • 请注意只会有一个构造方法。
  • Record 类为 final

在 Record 类中可以添加的内容

  • 可以在Record声明的类中定义静态字段、静态方法、构造器或实例方法。
public record Person(String name, Integer age) {
    // 可以在Record声明的类中定义静态字段、静态方法、构造器或实例方法。
    public static String info = "Person 类";
    public static void showInfo() {
        System.out.println(info);
    }
    // 对于不是规范的记录类型的构造器中必须调用规范的记录类型构造器
    public Person() {
        this("李四", 44);
    }
    public String nameToUpperCase() {
        return name.toUpperCase();
    }
}

在 Record 类中不可以添加的内容

  • 不能在 Record 声明的类中定义实例字段,不能声明非静态属性
  • 类不能声明为abstract;
  • Record 类为 final,不能被继承
  • 不能声明显式的父类
  • Record 类已经默认继承了 Record

反射中引入的关于 Record 的方法

  • 在Java.lang.Class对象中添加如下两个新方法:
  • RecordComponent[] getRecordComponents()
  • boolean isRecord()

switch 表达式

  • 在JDK12和JDK13中为预览特性,在JDK14中成为正式特性
  • 该特性规定,switch可以当作语句使用,也可以当作表达式使用。
  • JDK12 中使用->来替代以前的: + break;,并且可以使用一个变量来接收switch表达式的值
  • JDK13 提供了 yield 来在 block 中返回值

JDK12 新特性之前 switch 的用法

@Test
public void test01() {
    String str = "d";
    switch (str) {
        case "a":
        case "b":
        case "c":
            System.out.println(123);
            break;
        case "d":
        case "e":
            System.out.println(45);
            break;
        case "f":
            System.out.println(6);
            break;
        default:
            System.out.println(0);
    }

JDK12 新特性 switch 的用法

  • 可以使用->来替代以前的: + break;,并且可以使用一个变量来接收switch表达式的值
@Test
public void test02() {
    String str = "e";
    // 使用 -> 来替代以前的 : + break;
    switch (str) {
        // 当 str 为 "a", "b", "c" 其中一个时,输出 123
        case "a", "b", "c" -> System.out.println(123);
        case "d", "e" -> System.out.println(45);
        case "f" -> System.out.println(6);
        default -> System.out.println(0);
    }
    // 可以使用一个变量来接收switch表达式的值
    int num = switch (str) {
        // 当 str 为 "a", "b", "c" 其中一个时,返回 123
        case "a", "b", "c" -> 123;
        case "d", "e" -> 45;
        case "f" -> 6;
        default -> 0;
    };
    System.out.println(num); // 45
}

JDK13 新特性 switch 的用法

  • 可以使用 yield 来在 block 中返回值
  • 在 -> 后面可以接一个代码块,可以使用 yield 来在 block 中返回值

用法一

@Test
public void test03() {
    String str = "a";
    // 可以使用一个变量来接收switch表达式的值
    int num = switch (str) {
        // 当 str 为 "a", "b", "c" 其中一个时,返回 123
        case "a", "b", "c" -> {
            System.out.println("abc");
            yield 123;
        }
        case "d", "e" -> {
            System.out.println("de");
            yield 45;
        }
        case "f" -> {
            System.out.println("f");
            yield 6;
        }
        default -> {
            System.out.println("default");
            yield 0;
        }
    };
    System.out.println(num); // 123
}

用法二

@Test
public void test04() {
    String str = "a";
    // 使用 : + yield 的写法,全部的分支都要使用该写法
    int num = switch (str) {
        case "a", "b", "c":
            yield 123;
        case "d", "e":
            yield 45;
        case "f":
            yield 6;
        default:
            yield 0;
    };
    System.out.println(num); // 123
}

文本块

  • 文本块在JDK13中第一次引入,在JDK14中进行第二次预览
  • 在JDK14中主要是引入了两个转义字符
  • \:表示取消换行
  • \s:表示一个空格操作
  • 引入的原因:在Java中,通常需要使用String类型表达HTML,XML,SQL或JSON等格式的字符串,在进行字符串赋值时需要进行转义和连接操作,然后才能编译该代码,这种表达方式难以阅读并且难以维护。
  • 引入文本块,可以使得我们可以在多行编写字符串,增加代码可读性

JDK13 之前多行编写字符串

@Test
public void test1() {
    // 代码可读性差
    String s = "<!DOCTYPE html>\n" +
            "<html lang=\"en\">\n" +
            "<head>\n" +
            "    <meta charset=\"UTF-8\">\n" +
            "    <title>Title</title>\n" +
            "</head>\n" +
            "<body>\n" +
            "\n" +
            "</body>\n" +
            "</html>";
    System.out.println(s);
}

JDK13 使用文本块

@Test
public void test2() {
    // 使用文本块编写字符串,输出该字符串时,
    // 会保留输出每行最后的换行符
    // 代码可读性提高
    String s = """
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>Title</title>
            </head>
            <body>
            </body>
            </html>
            """;
    System.out.println(s);
}

JDK14 使用文本块

  • 在JDK14中引入了一个新的转义字符 \ ,该转义字符可以用于取消文本块中每行文本最后的换行
@Test
public void test2() {
    String s = """
            <!DOCTYPE html>\
            <html lang="en">\
            <head>\
                <meta charset="UTF-8">\
                <title>Title</title>\
            </head>\
            <body>\
            \
            </body>\
            </html>\
            """;
    System.out.println(s);
}

  • 在JDK14中,也引入了另一个转义字符 \s,表示一个空格操作
@Test
public void test2() {
    String s = """
            select id, name, age\
            from t\
            where id = 1;\
            """;
    System.out.println(s);
}

@Test
public void test2() {
    String s = """
            select id, name, age \
            from t\s\
            where id = 1;\
            """;
    System.out.println(s);
}

  • 在文本块中,如果结尾的引号和字符串的最后一个字符不在同一行,最后一行也有一个换行符
@Test
public void test2() {
    String s1 = """
            select id, name, age\
            from t\
            where id = 1;
            """;
    String s2 = """
            select id, name, age\
            from t\
            where id = 1;\
            """;
    String s3 = """
            select id, name, age\
            from t\
            where id = 1;""";
    System.out.println(s1.length());
    System.out.println(s2.length());
    System.out.println(s3.length());
}

相关文章
|
26天前
|
存储 安全 Java
Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
【10月更文挑战第17天】Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
52 2
|
27天前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
38 3
|
27天前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
32 2
|
9天前
|
安全 Java 编译器
JDK 10中的局部变量类型推断:Java编程的简化与革新
JDK 10引入的局部变量类型推断通过`var`关键字简化了代码编写,提高了可读性。编译器根据初始化表达式自动推断变量类型,减少了冗长的类型声明。虽然带来了诸多优点,但也有一些限制,如只能用于局部变量声明,并需立即初始化。这一特性使Java更接近动态类型语言,增强了灵活性和易用性。
91 53
|
9天前
|
分布式计算 Java API
Java 8引入了流处理和函数式编程两大新特性
Java 8引入了流处理和函数式编程两大新特性。流处理提供了一种声明式的数据处理方式,使代码更简洁易读;函数式编程通过Lambda表达式和函数式接口,简化了代码书写,提高了灵活性。此外,Java 8还引入了Optional类、新的日期时间API等,进一步增强了编程能力。这些新特性使开发者能够编写更高效、更清晰的代码。
21 4
|
23天前
|
存储 Java API
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
46 3
|
23天前
|
存储 安全 Java
Java Map新玩法:深入探讨HashMap和TreeMap的高级特性
【10月更文挑战第19天】Java Map新玩法:深入探讨HashMap和TreeMap的高级特性,包括初始容量与加载因子的优化、高效的遍历方法、线程安全性处理以及TreeMap的自然排序、自定义排序、范围查询等功能,助你提升代码性能与灵活性。
24 2
|
27天前
|
Java 开发者
在Java集合世界中,Set以其独特的特性脱颖而出,专门应对重复元素
在Java集合世界中,Set以其独特的特性脱颖而出,专门应对重复元素。通过哈希表和红黑树两种模式,Set能够高效地识别并拒绝重复元素的入侵,确保集合的纯净。无论是HashSet还是TreeSet,都能在不同的场景下发挥出色的表现,成为开发者手中的利器。
26 2
|
Java 测试技术
Java特性组合的通用方案
Java特性组合的通用方案
193 0
|
8天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。