目录
一、JDK新特性
1.1Java Record
1.1.1Record的使用
1.1.2Instance Methods
1.1.3静态方法 Static Method
1.1.4Record构造方法
1.1.5Record与Lombok
1.1.6Record实现接口
1.1.7Local Record
1.1.8嵌套Record
1.1.9instanceof判断Record类型
1.1.10总结
1.2Switch
1.2.1箭头表达式
1.2.2yield返回值
1.2.3Java Record
1.3Text Block
1.3.1认识文本块
1.3.2字符串比较与方法
1.3.3空白
1.3.4文本块的方法
1.3.5转义字符
1.3.6总结
1.4var
1.4.1var声明局部变量
1.4.2什么时候使用var
1.5sealed
1.5.1Sealed Classes
1.5.2Sealed Interface
一、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对象
@Testpublic 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.调用实例方法
@Testpublic 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.测试静态方法
@Testpublic 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.测试
@Testpublic 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 {@Overridepublic void print() {StringJoiner joiner = new StringJoiner("-");String s = joiner.add(id.toString()).add(name).add(qty.toString()).toString();System.out.println("商品信息:" + s); }}
3.测试print方法
@Testpublic void test5() {ProductRecord productRecord = new ProductRecord(1001, "电脑", 1000);productRecord.print();}
1.1.7Local Record
Record 可以作为局部对象使用,在代码块中定义并使用Record。
定义Local Record
@Testpublic void test6() {//定义local recordrecord 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对象并测试
@Testpublic 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 {//定义业务方法,判断年龄是否18public 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.测试
@Testpublic 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箭头表达式
@Testpublic 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是表达式的返回值。
@Testpublic 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结合使用
@Testpublic 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
@Testpublic 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= """lisi20"""; //Error 文本块的内容不能在没有中间行结束符的情况下跟随三个开头双引号String myname= """zhangsan20"""; //正确String s1 = """张三""";//正确
1.3.2字符串比较与方法
三个双引号字符””” 与两个双引号””的字符串处理是一样的,与普通字符串一样使用。例如 equals(),”==”,连接字符串(“+”),作为方法的参数等。
@Testpublic 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""";//字符串方法 substringString sub = msg.substring(0, 5);System.out.println("sub = " + sub); }
1.3.3空白
示例:
@Testpublic void test02() {//按tab键向右移动,保留左侧空格String msg = """张三""";System.out.println(msg);String colors = """blueredgreen""";//indent(int space) 包含缩进,space为空格的数量colors = colors.indent(10);System.out.println(colors);}
输出
1.3.4文本块的方法
Text Block的格式方法 formatted()
@Testpublic void test03() {String info= """Name:%s\nPhone:%sAge:%d""".formatted("张三","123",23);System.out.println(info.translateEscapes().strip());}
String strip():删除每行开头和结尾的空白
String translateEscapes():转义序列转换为字符串字面量
1.3.5转义字符
@Testpublic 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 result = dbconn.executeQuery(query)) {//...//推荐try (var customers = dbconn.executeQuery(query)) {//...}比较 Stream 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 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 非密封类,扩展使用,不受限
//第一种 finalpublic final class Circle extends Shape {}//第二种 sealed classpublic sealed class Square extends Shape permits RoundSquare {@Overridepublic 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 {@Overridepublic void doSome() {System.out.println("SomeServiceImpl");}}