1. JDK 关注的新特性
1.1 搭建学习环境
JDK:JDK19
OpenJDK: https://jdk.java.net/19/
Liberica JDK: Download Java | Java 8, Java 11, Java 17, Java 20 - OpenJDK Builds for Linux, Windows & macOS , 是一个 OpenJDK 发行版,为云原生,容器特别优化。
Maven:构建和依赖管理,版本选择 3.6 以上
配置本地仓库和阿里云镜像
IDEA2022.3. 1Ultimate :主要的开发工具,我是 30 天试用版本
数据库:MySQL 5 以上版本
火狐浏览器:版本用比较新的,中文版本。
文本工具:EditPlus, Sublime 任意。
1.2 有用的新特性
JDK8- 19 新增了不少新特性,这里我们把实际常用的新特性,给大家介绍一下。包括以下几个方面:
Java Record
Swich 开关表达式
Text Block 文本块
var 声明局部变量
sealed 密封类
1.2.1 Java Record
Java14 中预览的新特性叫做 Record ,在 Java 中,Record 是一种特殊类型的 Java 类。可用来创建不可变类,语法 简短。参考[JEP 395](https://openjdk.java.net/jeps/395). Jackson 2. 12 支持 Record 类。
任何时候创建 Java类,都会创建大量的样板代码,我们可能做如下:
- 每个字段的 set ,get 方法
- 公共的构造方法
- 重写 hashCode, toString(), equals()方法
Java Record 避免上述的样板代码,如下特点:
- 带有全部参数的构造方法
- public 访问器
- toString(),hashCode(),equals()
- 无 set ,get 方法。没有遵循 Bean 的命名规范
- final 类,不能继承 Record ,Record 为隐士的 final 类。除此之外与普通类一样
- 不可变类,通过构造创建 Record
- final 属性,不可修改
- 不能声明实例属性,能声明 static 成员
IDEA 创建新的 Maven 工程 Lession01-feature
1.2.1.1 看看 Record 怎么用
IDEA 新建 Class ,选择类 Record
step1: 创建 Student Record
public record Student(Integer id,String name,String email,Integer age) { }
step2:创建 Record 对象
public static void main(String[] args) {
Student lisi = new Student(1001, "lisi","lisi@qq.com",20) ; System.out.println("lisi = " + lisi.toString()) ;
Student zhangsan = new Student(1002, "zhangsan","lisi@qq.com",20) ; System.out.println("zhangsan = " + zhangsan.toString()) ;
System.out.println("lisi.equals(zhangsan) = " + lisi.equals(zhangsan)) ; System.out.println("lisi.name () = " + lisi.name()) ;
System.out.println("zhangsan.name () = " + zhangsan.name()) ;
}
现在能查看控制台输出:
public static void main(String[] args) { Student lisi = new Student(1001, "lisi","lisi@qq.com",20) ; System.out.println("lisi = " + lisi.toString()) ; Student zhangsan = new Student(1002, "zhangsan","lisi@qq.com",20) ; System.out.println("zhangsan = " + zhangsan.toString()) ; System.out.println("lisi.equals(zhangsan) = " + lisi.equals(zhangsan)) ; System.out.println("lisi.name () = " + lisi.name()) ; System.out.println("zhangsan.name () = " + zhangsan.name()) ; }
Record 通过构造方法创建了只读的对象,能够读取每个属性,不能设置新的属性值。 Record 用于创建不可变的 对象,同时减少了样板代码。
Record 对每个属性提供了 public 访问器,例如 lisi.name()
1.2.1.2 Instance Methods
Record 是 Java 类,和普通 Java 类一样定义方法。下面定义方法 concat ,将姓名和年龄一起打印输出。 我们创建普通的方法 concat ,将 name 和 age 连接为一个字符串输出。
step1:创建实例方法
public record Student(Integer id,String name,String email,Integer age) { public String concat() {
return String.format("姓名:%s,年龄是:%d", this.name,this.age) ; }
}
step2: 调用实例方法
public static void main(String[] args) {
Student lisi = new Student(1001, "lisi","lisi@qq.com",20) ;
String nameAndAge = lisi.concat() ;
System.out.println( nameAndAge) ;
}
最后控制台输出:
姓名:lisi ,年龄是:20
1.2.1.3 静态方法 Static Method
Record 类定义静态方法,试用静态方法与普通类一样。
step1: 创建静态方法
public record Student(Integer id,String name,String email,Integer age) { public String concat() {
return String.format("姓名:%s,年龄是:%d", this.name,this.age) ; }
/** 静态方法 */
public static String emailUpperCase(String email) {
return Optional.ofNullable(email).orElse("no email").toUpperCase() ; }
}
step2:测试静态方法
public static void main(String[] args) {
String emailUpperCase = Student.emailUpperCase("lisi@163.com") ;
System.out.println("emailUpperCase = " + emailUpperCase) ;
}
1.2.1.4 Record 的构造方法
我们可以在 Record 中添加构造方法, 有三种类型的构造方法分别:是紧凑的,规范的和定制构造方法 紧凑型构造方法没有任何参数,甚至没有括号。
规范构造方法是以所有成员作为参数
定制构造方法是自定义参数个数
step1: 紧凑和定制构造方法
public record Student(Integer id,String name,String email,Integer age) { /*紧凑构造方法*/
public Student {
System.out.println("id"+ id ) ;
if( id < 1 ) {
throw new RuntimeException("ok") ;
}
}
/*自定义构造方法*/
public Student(Integer id, String name) {
this(id, name, null, null) ;
}
}
step2:编译 Student.java -> Student.class
public record Student(Integer id, String name, String email, Integer age) { /** 紧凑构造方法和规范构造方法合并了 */
public Student(Integer id, String name, String email, Integer age) { System.out.println("id" + id) ;
if (id < 1) {
throw new RuntimeException("ok") ; }
else {
this.id = id ;
this.name = name ;
this.email = email ;
this.age = age ;
}
}
public Student(Integer id, String name) {
this(id, name, (String)null, (Integer)null) ;
}
}
1.2.1.5 Record 与 Lombok
Java Record 是创建不可变类且减少样板代码的好方法。Lombok 是一种减少样板代码的工具。两者有表面上的重叠部 分。可能有人会说Java Record 会代替 Lombok. 两者是有不同用途的工具。
Lombok 提供语法的便利性,通常预装一些代码模板,根据您加入到类中的注解自动执行代码模板。这样的库纯粹是 为了方便实现 POJO 类。通过预编译代码。将代码的模板加入到class 中。
Java Record 是语言级别的,一种语义特性,为了建模而用,数据聚合。简单说就是提供了通用的数据类,充当“数据 载体" ,用于在类和应用程序之间进行数据传输。
1.2.1.6 Record 实现接口
Java Record 可以与普通类一样实现接口,重写接口的方法。
step1: 创建新的接口,定义一个规范方法。
public interface PrintInterface {
/** 输出自定义描述信息 */
void print() ;
}
step2: 创建新的 Record 实现接口,重写接口的方法,实现当前 Record 有关的业务逻辑
public record ProductRecord(String id,String name,Integer qty)
implements PrintInterface {
@Override
public void print() {
String productDesc = String.join("-", id, name, qty.toString()) ; System.out.println("商品信息 = " + productDesc) ;
}
}
ProductRecord 实现 print()方法,打印商品详情。
step3:测试 print 方法
public static void main(String[] args) {
ProductRecord product = new ProductRecord("P001", "手机", 100) ; product.print() ;
}
1.2.1.7 Local Record
Record 可以作为局部对象使用。在代码块中定义并使用 Record ,下面定义一个 SaleRecord
step1:定义 Local Record
public static void main(String[] args) {
//定义 Java Record
record SaleRecord(String saleId,String productName,Double money) {} ; //创建 Local Record
SaleRecord saleRecord = new SaleRecord("S22020301", "手机", 3000.0) ; //使用 SaleRecor
System.out.println("销售记录 = " + saleRecord.toString()) ;
}
控制台输出:
销售记录 = SaleRecord[saleId=S22020301, productName=手机, money=3000.0]
1.2.1.8 嵌套 Record
多个 Record 可以组合定义, 一个 Record 能够包含其他的 Record。
我们定义 Record 为 Customer ,存储客户信息,包含了 Address 和 PhoneNumber 两个 Record
step1:定义 Record
public record Address(String city,String address,String zipcode) {} public record PhoneNumber(String areaCode,String number) {}
public record Customer(String id, String name, PhoneNumber phoneNumber,
Address address) {}
tep2: 创建 Customer 对象
public static void main(String[] args) {
Address address = new Address("北京", "大兴区凉水河二街-8 号 10栋三层", "100176"); PhoneNumber phoneNumber = new PhoneNumber("010", "400-8080-105") ;
Customer customer = new Customer("C1001", "李项", phoneNumber, address) ; System.out.println("客户 = " + customer.toString()) ;
}
控制台输出:
客户 = Customer[id=C1001, name=李项, phoneNumber=PhoneNumber[areaCode=010, number=400-8080- 105], address=Address[city=北京, address=大兴区凉水河二街 9 号 10 栋三层, zipcode= 100176]]
1.2.1.9 instanceof 判断 Record 类型
instanceof 能够与 Java Record 一起使用。编译器知道记录组件的确切数量和类型。
step1:声明 Person Record ,拥有两个属性 name 和 age
public record Person(String name,Integer age) {
}
step2: 在一个业务方法判断当是 Record 类型时,继续判断 age 年龄是否满足 18 岁。
public class SomeService {
public boolean isEligible(Object obj) {
// 判断 obj 为 Person 记录类型
if( obj instanceof Person(String name, Integer age)) {
return age >= 18 ;
}
return false ;
}
}
instanceof 还可以下面的方式
if( obj instanceof Person(String name, Integer age) person) {
return person.age() >= 18 ;
}
或者
if( obj instanceof Person p) {
return p.age() >= 18 ;
}
step3: 测试代码
public static void main(String[] args) {
SomeService service = new SomeService() ;
boolean flag = service.isEligible(new Person("李四", 20)) ;
System.out.println("年龄符合吗?" + flag);
}
控制台输出:
控制台输出 flag 为true
处理判断中 Record 为 null
Java Record 能够自动处理 null。
step1 :record 为 null
public static void main(String[] args) {
SomeService service = new SomeService() ;
boolean eligible = service.isEligible(null) ;
System.out.println("年龄符合吗?" + eligible) ;
}
控制台输出 eligible 为 false ,Debug 调试代码,发现 if 语句判断为 false ,不执行
总结
- abstract 类 java.lang.Record 是所有 Record 的父类。
- 有对于 equals() ,hashCode() ,toString()方法的定义说明
- Record 类能够实现 java.io.Serializable 序列化或反序列化
- Record 支持泛型,例如 record Gif<T>( T t ) { }
- java.lang.Class 类与 Record 类有关的两个方法:
1 2 |
|
Customer customer = new Customer(....) ;
RecordComponent[] recordComponents = customer.getClass().getRecordComponents() ; for (RecordComponent recordComponent : recordComponents) {
System.out.println("recordComponent = " + recordComponent) ;
}
boolean record = customer.getClass().isRecord() ;
System.out.println("record = " + record) ;
1.2.2 Switch
Switch 的三个方面,参考:JEP 361
支持箭头表达式
支持 yied 返回值
支持 Java Record
1.2.2.1 箭头表达式,新的 case 标签
Switch 新的语法,case label -> 表达式|throw 语句|block
case label_ 1, label_2, ..., label_n -> expression;|throw-statement;|block
step1:新的 case 标签
public static void main(String[] args) {
int week = 7 ;
String memo = "" ;
switch (week) {
case 1 -> memo = "星期日,休息";
case 2,3,4,5,6-> memo="工作日" ;
case 7 -> memo="星期六,休息";
default -> throw new IllegalArgumentException("无效的日期:") ; }
System.out.println("week = " + memo) ;
}
week:表示周日 (1) 到周六 (7) ,1 和 7 是休息日,其他是工作日。如果 1-7 以外为无需日期
1.2.2.2 yeild 返回值
yeild 让 switch 作为表达式,能够返回值
语法
变量 = switch(value) { case v1: yield 结果值; case v2: yield 结果值;case v3,v4,v5.. yield 结果值 }
无需中间变量, switch作为表达式计算,可以得到结果。yield 是表达式的返回值
示例:多表达式, case 与 yield 结合使用
public static void main(String[] args) {
int week = 2 ;
//yield 是 switch 的返回值, yield 跳出当前 switch 块
String memo = switch (week) {
case 1: yield "星期日,休息";
case 2,3,4,5,6: yield "工作日" ;
case 7: yield "星期六,休息";
default: yield "无效日期";
} ;
System.out.println("week = " + memo) ;
无需中间变量, switch作为表达式计算,可以得到结果。yield 是表达式的返回值
示例:多表达式, case 与 yield 结合使用
未完待续……