【Java】你应该知道的JDK19新特性

9月20日,JDK19 终于在千呼万唤中正式发布,随之发布的新特性中包括了最为广受期待的虚拟线程,以改善多线程、并发编程难度。

在这里插入图片描述

1 前言

1.1 哪些新特性

本次 JDK19 中带来了 7 个新特性:

更多详细介绍推荐去阅读原文,本文仅从笔者自身代码编写的角度出发,展示有哪些全新的代码结构、书写方式被支持。

1.2 本地编译运行

JDK19 虽然已经发布,但目前 Idea 还没有支持到 JDK19,所以只能用命令行的方式编译运行:

javac --release 19 --enable-preview -Xlint:preview Main.java
java --enable-preview Main

2 记录模式(Record Patterns)

record 关键字首次在 JDK14 中出现,它允许用一种更简单的方式去构造一个不可变对象。

记录模式可以极其方便去编写一个 pojo 类,比如之前需要这么编写的一个拥有属性 x 和 y 的 Point 类:

class Point{
    
    
    private int x;
    
    private int y;

    public Point(int x, int y) {
    
    
        this.x = x;
        this.y = y;
    }

    public int getX() {
    
    
        return x;
    }

    public void setX(int x) {
    
    
        this.x = x;
    }

    public int getY() {
    
    
        return y;
    }

    public void setY(int y) {
    
    
        this.y = y;
    }
}

使用 record 关键字后,只需要写一行代码即可实现上述 pojo 类声明:

record Point(int x, int y) {
    
    }

而在 JDK19 中进一步增强了记录模式,不仅可以使用 instanceof 来进行对象类型的判断,还可以直接从 x、y 中获取值:

record Point(int x, int y) {
    
    }

void printSum(Object o) {
    
    
    if (o instanceof Point(int x, int y)) {
    
    
        System.out.println(x+y);
    }
}

当然,在这种思想下,我们还可以编写出拥有嵌套多层的代码从而匹配更加复杂的对象。

3 外部函数和内存 API(Foreign Function & Memory API

在此之前,开发者如果想在 Java 代码中调用外部代码或者操作外部内存(JVM 之外的内存),只能通过JNI(Java Native Interface)的方式进行操作,这种方式主要就是通过调用 C、C++代码来进行实现,这种方式是危险且脆弱的。而在 JDK19 中引入了一个 API 来解决 JNI 这种脆弱且危险的调用方式。

// 1. Find foreign function on the C library path
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
MethodHandle radixSort = linker.downcallHandle(stdlib.lookup("radixsort"), ...);
// 2. Allocate on-heap memory to store four strings
String[] javaStrings   = {
    
     "mouse", "cat", "dog", "car" };
// 3. Allocate off-heap memory to store four pointers
SegmentAllocator allocator = SegmentAllocator.implicitAllocator();
MemorySegment offHeap  = allocator.allocateArray(ValueLayout.ADDRESS, javaStrings.length);
// 4. Copy the strings from on-heap to off-heap
for (int i = 0; i < javaStrings.length; i++) {
    
    
    // Allocate a string off-heap, then store a pointer to it
    MemorySegment cString = allocator.allocateUtf8String(javaStrings[i]);
    offHeap.setAtIndex(ValueLayout.ADDRESS, i, cString);
}
// 5. Sort the off-heap data by calling the foreign function
radixSort.invoke(offHeap, javaStrings.length, MemoryAddress.NULL, '\0');
// 6. Copy the (reordered) strings from off-heap to on-heap
for (int i = 0; i < javaStrings.length; i++) {
    
    
    MemoryAddress cStringPtr = offHeap.getAtIndex(ValueLayout.ADDRESS, i);
    javaStrings[i] = cStringPtr.getUtf8String(0);
}
assert Arrays.equals(javaStrings, new String[] {
    
    "car", "cat", "dog", "mouse"});  // true

4 虚拟线程(Virtual Threads)

Java 终于可以原生支持虚拟线程了,对于 IO 密集型任务,使用虚拟线程可以带来更高的并发和更低的系统消耗从而来提高更大的吞吐量。

此次在 Thread 类中新增加了一个工厂方法 ofVirtual(),通过该方法可以很方便的创建虚拟线程。在具体使用上,感觉虚拟线程和目前真实线程方法和使用没有太大区别。

// 声明虚拟线程
Thread ofVirtual = Thread.ofVirtual().start(new Runnable() {
    
    
    public void run() {
    
    
        System.out.println("this is Thread.ofVirtual()");
    }
});

// 声明虚拟线程池
ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();

据官方数据说明,使用虚拟线程池,可以轻松支持 10000 个虚拟线程的并发,并且在 OS 层仅仅是用了少数的真实线程,可能在 OS 层仅仅使用一个线程!!!

官方原文:can easily support 10,000 virtual threads running,Behind the scenes, the JDK runs the code on a small number of OS threads, perhaps as few as one.

5 Switch 的模式匹配(Pattern Matching for Switch)

JDK19 中也进一步增强了switch的匹配模式,比如之前我们如果要进行对象类型匹配,可能会编写以下代码:

public String formatter(Object o) {
    
    
    String formatted = "unknown";
    if (o instanceof Integer i) {
    
    
        formatted = String.format("int %d", i);
    } else if (o instanceof Long l) {
    
    
        formatted = String.format("long %d", l);
    } else if (o instanceof Double d) {
    
    
        formatted = String.format("double %f", d);
    } else if (o instanceof String s) {
    
    
        formatted = String.format("String %s", s);
    }
    return formatted;
}

而在新版本中,可以通过 switch 的箭头表达式来简化上述代码:

public String formatterPatternSwitch(Object o) {
    
    
    return switch (o) {
    
    
        case Integer i -> String.format("int %d", i);
        case Long l    -> String.format("long %d", l);
        case Double d  -> String.format("double %f", d);
        case String s  -> String.format("String %s", s);
        default        -> o.toString();
    };
}

同时还支持 null 值和多值匹配:

public void testFooBar(String s) {
    
    
    switch (s) {
    
    
        case null         -> System.out.println("Oops");
        case "Foo", "Bar" -> System.out.println("Great");
        default           -> System.out.println("Ok");
    }
}

甚至还支持了 case when 语句:

// 在此之前需要在 case 中通过 if 判断
public void test(Object o) {
    
    
    switch (o) {
    
    
        case String s:
            if (s.length() == 1) {
    
     ... }
            else {
    
     ... }
            break;
        ...
    }
}

// 现在可以直接通过 when 语法实现
public void test(Object o) {
    
    
    switch (o) {
    
    
        case String s when s.length() == 1 -> ...
        case String s                      -> ...
        ...
    }
}

6 结构化并发编程(Structured Concurrency)

在 JDK19 中提出了 Java 的结构化并发编程实现方式,但目前仍是孵化器阶段,简单来理解,我们可以将所有线程的生命周期声明在一个 scope 中,并且通过 scope 的控制来控制线程。

 Response handle() throws ExecutionException, InterruptedException {
    
    
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    
    
        Future<String>  user  = scope.fork(() -> findUser());
        Future<Integer> order = scope.fork(() -> fetchOrder());

        scope.join();           // Join both forks
        scope.throwIfFailed();  // ... and propagate errors

        // Here, both forks have succeeded, so compose their results
        return new Response(user.resultNow(), order.resultNow());
    }
}

7 结语

从 Java 出现至今,已经走过了 20 多个年头,不断出现的新特性相信必将给 Java 带来更强的生命力和更大的应用场景,同时 Java 也在不断挑战并拓展着自身的边界,虽然本次发布的 JDK19 并不是 LTS 版本,只有 6 个月的支持期,但其带来的新特性尤其是虚拟线程依然是非常重磅的。

猜你喜欢

转载自blog.csdn.net/adminpd/article/details/127180748