Skip to content
<

个人感觉 Java 18 提⁢供的功能都没什么用⁡,简单了解一下就好⁡。

正式特性

【了解】UTF-8 作为默认字符集

Java 18 将 UTF-8 设⁢为默认字符集,解决了很⁡多字符编码相关的问题,⁡Java 程序在不同平台上的行为会更加一致。

java
// 这些操作现在默认使用 UTF-8 编码  
FileReader reader = new FileReader("file.txt");  
FileWriter writer = new FileWriter("file.txt");  
PrintStream out = new PrintStream("output.txt");

在这之前,Java 使用的是 系统默认字符集,会导致同一段代码在不同操作系统上可能产生完全不同的结果。

字符集统一的好处

java
public class CharsetExample {  
    public static void main(String[] args) throws IOException {  
        String chinese = "你好,世界!";  
          
        // Java 18 之前,这在不同系统上可能有不同结果  
        try (FileWriter writer = new FileWriter("test.txt")) {  
            writer.write(chinese);  
        }  
          
        try (FileReader reader = new FileReader("test.txt")) {  
            char[] buffer = new char[1024];  
            int length = reader.read(buffer);  
            String result = new String(buffer, 0, length);  
            System.out.println("读取结果: " + result);  
        }  
          
        // 现在在所有平台上都会正确处理中文  
        System.out.println("默认字符集: " + Charset.defaultCharset());  
        // 输出: UTF-8  
    }  
}

影响的 API

以下 API 现在默认使用 UTF-8:

java
// 文件操作  
new FileReader("file.txt")           // 现在默认 UTF-8  
new FileWriter("file.txt")           // 现在默认 UTF-8  
new PrintWriter("file.txt")          // 现在默认 UTF-8  
  
// 流操作  
new InputStreamReader(inputStream)   // 现在默认 UTF-8  
new OutputStreamWriter(outputStream) // 现在默认 UTF-8  
  
// 格式化输出  
new PrintStream("file.txt")          // 现在默认 UTF-8  
new Formatter("file.txt")            // 现在默认 UTF-8

【了解】简单 Web 服务器

Java 18 引入了一个简单的 W⁢eb 服务器,主要用于开发和⁡测试。       ⁡

bash
# 启动简单的文件服务器,服务当前目录  
jwebserver  
  
# 指定端口和目录  
jwebserver -p 8080 -d /path/to/your/files  
  
# 绑定到特定地址  
jwebserver -b 127.0.0.1 -p 9000

Nginx 不香么,我要用这个东西?

实际使用场景

bash
# 快速分享文件  
cd ~/Documents  
jwebserver -p 8000  
# 访问 http://localhost:8000 查看文件  
  
# 测试静态网站  
cd my-website/dist  
jwebserver -p 3000  
  
# 开发时的临时服务器  
jwebserver -b 0.0.0.0 -p 8080 -d ./public

这个工具主要用于:

  • 快速原型开发:不需要配置复杂的服务器

  • 文件分享:在局域网内快速分享文件

  • 静态网站测试:测试前端项目

  • 教学演示:简单的 HTTP 服务器示例

【了解】JavaDoc 代码片段

Java 18 引入了 @snippet 标签,可以让 JavaDoc 生成的代码示例更美观,而且支持从外部文件引入代码片段。

java
/**  
 * 计算两个数的最大公约数  
 *   
 * {@snippet :  
 * int a = 48;  
 * int b = 18;  
 * int gcd = MathUtils.gcd(a, b);  
 * System.out.println("GCD: " + gcd); // @highlight substring="GCD"  
 * }  
 *   
 * @param a 第一个数  
 * @param b 第二个数  
 * @return 最大公约数  
 */  
public static int gcd(int a, int b) {  
    // 实现代码...  
}  
  
/**  
 * 从外部文件引入代码片段  
 *   
 * {@snippet file="examples/QuickSort.java" region="main-algorithm"}  
 *   
 * @param arr 要排序的数组  
 */  
public static void quickSort(int[] arr) {  
    // 实现代码...  
}

@snippet 的高级功能

java
/**  
 * 演示数组操作  
 *   
 * {@snippet :  
 * int[] numbers = {3, 1, 4, 1, 5}; // @highlight  
 * Arrays.sort(numbers);            // @highlight  
 * System.out.println(Arrays.toString(numbers)); // @highlight substring="toString"  
 * // 输出: [1, 1, 3, 4, 5]         // @replace regex="输出:" replacement="结果:"  
 * }  
 */  
public void demonstrateArraySort() {  
    // 方法实现  
}  
  
/**  
 * 使用外部代码文件  
 *   
 * {@snippet file="examples/DatabaseExample.java" region="connection-setup"}  
 *   
 * 上面的代码展示了如何建立数据库连接。  
 */  
public void setupDatabase() {  
    // 方法实现  
}

外部文件示例(examples/DatabaseExample.java):

java
public class DatabaseExample {  
    public void example() {  
        // @start region="connection-setup"  
        String url = "jdbc:mysql://localhost:3306/mydb";  
        String username = "user";  
        String password = "password";  
          
        try (Connection conn = DriverManager.getConnection(url, username, password)) {  
            // 使用连接进行数据库操作  
        } catch (SQLException e) {  
            e.printStackTrace();  
        }  
        // @end  
    }  
}

不过这年头还有开发者阅读 JavaDoc 么?

【了解】使用方法句柄重新实现核心反射

Java 18 在内部使用方⁢法句柄重新实现了核⁡心反射功能,这个改⁡进对开发者是透明的,但可以提供:

  • 更好的性能:方法句柄比传统反射更快

  • 更少的内存占用:减少了元数据开销

  • 更好的 JIT 优化:编译器可以更好地优化

java
// 反射 API 的使用方式没有变化  
Class<?> clazz = String.class;  
Method method = clazz.getMethod("length");  
Object result = method.invoke("Hello");  
  
// 但底层实现使用了方法句柄,性能更好

【了解】互联网地址解析 SPI

Java 18 引入了互联网⁢地址解析的服务提供⁡者接口(SPI),⁡允许自定义 DNS 解析:

java
// 自定义地址解析器  
public class CustomInetAddressResolver extends InetAddressResolver {  
    @Override  
    public Stream<InetAddress> lookupByName(String host, LookupPolicy lookupPolicy) {  
        // 自定义 DNS 解析逻辑  
        if ("myapp.local".equals(host)) {  
            return Stream.of(InetAddress.getByName("127.0.0.1"));  
        }  
        // 委托给默认解析器  
        return InetAddressResolver.platformResolver().lookupByName(host, lookupPolicy);  
    }  
      
    @Override  
    public String lookupByAddress(byte[] addr) {  
        // 自定义反向 DNS 解析  
        return InetAddressResolver.platformResolver().lookupByAddress(addr);  
    }  
}

这个特性主要用于:

  • 企业内部 DNS:解析内部域名

  • 负载均衡:自定义负载均衡策略

  • 缓存优化:实现 DNS 缓存策略

  • 测试环境:模拟不同的网络环境

【了解】弃用完成以供删除

Java 18 将 Finalization 标记为弃用并计划删除:

java
// 这种做法已被弃用  
public class OldStyleResource {  
    @Override  
    protected void finalize() throws Throwable {  
        // 不推荐:依赖 finalize 进行资源清理  
        super.finalize();  
    }  
}  
  
// 推荐的资源管理方式  
public class ModernResource implements AutoCloseable {  
    @Override  
    public void close() {  
        // 推荐:显式资源管理  
    }  
}  
  
// 使用 try-with-resources  
try (ModernResource resource = new ModernResource()) {  
    // 使用资源  
} // 自动调用 close()

预览特性

【了解】switch 的模式匹配(第二次预览)

Java 18 继续完善 sw⁢itch 的模式匹配⁡:         ⁡

java
// 需要启用预览功能  
// javac --enable-preview --release 18 PatternMatchingExample.java  
// java --enable-preview PatternMatchingExample  
  
public String processValue(Object obj) {  
    return switch (obj) {  
        case String s -> "字符串: " + s;  
        case Integer i -> "整数: " + i;  
        case Long l -> "长整数: " + l;  
        case Double d -> "浮点数: " + d;  
        case null -> "空值";  
        default -> "其他类型: " + obj.getClass().getSimpleName();  
    };  
}  
  
// 结合条件判断(守卫模式)  
public String processString(Object obj) {  
    return switch (obj) {  
        case String s && s.length() > 10 -> "长字符串: " + s.substring(0, 10) + "...";  
        case String s && s.isEmpty() -> "空字符串";  
        case String s -> "字符串: " + s;  
        case null -> "空值";  
        default -> "非字符串";  
    };  
}

与 Records 和 Sealed 类结合

java
public sealed interface Shape permits Circle, Rectangle, Triangle {}  
public record Circle(double radius) implements Shape {}  
public record Rectangle(double width, double height) implements Shape {}  
public record Triangle(double base, double height) implements Shape {}  
  
public double calculateArea(Shape shape) {  
    return switch (shape) {  
        case Circle(var radius) -> Math.PI * radius * radius;  
        case Rectangle(var width, var height) -> width * height;  
        case Triangle(var base, var height) -> 0.5 * base * height;  
        // 不需要 default,sealed 类保证完整性  
    };  
}  
  
// 更复杂的模式匹配  
public String describeShape(Shape shape) {  
    return switch (shape) {  
        case Circle(var r) && r > 10 -> "大圆,半径:" + r;  
        case Circle(var r) -> "小圆,半径:" + r;  
        case Rectangle(var w, var h) && w == h -> "正方形,边长:" + w;  
        case Rectangle(var w, var h) -> "矩形,宽:" + w + ",高:" + h;  
        case Triangle(var b, var h) -> "三角形,底:" + b + ",高:" + h;  
    };  
}

孵化器特性

【了解】向量 API(第三次孵化器)

Java 18 继续改进向量 API:

java
import jdk.incubator.vector.*;  
  
public class VectorOperations {  
    private static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;  
      
    // 向量化的数组求和  
    public static float vectorSum(float[] array) {  
        float sum = 0.0f;  
        int upperBound = SPECIES.loopBound(array.length);  
          
        // 向量化部分  
        FloatVector accVector = FloatVector.zero(SPECIES);  
        for (int i = 0; i < upperBound; i += SPECIES.length()) {  
            FloatVector vector = FloatVector.fromArray(SPECIES, array, i);  
            accVector = accVector.add(vector);  
        }  
        sum += accVector.reduceLanes(VectorOperators.ADD);  
          
        // 处理剩余元素  
        for (int i = upperBound; i < array.length; i++) {  
            sum += array[i];  
        }  
          
        return sum;  
    }  
      
    // 向量化的数组乘法  
    public static void vectorMultiply(float[] a, float[] b, float[] result) {  
        int upperBound = SPECIES.loopBound(a.length);  
          
        for (int i = 0; i < upperBound; i += SPECIES.length()) {  
            FloatVector va = FloatVector.fromArray(SPECIES, a, i);  
            FloatVector vb = FloatVector.fromArray(SPECIES, b, i);  
            FloatVector vr = va.mul(vb);  
            vr.intoArray(result, i);  
        }  
          
        // 处理剩余元素  
        for (int i = upperBound; i < a.length; i++) {  
            result[i] = a[i] * b[i];  
        }  
    }  
      
    public static void main(String[] args) {  
        float[] numbers = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f};  
        System.out.println("向量化求和: " + vectorSum(numbers));  
          
        float[] a = {1, 2, 3, 4, 5, 6, 7, 8};  
        float[] b = {8, 7, 6, 5, 4, 3, 2, 1};  
        float[] result = new float[8];  
          
        vectorMultiply(a, b, result);  
        System.out.println("向量化乘法结果: " + Arrays.toString(result));  
    }  
}

【了解】外部函数和内存 API(第二次孵化器)

Java 18 继续完善外部函数和内存 API:

java
import jdk.incubator.foreign.*;  
  
public class ForeignFunctionExample {  
    public static void main(String[] args) throws Throwable {  
        // 获取系统链接器  
        Linker linker = Linker.nativeLinker();  
        SymbolLookup stdlib = linker.defaultLookup();  
          
        // 查找 C 标准库函数 strlen  
        MemoryAddress strlenAddr = stdlib.lookup("strlen").orElseThrow();  
          
        // 创建函数描述符  
        FunctionDescriptor strlenDesc = FunctionDescriptor.of(  
            CLinker.C_LONG,     // 返回类型  
            CLinker.C_POINTER   // 参数类型  
        );  
          
        // 创建方法句柄  
        MethodHandle strlen = linker.downcallHandle(strlenAddr, strlenDesc);  
          
        // 分配内存并调用函数  
        try (ResourceScope scope = ResourceScope.newConfinedScope()) {  
            MemorySegment cString = CLinker.toCString("Hello, Foreign Function!", scope);  
            long length = (long) strlen.invoke(cString);  
            System.out.println("字符串长度: " + length);  
        }  
          
        // 调用 printf 函数  
        MemoryAddress printfAddr = stdlib.lookup("printf").orElseThrow();  
        FunctionDescriptor printfDesc = FunctionDescriptor.of(  
            CLinker.C_INT,  
            CLinker.C_POINTER  
        );  
        MethodHandle printf = linker.downcallHandle(printfAddr, printfDesc);  
          
        try (ResourceScope scope = ResourceScope.newConfinedScope()) {  
            MemorySegment formatStr = CLinker.toCString("Hello from Java %d!\n", scope);  
            printf.invoke(formatStr, 2024);  
        }  
    }  
}

性能改进

【了解】JIT 编译器优化

Java 18 包含了多项 JIT 编译器优化:

  • 更好的循环优化:改进了循环展开和向量化

  • 方法内联优化:更智能的内联决策

  • 逃逸分析改进:减少不必要的堆分配

【了解】垃圾收集器改进

G1 垃圾收集器优化

bash
# G1 在 Java 18 中的性能改进  
java -XX:+UseG1GC \  
     -XX:MaxGCPauseMillis=100 \  
     -XX:G1HeapRegionSize=16m \  
     MyApp

ZGC 和 Shenandoah 优化

bash
# ZGC 改进  
java -XX:+UseZGC \  
     -XX:ZCollectionInterval=0 \  
     MyApp  
  
# Shenandoah 改进  
java -XX:+UseShenandoahGC \  
     -XX:ShenandoahGCMode=iu \  
     MyApp

总结

Java 18 虽然没有引入革命性的特性,但在⁢基础设施方面做了很多改进。UT⁡F-8 作为默认字符集解决了跨⁡平台一致性问题,简单 Web 服务器为开发和测试提供了便利。

switch 模式匹配的继续完善为未⁢来的正式版本做了准备,向⁡量 API 和外部函数 ⁡API 的改进也为高性能计算提供了更好的支持。

虽然 Java 18 的特性看起来⁢不够亮眼,但这些基础改⁡进为后续版本奠定了重要⁡基础,特别是在跨平台兼容性和开发工具方面的提升。