提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
一、JDK关注的新特性
1.1搭建学习环境
1.2有用的新特性
1.2.1 Java Record
1.2.1.1 看看Record怎么用
1.2.1.3静态方法 Static Method
1.2.1.4 Record的构造方法
1.2.1.5Record与Lombok
1.2.1.7Local Record
1.2.1.8嵌套Record
1.2.1.9instanceof 判断Record类型
1.2.2 Switch
1.2.2.1 箭头表达式,新的case标签
1.2.2.2 yeild返回值
1.2.2.3 Java Record
1.2.3 Text Block
1.2.3.1 认识文本块
1.2.3.2 文本块与普通的双引号字符串一样
1.2.3.3 空白
1.2.3.5 转义字符
1.2.4 var
1.2.4.1 var 声明局部变量
1.2.4.2 使用时候使用var
1.2.5 sealed
1.2.5.2 Sealed Interface
视频地址:
动力节点SpringBoot教程-spring boot3通俗易懂高薪必备【SpringBoot3从入门到项目实战】
以实际项目需求入手,结合新特性@HttpExchange声明式客户端、RFC7807、ProblemDetail、预编译AOT等,详解了SpringBoot核心知识点,突出Web应用痛点解决方案。采用知识点配合项目案例的方式,让各位小伙伴轻松掌握SpringBoot。
JDK:JDK19
Maven:构建和依赖管理,版本选择3.6以上
IDEA2022.3.1Ultimate:主要的开发工具,我是30天试用版本
数据库:MySQL 5以上版本
火狐浏览器:版本用比较新的,中文版本。
文本工具:EditPlus, Sublime任意。
JDK8-19新增了不少新特性,这里我们把实际常用的新特性,给大家介绍一下。
包括以下几个方面:
Java14中预览的新特性叫做Record,在Java中,Record是一种特殊类型的Java类。可用来创建不可变类,语法简短。参考[JEP 395](https://openjdk.java.net/jeps/395). Jackson 2.12支持Record类。
任何时候创建Java类,都会创建大量的样板代码,我们可能做如下:
Java Record避免上述的样板代码,如下特点:
IDEA创建新的Maven工程 Lession01-feature
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","[email protected]",20);
System.out.println("lisi = " + lisi.toString());
Student zhangsan = new Student(1002, "zhangsan","[email protected]",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());
}
现在能查看控制台输出: lisi = Student[id=1001, name=lisi, [email protected], age=20] zhangsan = Student[id=1002, name=zhangsan, [email protected], age=20] lisi.equals(zhangsan) = false lisi.name() = lisi zhangsan.name() = zhangsan Record通过构造方法创建了只读的对象,能够读取每个属性,不能设置新的属性值。 Record用于创建不可变的对象,同时减少了样板代码。 Record对每个属性提供了public访问器,例如lisi.name()
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","[email protected]",20);
String nameAndAge = lisi.concat();
System.out.println( nameAndAge);
}
最后控制台输出: 姓名:lisi,年龄是:20
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("[email protected]"); System.out.println("emailUpperCase = " + emailUpperCase);
}
我们可以在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);
}
}
Java Record是创建不可变类且减少样板代码的好方法。Lombok是一种减少样板代码的工具。两者有表面上的重叠部分。可能有人会说Java Record会代替Lombok. 两者是有不同用途的工具。
Lombok提供语法的便利性,通常预装一些代码模板,根据您加入到类中的注解自动执行代码模板。这样的库纯粹是为了方便实现POJO类。通过预编译代码。将代码的模板加入到class中。
Java Record是语言级别的,一种语义特性,为了建模而用,数据聚合。简单说就是提供了通用的数据类,充当“数据载体",用于在类和应用程序之间进行数据传输。
1.2.1.6Record实现接口
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();
}
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);
//使用SaleRecord
System.out.println("销售记录 = " + saleRecord.toString());
}
控制台输出:
销售记录 = SaleRecord[saleId=S22020301, productName=手机, money=3000.0]
多个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) {}
step2: 创建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]]
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,不执行 。
总结
boolean isRecord() : 判断一个类是否是Record类型
RecordComponent[] getRecordComponents():Record的数组,表示此记录类的所有记录组件
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);
Switch的三个方面,参考:JEP 361
Switch新的语法,case label -> 表达式|throw 语句|block
case label_1, label_2, ..., label_n -> expression;|throw-statement;|block
step1:新的case 标签
week:表示周日(1)到周六(7),1和7是休息日,其他是工作日。如果1-7以外为无需日期
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);
}
yeild让switch作为表达式,能够返回值
语法
变量 = switch(value) { case v1: yield 结果值; case v2: yield 结果值;case v3,v4,v5.. yield 结果值 }
示例: yield返回值,跳出switch块
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 结合使用
public static void main(String[] args) {
int week = 1;
//yield是switch的返回值, yield跳出当前switch块
String memo = switch (week){
case 1 ->{
System.out.println("week=1的 表达式部分");
yield "星期日,休息";
}
case 2,3,4,5,6 ->{
System.out.println("week=2,3,4,5,6的 表达式部分");
yield "工作日";
}
case 7 -> {
System.out.println("week=7的 表达式部分");
yield "星期六,休息";
}
default -> {
System.out.println("其他语句");
yield "无效日期";
}
};
System.out.println("week = " + memo);
}
提示:
case 标签-> 与 case 标签:不能混用。 一个switch语句块中使用一种语法格式。
switch作为表达式,赋值给变量,需要yield或者case 标签-> 表达式。->右侧表达式为case返回值。
示例:
public static void main(String[] args) {
int week = 1;
//yield是switch的返回值, yield跳出当前switch块
String memo = switch (week){
case 1 ->{
System.out.println("week=1的 表达式部分");
yield "星期日,休息";
}
case 2,3,4,5,6 ->{
System.out.println("week=2,3,4,5,6的 表达式部分");
yield "工作日";
}
case 7 -> "星期六,休息";
default -> "无效日期";
};
System.out.println("week = " + memo);
}
switch表达式中使用record,结合 case 标签-> 表达式,yield实现复杂的计算
step1: 准备三个Record
public record Line(int x,int y) {
}
public record Rectangle(int width,int height) {
}
public record Shape(int width,int height) {
}
step2: switch record
public static void main(String[] args) {
Line line = new Line(10,100);
Rectangle rectangle = new Rectangle(100,200);
Shape shape = new Shape(200,200);
Object obj = rectangle;
int result = switch (obj){
case Line(int x,int y) -> {
System.out.println("图形是线, X:"+x+",Y:"+y);
yield x+y;
}
case Rectangle(int w,int h) -> w * h;
case Shape(int w,int h) ->{
System.out.println("这是图形,要计算周长");
yield 2* (w + h);
}
default -> throw new IllegalStateException("无效的对象:" + obj);
};
System.out.println("result = " + result);
}
case Line , Rectangle,Shape 在代码块执行多条语句,或者箭头->表达式。
Text Block处理多行文本十分方便,省时省力。无需连接 "+",单引号,换行符等。Java 15 ,参考JEP 378.
语法:使用三个双引号字符括起来的字符串.
"""
内容
"""
例如:
String name = """lisi"""; //Error 不能将文本块放在单行上
String name= """lisi
20"""; //Error 文本块的内容不能在没有中间行结束符的情况下跟随三个开头双引号
String myname= """
zhangsan
20
"""; //正确
文本块定义要求:
三个双引号字符""" 与两个双引号""的字符串处理是一样的。与普通字符串一样使用。例如equals() , "==" , 连接字符串(”+“), 作为方法的参数等。
Text Block使用方式与普通字符串一样,==,equals比较,调用String类的方法。
step1:字符串比较与方法
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);
}
step2:输出结果
b1 = true
b2 = true
sub = hello
示例:
public void fun2(){
//按tab向右移动,保留左侧空格
String html= """
动力节点,Java黄埔军校
""";
System.out.println( html);
}
示例2:indent()方法
public void fun3(){
String colors= """
red
green
blue
""";
System.out.println( colors);
//indent(int space)包含缩进,space空格的数量
String indent = colors.indent(5);
System.out.println( indent);
}
输出:
red
green
blue
red
green
blue
1.2.3.4 文本块的方法
Text Block的格式方法formatted()
public void fun4(){
String info= """
Name:%s
Phone:%s
Age:%d
""".formatted("张三","13800000000",20);
System.out.println("info = " + info);
}
String stripIndent():删除每行开头和结尾的空白
String translateEscapes() :转义序列转换为字符串字面量
新的转义字符"\",表示隐士换行符,这个转义字符被Text Block转义为空格。通常用于是拆分非常长的字符串文本 ,串联多个较小子字符串,包装为多行生成字符串。
新的转义字符,组合非常长的字符串。
示例
public void fun5(){
String str= """
Spring Boot是一个快速开发框架 \
基于\"Spring\"框架,创建Spring应用 \
内嵌Web服务器,以jar或war方式运行 \
""";
System.out.println("str = " + str);
}
输出
Spring Boot是一个快速开发框架 基于Spring框架,创建Spring应用 内嵌Web服务器,以jar或war方式运行
总结:
例如:
String colors= """
red
green
blue
""";
在JDK 10及更高版本中,您可以使用var标识符声明具有非空初始化式的局部变量,这可以帮助您编写简洁的代码,消除冗余信息使代码更具可读性,谨慎使用.
var特点
var优缺点
示例:
//通常
try (Stream result = dbconn.executeQuery(query)) {
//...
//推荐
try (var customers = dbconn.executeQuery(query)) {
//...
}
比较 Stream result 与 var customers
示例:
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);
}
}
sealed 翻译为密封,密封类(Sealed Classes)的首次提出是在 Java15 的 JEP 360 中,并在 Java 16 的 JEP 397 再次预览,而在 Java 17 的 JEP 409 成为正式的功能。
Sealed Classes主要特点是限制继承
Sealed Classes主要特点是限制继承,Java中通过继承增强,扩展了类的能力,复用某些功能。当这种能力不受控。与原有类的设计相违背,导致不预见的异常逻辑。
Sealed Classes限制无限的扩张。
Java中已有sealed 的设计
sealed 作为关键字可在class和interface上使用,结合permits 关键字。定义限制继承的密封类
1.2.5.1Sealed Classes
sealed class 类名 permits 子类1,子类N列表 {}
step1::声明sealed Class
public sealed class Shape permits Circle, Square, Rectangle {
private Integer width;
private Integer height;
public void draw(){
System.out.println("=======Shape图形======");
}
}
permits表示允许的子类,一个或多个。
step2::声明子类
子类声明有三种
示例:
//第一种 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{
}
密封类不支持匿名类与函数式接口
密封接口同密封类
step1:声明密封接口
public sealed interface SomeService permits SomeServiceImpl {
void doThing();
}
step2:实现接口
public final class SomeServiceImpl implements SomeService {
@Override
public void doThing() {
}
}
以上类和接口要在同一包可访问范围内。