[Java]JDK17新特性
1.1Java Record1.1.1Record的使用1.1.2Instance Methods1.1.3静态方法 Static Method1.1.4Record构造方法1.1.5Record与Lombok1.1.6Record实现接口1.1.7Local Record1.1.8嵌套Record1.1.9instanceof判断Record类型1.1.10总结1.2Switch1.2.1箭头表
目录
一、JDK新特性
-
JDK8-19新增了不少新特性,这里我们把实际常用的新特性进行介绍。
-
Java Record
-
Switch 开关表达式
-
Text Block 文本块
-
var 声明局部变量
-
sealed 密封类
-
1.1Java Record
-
Java14中预览的新特性叫做Record,在Java中,Record是一种特殊类型的Java类。可用来创建不可变类,语法简短。
-
任何时候创建Java类,都会创建大量的样板代码,如:
-
每个字段的set、get方法
-
公共的构造方法
-
重写hashCode()、toString()、equals()方法
-
-
使用Java Record避免上述的样板代码,如下特点:
-
带有全部参数的构造方法
-
public访问器
-
toString()、hashCode()、equals()
-
无set、get方法,没有遵循Bean的命名规范
-
final类,不能继承Record,Record为隐式的final类,除此之外与普通类一样
-
不可变类,通过构造创建Record
-
final属性不可修改
-
不能声明实例属性,能声明static成员
-
1.1.1Record的使用
-
IDEA新建Class,选择类Record
1.创建 Student Record
public record Student(Integer id, String name, String email, Integer age) {
}
2.创建 Record对象
@Test
public void test01() {
//创建Record对象
Student student = new Student(1001, "张三", "zhangsan@qq.com", 20);
System.out.println(student);
//public访问器获取属性值,只读,没有set、get方法
Integer id = student.id();
String name = student.name();
System.out.println("id=" + id);
System.out.println("name=" + name);
Student student1 = new Student(1002, "李四", "lisi@qq.com", 21);
System.out.println(student1);
System.out.println("student1.equals(student)="+student1.equals(student));
System.out.println(student1.name());
System.out.println(student1.id());
}
3.查看控制台输出结果
-
Record 通过构造方法创建了只读对象,能够读取每个属性,不能设置新的属性值。
-
Record 用于创建不可变的对象,同时减少了样板代码。
-
Record 对每个属性提供了public访问器,例如student.name()
1.1.2Instance Methods
-
Record是Java类,和普通Java类一样定义方法。
1.创建实例方法
//实例方法,concat连接字符串
public String concat() {
return String.format("姓名是%s,年龄是%d", this.name, this.age);
}
2.调用实例方法
@Test
public void test2() {
Student student = new Student(1001, "张三", "zhangsan@qq.com", 20);
//使用对象,调用实例方法
System.out.println(student.concat());
}
1.1.3静态方法 Static Method
-
Record类定义静态方法,和普通类一样。
1.创建静态方法
//静态方法,把email转为大写
public static String emailToUpperCase(String email) {
return Optional.ofNullable(email).orElse("no email").toUpperCase();
}
2.测试静态方法
@Test
public void test3() {
//使用类,调用静态方法
String email = Student.emailToUpperCase("zhangsan@qq.com");
System.out.println(email);
}
1.1.4Record构造方法
-
我们可以在Record中添加构造方法,有三种类型的构造方法:
-
紧凑型构造方法:没有任何参数,甚至没有括号
-
规范构造方法:以所有成员作为参数
-
定制构造方法:自定义参数个数
-
1.紧凑和定制构造方法
//紧凑构造方法
public Student {
System.out.println("id=" + id);
if (id < 1000) {
throw new RuntimeException("id<1000");
}
}
//自定义构造方法
public Student(Integer id, String name) {
this(id, name, null, null);
}
2.编译Student.java->Student.class
/*紧凑构造方法和规范构造方法合并了*/
public Student(Integer id, String name, String email, Integer age) {
System.out.println("id=" + id);
if (id < 1000) {
throw new RuntimeException("id<1000");
} 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);
}
3.测试
@Test
public void test4() {
Student student = new Student(1001, "张三");
System.out.println(student);
}
1.1.5Record与Lombok
-
Java Record是创建不可变类且减少样板代码的好方法,Lombok是一种减少样板代码的工具。
-
Lombok提供语法的便利性,通常预装一些代码模板,根据您加入到类中的注解自动执行代码模板。这样的库纯粹是为了方便实现POJO类,通过预编译代码,将代码的模板加到class中。
-
Java Record是语言级别的,一种语义特性。为了建模而用,数据聚合。简单说就是提供了通用的数据类,充当“数据载体”,用于在类和应用程序之间进行数据传输。
1.1.6Record实现接口
-
Java Record可以与普通类一样实现接口,重写接口的方法。
1.创建新的接口,定义一个规范方法
public interface Print {
void print();
}
2.创建新的Record实现接口,重写接口的方法,实现当前Record有关的业务逻辑
public record ProductRecord(Integer id, String name, Integer qty) implements Print {
@Override
public void print() {
StringJoiner joiner = new StringJoiner("-");
String s = joiner.add(id.toString()).add(name).add(qty.toString()).toString();
System.out.println("商品信息:" + s);
}
}
3.测试print方法
@Test
public void test5() {
ProductRecord productRecord = new ProductRecord(1001, "电脑", 1000);
productRecord.print();
}
1.1.7Local Record
-
Record 可以作为局部对象使用,在代码块中定义并使用Record。
定义Local Record
@Test
public void test6() {
//定义local record
record SaleRecord(String saleId, String productName, Double money) {}
//创建对象
SaleRecord saleRecord = new SaleRecord("001", "显示器", 3000.00);
System.out.println("saleRecord=" + saleRecord);
}
1.1.8嵌套Record
-
多个Record可以组合定义,一个Record能够包含其他得到Record。
1.定义Record
public record Address(String city,String address,String zipcode) {}
public record Phone(String areaCode,String number) {}
public record Customer(String id, String name, Address address, Phone phone) {}
2.创建Customer对象并测试
@Test
public void test7() {
Address address = new Address("郑州", "新密", "452370");
Phone phone = new Phone("0371", "60281234");
Customer customer = new Customer("1001", "张三", address, phone);
System.out.println(customer);
System.out.println(customer.address().address());
System.out.println(customer.phone().number());
}
1.1.9instanceof判断Record类型
-
instance能够与Java Record一起使用,编译器知道记录组件的确切数量和类型。
-
instanceof是Java的一个保留关键字,左边是对象,右边是类,返回类型是Boolean类型。
-
instanceof是测试左边的对象是否是右边类或者该类的子类创建的实例对象,是返回true,否返回false。
1.声明Person Record,拥有两个属性name和age
public record Person(String name, Integer age) {}
2.在一个业务方法判断当是Record类型时,继续判断age年龄是否满足18岁
public class SomeService {
//定义业务方法,判断年龄是否18
public boolean isEligible(Object obj) {
/*if (obj instanceof Person(String name,Integer age)) {
return age >= 18;
}*/
if (obj instanceof Person(String name,Integer age) p) {
return p.age() >= 18;
}
return false;
}
}
3.测试
@Test
public void test8() {
Person person = new Person("李四", 20);
SomeService someService = new SomeService();
boolean flag = someService.isEligible(person);
System.out.println(flag);
}
1.1.10总结
-
java.lang.Class类与Record类有关的两个方法
-
判断一个类是否是Record类型:
boolean isRecord()
-
Record的数组,表示此记录类的所有记录组件:
RecordComponent[] getRecordComponents()
-
//判断Customer是否为Java Record类型
boolean record = customer.getClass().isRecord();
System.out.println("record=" + record);
RecordComponent[] recordComponents = customer.getClass().getRecordComponents();
for (RecordComponent recordComponent : recordComponents) {
System.out.println("recordComponent=" + recordComponent);
}
1.2Switch
-
Switch特点:
-
支持箭头表达式
-
支持yield返回值
-
支持Java Record
-
1.2.1箭头表达式
@Test
public void test01() {
int week = 7;
String memo = "";//表示计算结果
switch (week) {
case 1 -> memo = "星期日,休息";
case 2, 3, 4, 5, 6 -> memo = "工作日";
case 7 -> memo = "星期六,休息";
default -> throw new RuntimeException("无效的日期");
}
System.out.println("memo=" + memo);
}
1.2.2yield返回值
-
yield让switch作为表达式,能够返回值。
-
无需中间变量,switch作为表达式计算,可以得到结果,yield是表达式的返回值。
@Test
public void test02() {
int week = 10;
String memo = switch (week) {
case 1:
yield "周日";
case 2, 3, 4, 5, 6:
yield "工作日";
case 7:
yield "周六";
default:
yield "无效日期";
};
System.out.println("week = " + memo);
}
-
多表达式,case与yield结合使用
@Test
public void test03() {
int week = 7;
String memo = switch (week) {
case 1 -> {
System.out.println("周日,执行了代码块");
yield "周日";
}
case 2, 3, 4, 5, 6 -> {
System.out.println("工作日,执行了代码块");
yield "工作日";
}
case 7 -> "周六";
default -> {
System.out.println("其它");
throw new RuntimeException("无效日期");
}
};
System.out.println("week=" + memo);
}
-
提示
-
case -> 不能与 case : 混用,一个switch语句块中使用一种语法格式。
-
1.2.3Java Record
-
switch表达式中使用record,结合case -> 表达式 ,yield实现复杂的计算。
1.准备三个Record
public record Line(int x, int y) {}
public record Circle(int r) {}
public record Rectangle(int width, int height) {}
2.switch record
@Test
public void test04() {
Circle circle = new Circle(10);
Line line = new Line(10, 20);
Rectangle rectangle = new Rectangle(10, 20);
Object obj = circle;
int res = switch (obj) {
case Circle(int r) -> {
System.out.println("圆的半径为:" + r);
yield 3 * r * r;
}
case Line(int x,int y) -> {
System.out.println("线段的长度为:" + (y - x));
yield y - x;
}
case Rectangle(int w,int h) -> w * h;
default -> 0;
};
System.out.println("res=" + res);
}
1.3Text Block
-
Text Bolck处理多行文本十分方便,无需连接“+”单引号、换行符等。
1.3.1认识文本块
-
语法:使用三个双引号括起来的字符串。
"""
内容
"""
-
例如:
String name = """lisi"""; //Error 不能将文本块放在单行上 String name= """lisi 20"""; //Error 文本块的内容不能在没有中间行结束符的情况下跟随三个开头双引号 String myname= """ zhangsan 20 """; //正确 String s1 = """ 张三""";//正确
1.3.2字符串比较与方法
-
三个双引号字符""" 与两个双引号""的字符串处理是一样的,与普通字符串一样使用。例如 equals(),"==",连接字符串("+"),作为方法的参数等。
@Test
public void fun1() {
String s1 = """
lisi
""";
String s2 = """
lisi
""";
//比较字符串
boolean b1 = s1.equals(s2);
System.out.println("b1 = " + b1);
//使用 == 的比较
boolean b2 = s1 == s2;
System.out.println("b2 = " + b2);
String msg = """
hello world""";
//字符串方法 substring
String sub = msg.substring(0, 5);
System.out.println("sub = " + sub);
}
1.3.3空白
示例:
@Test
public void test02() {
//按tab键向右移动,保留左侧空格
String msg = """
<html>
<body>张三</body>
</html>
""";
System.out.println(msg);
String colors = """
blue
red
green
""";
//indent(int space) 包含缩进,space为空格的数量
colors = colors.indent(10);
System.out.println(colors);
}
输出
1.3.4文本块的方法
-
Text Block的格式方法 formatted()
@Test
public void test03() {
String info= """
Name:%s
\nPhone:%s
Age:%d
""".formatted("张三","123",23);
System.out.println(info.translateEscapes().strip());
}
-
String strip():删除每行开头和结尾的空白
-
String translateEscapes():转义序列转换为字符串字面量
1.3.5转义字符
@Test
public void test04() {
String str= """
Spring Boot 是一个快速开发框架 \
基于\"Spring\"框架,创建 Spring 应用 \
内嵌 Web 服务器,以 jar 或 war 方式运行 \
""";
System.out.println(str);
}
1.3.6总结
-
多行字符串,应该使用Text Block
-
当Text Block可以提高代码的清晰的时,推荐使用。例如代码中嵌入SQL语句。
-
避免不必要的缩进,开头和结尾部分。
-
空格和制表符不能混合使用。
-
对于大多数多行字符串, 分隔符位于上一行的右端,并将结束分隔符位于文本块单独行上。
1.4var
-
在JDK10及更高版本中,可以使用var标识符声明具有非空初始化式的局部变量,这可以帮助您编写简洁的代码,消除冗余信息使代码更具有可读性,谨慎使用。
1.4.1var声明局部变量
-
var特点
-
var 是一个保留字,不是关键字(可以声明var为变量名)
-
方法内声明的局部变量,必须有初始值,不能为空
-
每次声明一个变量,不能复合声明多个变量。
var s1="Hello", age=20; //Error
-
var 动态类型是编译器根据变量所赋的值来推断类型
-
var代替显示类型,代码简洁,减少不必要的排版
-
-
var优缺点
-
代码简洁和整齐
-
降低了程序的可读性
-
示例
//通常
try (Stream<Customer> result = dbconn.executeQuery(query)) {
//...
//推荐
try (var customers = dbconn.executeQuery(query)) {
//...
}
比较 Stream<Customer> result 与 var customers
1.4.2什么时候使用var
-
简单的临时变量
-
复杂,多步骤逻辑,嵌套的表达式等,简短的变量有助于理解代码
-
能够确定变量的初始值
-
变量类型比较长时
示例
public void fun1(){
var s1="lisi";
var age = 20;
for(var i=0;i<10;i++){
System.out.println("i = " + i);
}
List<String> strings = Arrays.asList("a", "b", "c");
for (var str: strings){
System.out.println("str = " + str);
}
1.5sealed
-
Sealed Classes 主要特点是限制继承,Java中通过继承增强,扩展了类的能力,复用某些功能。当这种能力不受控,与原有类的设计相违背,导致不预见的异常逻辑。
-
Sealed 限制无限的扩张。
-
Java种已有的sealed的设计
-
final关键字,修饰类不能被继承
-
private限制私有类
-
-
sealed作为关键字可以在class和interface上使用,结合permits关键字。定义限制继承的密封类。
1.5.1Sealed Classes
sealed class 类名 permits 子类1,子类2,... 列表{}
1.声明sealed Class
public sealed class Shape permits Circle,Square,Rectangle{
public void draw(){
System.out.println("画一个Shape");
}
}
-
permits表示允许的子类,一个或多个
2.声明子类
-
子类声明的三种方式
-
final 终结,依然是密封的
-
sealed 子类是密封的,需要子类实现
-
non-sealed 非密封类,扩展使用,不受限
-
//第一种 final
public final class Circle extends Shape {
}
//第二种 sealed class
public sealed class Square extends Shape permits RoundSquare {
@Override
public void draw() {
System.out.println("=======Square 图形======");
}
}
//密封类的子类的子类
public final class RoundSquare extends Square{
}
//非密封类 , 可以被扩展。放弃密封
public non-sealed class Rectangle extends Shape {
}
//继承非密封类
public class Line extends Rectangle{
}
1.5.2Sealed Interface
-
密封接口同密封类
1.声明密封接口
public sealed interface SomeService permits SomeServiceImpl {
void doSome();
}
2.实现接口
public final class SomeServiceImpl implements SomeService {
@Override
public void doSome() {
System.out.println("SomeServiceImpl");
}
}
更多推荐
所有评论(0)