一、switch表达式
-
1.传统switch声明语句的弊端:
-
匹配是自上而下的,如果忘记写break,后面的case语句不论匹配与否都会执行; ---> case 穿透
-
所有的case语句共有一个块范围,在不同的case语句定义的变量名不能重复;
-
不能在一个case里写多个执行结果一致的条件;
-
整个switch不能作为表达式返回值;
二、JDK12中预览特性
-
java 12 将会对switch 声明语句进行扩展,使用case L -> 来代替以前的break;,省去了break语句,笔迷拿了因少写break而出错。
-
同时将多个case合并到一行,显得简洁、清晰,也更加优雅的表达逻辑分支。
-
为了保持兼容性,case条件语句中依然可以使用字符:,但是同一个 switch 结构里不能混用 -> 和 : ,否则编译错误。
-
JDK12中的写法:switch表达式,省去了break语句,避免了因少些break而出现case穿透
/**
* 示例一
* JDK12中的写法:switch表达式,省去了break语句,避免了因少些break而出现case穿透
*/
@Test
void test2() {
Week day = Week.FRIDAY;
switch (day){
case MONDAY -> System.out.println(1);
case TUESDAY,WEDENSDAY,THURSDAY -> System.out.println(2);
case FRIDAY -> System.out.println(3);
case SATURDAY,SUNDAY -> System.out.println(4);
default ->
throw new RuntimeException("??????????" + day);
}
}
-
JDK12中的写法: 还可以使用变量接收switch表达式的结果
/**
* 示例一
* JDK12中的写法: 还可以使用变量接收switch表达式的结果
*/
@Test
void test2() {
Week day = Week.FRIDAY;
int result = switch (day){
case MONDAY -> 1;
case TUESDAY,WEDENSDAY,THURSDAY -> 2;
case FRIDAY -> 3;
case SATURDAY,SUNDAY -> 4;
default ->
throw new RuntimeException("??????????" + day);
};
System.out.println(result);
}
-
JDK13中的写法: 引入了yield关键字,用于返回指定的数据,结束switch结构
-
这意味着,switch表达式(返回值)应该使用yield,switch语句(不返回值)应该使用break。
-
和return的区别在于:return会直接跳出当前方法,而yield只会跳出当前switch块
/**
* 示例一
* JDK13中的写法: 引入了yield关键字,用于返回指定的数据,结束switch结构
* 这意味着,switch表达式(返回值)应该使用yield,switch语句(不返回值)应该使用break。
* 和return的区别在于:return会直接跳出当前方法,而yield只会跳出当前switch块
*/
@Test
void test3() {
Week day = Week.FRIDAY;
int result = switch (day){
case MONDAY -> { yield 1;}
case TUESDAY,WEDENSDAY,THURSDAY -> { yield 2;}
case FRIDAY -> { yield 3;}
case SATURDAY,SUNDAY -> { yield 4;}
default ->
{
System.out.println("?????????????");
yield 5;
}
};
System.out.println(result);
}
@Test
void test3() {
Week day = Week.FRIDAY;
int result = switch (day){
case MONDAY : yield 1;
case TUESDAY,WEDENSDAY,THURSDAY : yield 2;
case FRIDAY : yield 3;
case SATURDAY,SUNDAY :yield 4;
default :
System.out.println("?????????????");
yield 5;
};
System.out.println(result);
}
3.JDK17的预览特性:switch的模式匹配
/*
* JDK17中的switch的模式匹配
*/
@Test
static String formatterSwitchPattern(Object o) {
String formatted = switch (o) {
case Integer i:
yield "int " + i;
case Long l:
yield "long " + l;
case Double d:
yield "Double " + d;
default:
yield o.toString();
};
return formatted;
}
4.文本块的使用
/*
* JDK14新特性
* \:取消换行操作
* \s:表示一个空格
*/
@Test
void test7() {
String newQuery1 = """
SELECT id,name,email \
From cusomers\s\
WHERE id > 4 \
ORDER BY email DESC
""";
}
5.Record(引用数据类型)的使用
-
record是一种全新的类型,它本质上是一个final类,同时所有的属性都是final修饰,它会自动编译出public get、hashcode、equals、toString、构造器等结构,减少了代码的编写量。
-
具体来说:当你用record声明一个类时,该类将自动拥有以下功能:
-
获取成员变量的简单方法,比如例题中的name()和partner()。注意区别于我们平常getter()的写法。
-
一个equals方法的实现,执行比较时会比较该类的所有成员属性
-
重写hashCode()方法
-
一个可以打印该类所有成员属性的toString()方法
-
只有一个构造方法
public record Order1(int orderId,String orderName) {
}
public class Order {
private final int orderId;
private final String orderName;
public Order(int orderId, String orderName) {
this.orderId = orderId;
this.orderName = orderName;
}
public int getOrderId() {
return orderId;
}
public String getOrderName() {
return orderName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Order order = (Order) o;
return orderId == order.orderId && Objects.equals(orderName, order.orderName);
}
@Override
public int hashCode() {
return Objects.hash(orderId, orderName);
}
@Override
public String toString() {
return "Order{" +
"orderId=" + orderId +
", orderName='" + orderName + '\'' +
'}';
}
}
/*
* 测试-order
*/
@Test
void test8() {
Order order1 = new Order(1001, "orderAA");
//测试toString()
System.out.println(order1);
//测试getter()
System.out.println(order1.getOrderId());
System.out.println(order1.getOrderName());
Order order2 = new Order(1001, "我是你爸爸");
//测试equals()
System.out.println(order1.equals(order2));
//测试hashCode()和equals()
HashSet<Order> set = new HashSet<>();
set.add(order1);
set.add(order2);
System.out.println(set);
}
/*
* 测试-order1
*/
@Test
void test9() {
Order1 order1 = new Order1(1001, "orderAA");
//测试toString()
System.out.println(order1);
//测试getter()
System.out.println(order1.orderId());
System.out.println(order1.orderName());
Order1 order2 = new Order1(1001, "我是你爸爸");
//测试equals()
System.out.println(order1.equals(order2));
//测试hashCode()和equals()
HashSet<Order1> set = new HashSet<>();
set.add(order1);
set.add(order2);
System.out.println(set);
}
此外:
-
还可以在record声明的类中定义静态字段、静态方法、构造器或实例方法。
-
不能在record中声明的类中定义实例字段;类不能声明为abstract;不能声明显示的父类等。
注意:record的设计目标是提供一种将数据建模为数据的好方法。它也不是 JavaBeans 的直接替代品,应为 record 的方法不符合 JavaBeans 的get 标准。另外 JavaBeans 通常是可变的,而记录是不可变的。尽管他们的用途有点像,但记录并不会以某种方式取代 JavaBean。
public record Person(int id,String name) {
//- 还可以在 record 声明的类中定义静态字段、静态方法、构造器或实例方法。
static String info = "我是你爸爸";
public static void show(){
System.out.println("我真的你是爸爸!!!");
}
public Person(){
this(0,null);
}
public void eat(){
System.out.println("其实我不是你爸爸,我是你爷爷~~~没想到吧");
}
//- 不能再record声明的类中定义实例字段;类不能声明为abstract;不能声明显示的父类等。
//final int age;
//abstract record Dog(int id){}
//record Cat(int id) extends Thread{}
//class Student extends Person{}
}
6.密封类
在Java中如果想让一个类不能被继承和修改,则会使我们应该使用final关键字对类进行修饰。不过这种要么可以继承,要么不能继承的机制不够灵活,有些时候我们可能想让某个类可以被某些类型继承,但是又不能随意继承,是做不到的。Java15尝试解决这个问题,引入了 sealed 类,被sealed 修饰的类可以指定子类。这样这个类就只能被指定的类继承。
通过秘方的类和接口来限制超类的使用,密封的类和接口限制其它可能继承或实现他们的其它类或接口
具体使用:
-
使用修饰符 sealed,可以将一个类声明为密封类。密封的类使用保留关键字 permits累出可以直接扩展(即extends)它的类。
-
sealed 修饰的类的机制具有传递性,他的子类必须使用指定的关键字进行修饰,且只能是final、sealed、non-sealed三者之一。
7.API的变化
Optional类
Optional<T>类(java.util.Optional)是一个容器类,他可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。如果值存在,则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional提供很多有用的方法,这样我们就不用显示进行空值检测。
JDK8的新特性,JDK9-11中新增了一些方法。
-
1.为什么需要Optional类?
为了避免代码中出现空指针异常。
static<T>Optional<T> ofNullable(T value): 用来创建一个Optional实例,value可能是空,也可能是非空。
-
3.常用方法
-
创建Optional类对象的方法:
-
static<T> Optional<T> empty():用来创建一个空的Optional实例
-
static<T> Optional<T> of(T value):用来创建一个Optional实例,value必须非空
-
static <T> Optional<T> ofNullable(T value):用来创建一个Optional实例,value可能是空,也可能非空
-
判断Optional容器中包含对象:
-
boolean isPresent():判断Optional容器中的值是否存在
-
void ifPresent(Consumer<? super T> consumer): 判断Optional容器中的值是否存在,如果存在,就对他进行Consumer指定的操作,如果不存在就不做
-
获取Optional容器的对象:
-
T get(): 如果调用对象包含值,返回该值。否则抛异常。T get()与of(T value)配合使用
-
T orElse(T other):orElse(T other)与ofNullable(T value)配合使用,如果Optional容器中非空,就返回所包装值,如果为空,就用orElse(T other)other指定的默认值(备胎)代替
-
T orElseGet(Supplier<? extends T> other):如果Optional容器中非空,就返回所包装值,如果为空就用Supplier接口的Lambda表达式提供的值代替
-
T orElseThrow(Supplier<? extends T> exceptionSupplier):如果Optional容器中非空,就返回所包装值,如果为空,就抛出你指定的异常类型代替原来的NoSuchElementException
@Test
void test01() {
String star = "你爸爸";
//使用Optional避免空指针的问题
//1.实例化:
//ofNullable(T value):用来创建一个Optional实例,value可能是空,也可能非空
Optional<String> optional = Optional.ofNullable(star);
//orElse(T other):如果Optional实力内部的value属性不为null,
// 则返回value,如果value为null,则返回other。
String otherStar = "你爷爷";
String finalStar = optional.orElse(otherStar);
System.out.println(finalStar.toString());
}