jdk17新特性梳理

jdk17新特性梳理

jdk8升级至jdk17新特性梳理

因为公司后续将采用jdk17进行开发,顾进行一个jdk17新特性的简单梳理

升级jdk17的理由

最简单的理由就是,springboot3.0放弃了jdk8,kafka4.0同样放弃了jdk8

新特性梳理

可以在接口中定义私有方法,主要为了jdk8的default方法

public interface PricateMethodTest{
    default void defaultMethod(){
        privateMethod();
    }

    private void privateMethod(){

    }
}

局部变量可以使用var来定义

// jdk8的写法
String a1 = "111";
// jdk10之后的写法
var a2 = "222";

Http客户端链式操作

public class Test{
    public static void main(String[] args){
        var httpClient = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_1_1)
            .connectTimeout(Duration.ofSeconds(10))
            .build();
        var resuest = HttpClient.newBuilder()
            .GET()
            .uri(URI.create("www.baidu.com"))
            .setHeader("User-Agent","Google")
            .build();
    }
}

提供了原生的websocket相关api(这块暂无测试)

主要得益于SocketAPI 重构,Socket的底层实现优化,引入了NIO

空指针问题

在jdk14之前,异常捕获后抛出信息只有java.lang.NullPointerException
测试代码

public static void main(String[] args) {
        try {
            List<Integer> list = new ArrayList<>();
            list.add(null);
            System.out.println(list.get(0).longValue());
        }catch (Exception e){
            e.printStackTrace();
        }
    }

在jdk14之后,会显示具体的空指针对象,具体返回如下:

java.lang.NullPointerException: Cannot invoke "java.lang.Integer.longValue()" because the return value of "java.util.List.get(int)" is null

switch改造

jdk17之前版本采用了break返回的方式

public static int testBreak(String mode){
    int i = 0;
    switch(mode){
        case "a","b":
            i = 1;
            break;
        case "c":
            i = 2;
            break;
        default:
            i = 3;
            break;
    }
    return i;
}

jdk14之后采用了yield

public static void testYield(String mode){
    int result = switch(mode){
        case "a","b":
            yield 1; // 使用yield提供返回值
        case "c":
            yield 2; // 使用yield提供返回值
        default:
            yield 3; // 使用yield提供返回值
    }
    return result;
}

或者直接建图返回结果

public static void testYield(String mode){
    int result = switch(mode){
        case "a","b" -> 1;
        case "c" -> 2;
        default -> 3;
    }
    return result;
}

文本块的引入

// jdk1.8的写法
String html = "<html>\n" +
            "   <head>\n" +
            "       <title>test</title>\n" +
            "   </head>\n" +
            "   <body>\n" +
            "       <h1>test</h1>\n" +
            "   </body>\n" +
            "  </html>\n";
// jdk13的写法
var jdk15Html = """
                <html>
                    <body>
                        <p>hello, world</p>
                    </body>
                </html>
                """;

instanceof的变化

jdk8是使用instanceof关键字判断类型之后,再强转,示例代码如下

// jdk8
if (obj instanceof String) {
    String s = (String) obj;
    if (s.equals("1")) {
        System.out.println("1");
    }
}
// jdk16,则直接可在if中完成操作,str为申明的变量
if (obj instanceof String str && str.equals("1")) {
    System.out.println("1");
}

申明对象时可用record关键字实现get set方法

public record User(long id,String name,int age){

 }

提供了VarHandle

jdk9之后提供的VarHandle时对Unsafe的一个优化,jdk9之前的Unsafe是不建议开发者直接使用的,因为Unsafe所操作的并不属于Java标准,会容易带来一些安全性的问题

什么是VarHandle

Varhandle是对变量或参数定义的变量系列的动态强类型引用,包括静态字段,非静态字段,数组元素或堆外数据结构的组件。 在各种访问模式下都支持访问这些变量,包括简单的读/写访问,volatile 的读/写访问以及 CAS (compare-and-set)访问。简单来说 Variable 就是对这些变量进行绑定,通过 Varhandle 直接对这些变量进行操作。

举例

public class Demo {
    public int publicVar = 1;
    protected int protectedVar = 2;
    private int privateVar = 3;
    public int[] arrayData = new int[]{1, 2, 3};
    @Override
    public String toString() {
        return "Demo{" +
                "publicVar=" + publicVar +
                ", protectedVar=" + protectedVar +
                ", privateVar=" + privateVar +
                ", arrayData=" + Arrays.toString(arrayData) +
                '}';
    }
}
// 访问private成员
private static void privateDemo() throws NoSuchFieldException, IllegalAccessException {
    Demo instance = new Demo();
    VarHandle varHandle = MethodHandles.privateLookupIn(Demo.class, MethodHandles.lookup())
            .findVarHandle(Demo.class, "privateVar", int.class);
    varHandle.set(instance, 33);
    System.out.println(instance);
}

输出:Demo

// 访问 protected 成员
private static void protectedDemo() throws NoSuchFieldException, IllegalAccessException {
    Demo instance = new Demo();

      VarHandle varHandle = MethodHandles.privateLookupIn(Demo.class,MethodHandles.lookup())
              .findVarHandle(Demo.class, "protectedVar", int.class);

    VarHandle varHandle = MethodHandles.lookup()
            .in(Demo.class)
            .findVarHandle(Demo.class, "protectedVar", int.class);
    varHandle.set(instance, 22);
    System.out.println(instance);
}

输出:Demo

// 访问public成员
private static void publicDemo() throws NoSuchFieldException, IllegalAccessException {
     Demo instance = new Demo();
     VarHandle varHandle = MethodHandles.lookup()
             .in(Demo.class)
             .findVarHandle(Demo.class, "publicVar", int.class);
     varHandle.set(instance, 11);
     System.out.println(instance);
 }

输出:Demo

// 访问数组
private static void arrayDemo() throws NoSuchFieldException, IllegalAccessException {
    Demo instance = new Demo();
    VarHandle arrayVarHandle = MethodHandles.arrayElementVarHandle(int[].class);
    arrayVarHandle.compareAndSet(instance.arrayData, 0, 1, 11);
    arrayVarHandle.compareAndSet(instance.arrayData, 1, 2, 22);
    arrayVarHandle.compareAndSet(instance.arrayData, 2, 3, 33);
    System.out.println(instance);
}

输出:Demo

Varhandle方法汇总

  • MethodHandles.privateLookupIn(class, MethodHandles.lookup())获取访问私有变量的Lookup
  • MethodHandles.lookup() 获取访问protected、public的Lookup
  • findVarHandle:用于创建对象中非静态字段的VarHandle。接收参数有三个,第一个为接收者的class对象,第二个是字段名称,第三个是字段类型。
  • findStaticVarHandle:用于创建对象中静态字段的VarHandle,接收参数与findVarHandle一致。
  • unreflectVarHandle:通过反射字段Field创建VarHandle。
  • MethodHandles.arrayElementVarHandle(int[].class) 获取管理数组的 Varhandle

内存屏障

VarHandle 除了支持各种访问模式下访问变量之外,还提供了一套内存屏障方法,目的是为了给内存排序提供更细粒度的控制。主要如下几个方法:

public static void fullFence() {
    UNSAFE.fullFence();
}
public static void acquireFence() {
    UNSAFE.loadFence();
}
public static void releaseFence() {
    UNSAFE.storeFence();
}
public static void loadLoadFence() {
    UNSAFE.loadLoadFence();
}
public static void storeStoreFence() {
    UNSAFE.storeStoreFence();
}

小结

在 java9 之后,对一些变量的并发操作时,可以考虑用 java.lang.invoke.VarHandle 来处理,而不是通过 Unsafe 类来处理,毕竟 Unsafe 不太适合直接使用。

Sealed Classes(封闭类)

  • 密封Sealed表示的概念是可以扩展一个类,但只能通过已知的子类型列表进行扩展,而不能通过其他任何扩展。
  • 其他语言可能对功能的看法有所不同,在Java中,应将其视为代表几乎最终类的功能。
  • Java 16开始,允许使用 sealed 修饰class,并通过permits明确写出能够从该class继承的子类名称。
// 测试代码
public sealed class Car permits MiniCar, SmallCar, Bigcar{
    ...
}
// 以下代码可以正常运行
public final class MiniCar extends Car {...}
// 以下代码会报错
public final class Train extends Car {...}

其他新特性

其他新特性有待于各位小伙伴努力尝试

来源:不晓得哪里抄的

posted @ 2023-03-23 17:09  bigroc  阅读(1777)  评论(0编辑  收藏  举报