资料&笔记
- 尚硅谷Java全套:https://pan.baidu.com/s/1GfkpJqPXGX9MszHDfyTM9g?pwd=yyds 提取码:yyds
- 本文章汇总整理于:https://www.yuque.com/u27599042/un32ge
- 视频链接:https://www.bilibili.com/video/BV1tC4y147US?p=1
相关概念
- “孵化器模块”:将尚未定稿的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 类已经默认继承了 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());
}