2.6 Java 11 版本新增特性和类

Java 11是自Java 8后的首个长期支持版本,非常值得关注。在语法灵活性、标准库丰富度以及底层性能上进行了大量优化。

一、语言语法特性

1. Lambda 参数支持局部变量类型推断(var)

允许在隐式类型 Lambda 表达式的每个形参前使用保留类型名 var。在 Java 10 中,var 关键字被引入用于局部变量类型推断,但当时它不能用于 Lambda 表达式的参数,Java 11 将 var 的适用范围扩展到了 Lambda 参数,使得 var 在局部变量和 Lambda 参数中的使用风格保持统一。

为什么需要这个特性?

  • 语法一致性:让局部变量(var x = ...)和 Lambda 形参((var x, var y) -> ...)的声明语法统一,减少认知负担。
  • 支持注解:这是最实际的用途。在 Java 11 之前,你无法为隐式类型的 Lambda 形参添加注解(如 @Nullable、@Nonnull)。现在,通过 var,你可以像修饰普通局部变量一样,为 Lambda 形参添加上注解,从而利用类型检查器进行更严格的静态分析。

它的核心价值不在于"简化代码"(因为省略类型已经够简洁了),而在于为 Lambda 参数添加注解提供了一个优雅的语法位置,同时保持了 var 在整个 Java 语言中使用风格的一致性。编译器仍然是根据 Lambda 表达式的目标类型(函数式接口的泛型参数)来推断参数类型,var 本身不提供任何额外的类型信息。

(1)基本用法:

javascript 复制代码
// 单参数 
Consumer<String> consumer = (var s) -> System.out.println(s); 

// 多参数 
BinaryOperator<String> concat = (var s1, var s2) -> s1 + s2;

(2)结合注解使用

less 复制代码
import org.jetbrains.annotations.NotNull; 
import org.jetbrains.annotations.Nullable; 

// 为 Lambda 参数添加注解 
Function<String, Integer> length = (@NotNull var s) -> s.length(); 

// 多参数注解 
Comparator<String> comparator = (@NotNull var a, @Nullable var b) -> a.compareTo(b);

(3)结合修饰符使用

sql 复制代码
// 使用 final 修饰符 
Function<Integer, Integer> square = (final var x) -> x * x;

使用规则与限制:

  • var 仅适用于隐式类型 Lambda 表达式。对于显式类型 Lambda(如 (String s) -> ...),继续使用具体的类型名即可。
  • var 不是关键字,而是一个保留类型名。这意味着你仍然可以将 var 用作变量名、方法名或包名,但不能将其用作类名或接口名。

二、新增API/包

1. 标准化 HTTP Client API (java.net.http)

标志着 Java 终于拥有了一个现代化的、内置的标准 HTTP 客户端,彻底告别了老旧且难用的 HttpURLConnection。在 Java 11 之前,Java 标准库中唯一的 HTTP 客户端是 HttpURLConnection,它存在诸多痛点:

  • 仅支持阻塞模式:每个请求独占一个线程,无法适配高并发异步场景。
  • 不支持 HTTP/2:无法享受多路复用、头部压缩等现代协议带来的性能提升。
  • API 设计繁琐:手动管理输入输出流、连接断开等,极易导致连接泄漏。
  • 功能缺失:没有内置的连接池管理、重定向策略等常用能力。

java.net.http 包定义了四大核心类型:

  • HttpClient:HTTP 客户端,跨请求的通用配置容器(协议版本、超时、代理、连接池等)。通过 HttpClient.newBuilder() 或 HttpClient.newHttpClient() 创建,一旦构建完成,就是不可变的,可用于发送多个请求。其 Builder 可以配置首选协议版本(HTTP/1.1 或 HTTP/2)、重定向策略、代理和认证器等。
  • HttpRequest:HTTP 请求,通过 Builder 模式构建(URI、方法、请求头、请求体等)。通过 HttpRequest.newBuilder() 创建,可以设置 URI、请求方法(GET, POST 等)、请求体、超时时间和请求头。构建后也是不可变的。
  • HttpResponse<T>:HTTP 响应,泛型 T 是响应体的类型,泛型参数由响应体处理器(BodyHandler)决定。它提供了 statusCode()、headers() 和 body() 等方法。
  • WebSocket:WebSocket 客户端,支持双向实时通信。
  • HttpRequest.BodyPublishers:提供多种请求体发布器(字符串、文件、字节数组等)。
  • HttpResponse.BodyHandlers:提供多种响应体处理器(字符串、文件、字节数组、输入流等)。

(1)创建 HttpClient

java 复制代码
import java.net.http.HttpClient;
import java.net.http.HttpClient.Version;
import java.net.http.HttpClient.Redirect;
import java.net.ProxySelector;
import java.net.InetSocketAddress;
import java.time.Duration;

// 方式一:使用默认配置
HttpClient defaultClient = HttpClient.newHttpClient();

// 方式二:使用 Builder 进行定制
HttpClient customClient = HttpClient.newBuilder()
        .version(Version.HTTP_2)                       // 首选 HTTP/2
        .followRedirects(Redirect.NORMAL)              // 允许正常重定向
        .connectTimeout(Duration.ofSeconds(10))        // 连接超时10秒
        .proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 8080)))
        .build();

(2)发送同步 GET 请求

java 复制代码
import java.net.URI;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
// 1. 创建 HttpClient(可复用)
HttpClient client = HttpClient.newHttpClient();

// 2. 构建 HttpRequest
HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://httpbin.org/get"))
        .header("Accept", "application/json")
        .GET() // 默认就是 GET,可以省略
        .build();

// 3. 同步发送,阻塞等待响应,响应体作为 String 处理
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
// 4. 处理响应
System.out.println("状态码: " + response.statusCode());
System.out.println("响应头: " + response.headers());
System.out.println("响应体: " + response.body());

(3)发送异步 GET 请求

arduino 复制代码
// 3. 异步发送,sendAsync 返回 CompletableFuture,非阻塞
CompletableFuture<HttpResponse<String>> future = client
                .sendAsync(request, HttpResponse.BodyHandlers.ofString());
// 链式处理响应
future.thenApply(HttpResponse::body)          // 提取响应体
      .thenAccept(System.out::println)        // 消费响应体
      .exceptionally(ex -> {                  // 异常处理
            ex.printStackTrace();
            return null;
        })
      .join(); // 等待异步流程完成

(4)发送 POST 请求(JSON)

swift 复制代码
import java.net.http.HttpRequest.BodyPublishers;

String jsonBody = "{\"name\": \"Alice\", \"age\": 30}";
HttpRequest postRequest = HttpRequest.newBuilder()
        .uri(URI.create("https://httpbin.org/post"))
        .header("Content-Type", "application/json")
        .POST(BodyPublishers.ofString(jsonBody)) // 请求体为 JSON 字符串
        .build();

HttpResponse<String> postResponse = client.send(postRequest, BodyHandlers.ofString());

System.out.println("状态码: " + postResponse.statusCode());
System.out.println("响应体: " + postResponse.body());

(5)使用 WebSocket

typescript 复制代码
WebSocket ws = client.newWebSocketBuilder()
.buildAsync(URI.create("wss://echo.websocket.org"), new WebSocket.Listener() {
    @Override
    public void onOpen(WebSocket webSocket) {
        System.out.println("WebSocket 已连接");
        webSocket.sendText("Hello, WebSocket!", true);
    }                    
    
    @Override
    public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
        System.out.println("收到消息: " + data);
        webSocket.request(1); // 请求下一条消息
        return null;
    }
})
.join(); // 等待连接建立

注意事项:

  • 线程模型:异步任务的执行线程由 HttpClient 的 executor() 决定。如果未指定,将使用默认的执行器。由 CompletionStage 触发的依赖任务,如果未指定执行器,将在 CompletableFuture 的默认执行器或调用线程中运行。
  • 不可变性:HttpClient 和 HttpRequest 实例在构建后都是不可变的,这意味着它们是线程安全的,可以在多个线程间安全地共享和复用。
  • null 参数:除非特别说明,向该包中任何方法传递 null 参数都会导致 NullPointerException。
2. String 类实用方法增强

新增了6个高频使用的实例方法,覆盖了空白字符处理、多行文本分割和字符串重复拼接等常见场景,大幅减少了对外部工具类(如 Apache Commons Lang)的依赖。

  • isBlank():判断字符串是否为空或仅包含空白字符,替代 str.trim().isEmpty(),且能识别 Unicode 空白字符。
  • lines():按行终止符分割字符串,返回 Stream<String>,替代 BufferedReader 或 StringTokenizer。
  • strip():去除首尾空白字符(Unicode 感知),替代 trim(),支持所有 Unicode 空白字符。
  • stripLeading():仅去除开头空白字符。
  • stripTrailing():仅去除末尾空白字符。
  • repeat(int n):将字符串重复 n 次,替代 StringBuilder 循环拼接。

strip() 与 trim() 的关键区别:

trim() 只移除码点小于等于 U+0020 的字符(即 ASCII 空格),而 strip() 基于 Character.isWhitespace() 判断,能处理所有 Unicode 空白字符,包括全角空格(\u3000)、零宽空格(\u200B)、制表符等。

less 复制代码
// trim() 无法去除全角空格(U+3000)
String s = "\u3000Hello\u3000";
System.out.println(s.trim().length());  // 输出: 7(全角空格仍在)
System.out.println(s.strip().length()); // 输出: 5(全角空格被去除)
3. Files 类文件读写简化

新增了readString() 和 writeString() 两个静态方法,将常见的文本文件读写操作简化为一行代码,内部自动处理资源的打开与关闭。

  • readString(Path):读取文件全部内容为字符串(默认 UTF-8)。
  • readString(Path, Charset):指定字符集读取文件全部内容。
  • writeString(Path, CharSequence, OpenOption...):将字符串写入文件(默认 UTF-8,覆盖模式)。
  • writeString(Path, CharSequence, Charset, OpenOption...):指定字符集、指定打开选项(如追加模式)写入文件。
java 复制代码
import java.nio.file.*; 
import java.nio.charset.StandardCharsets; 
import java.io.IOException; 

public class FilesDemo { 
    public static void main(String[] args) throws IOException { 
        Path path = Paths.get("test.txt"); 
        
        // 1. 一行读取文件为字符串(默认 UTF-8) 
        String content = Files.readString(path); 
        System.out.println(content); 
        
        // 2. 指定字符集读取 
        String utf16Content = Files.readString(path, StandardCharsets.UTF_16); 
        
        // 3. 一行写入字符串到文件(覆盖模式) 
        Files.writeString(path, "Hello, Java 11!", StandardCharsets.UTF_8); 
        
        // 4. 追加模式写入 
        Files.writeString(path, "追加内容\n", StandardCharsets.UTF_8, StandardOpenOption.APPEND); 
    } 
}

注意:

  • readString() 会将整个文件加载到内存中,不适合处理超大文件。如果文件可能超过 2GB,应使用流式处理方式
4. Optional 类增强

新增了 isEmpty() 方法,提供了与 isPresent() 语义相反的直观判断方式,使空值检查的代码意图更加清晰。

scss 复制代码
Optional<String> emptyOpt = Optional.empty();
Optional<String> nonEmptyOpt = Optional.of("Java 11");

// Java 11 之前:用 !isPresent() 判断为空
if (!emptyOpt.isPresent()) {
    System.out.println("值为空");
}

// Java 11 之后:用 isEmpty() 更直观
if (emptyOpt.isEmpty()) {
    System.out.println("值为空");
}

System.out.println(empty.isEmpty());    // true
System.out.println(nonEmptyOpt.isEmpty());  // false
5. 集合Collection 接口增强

新增了一个 toArray(IntFunction<T[]> generator) 默认方法,允许通过方法引用将集合转换为指定类型的数组,替代了旧版本中繁琐的 toArray(new T[0]) 写法。

ini 复制代码
import java.util.List; 
import java.util.stream.Collectors; 
import java.util.stream.Stream; 

public class CollectionDemo { 
    public static void main(String[] args) { 
        List<String> list = List.of("Java", "Python", "Go"); 
        
        // Java 11 之前:需要传入一个预分配的数组
        String[] array1 = list.toArray(new String[0]);
        String[] array2 = list.toArray(new String[list.size()]); 
        
        // Java 11 之后:使用方法引用,更简洁且性能更优
        String[] array3 = list.toArray(String[]::new);
        // 或者
        String[] array4 = list.toArray(size -> new String[size]);

        
        // 嵌套集合场景下优势更明显
        List<List<String>> nested = List.of(
            List.of("A", "B"),
            List.of("C", "D")
        );
        
        // 旧写法需要推断二维数组类型,比较繁琐
        // 新写法简洁明了
        String[][] matrix = nested.stream()
            .map(inner -> inner.toArray(String[]::new))
            .toArray(String[][]::new);
            
        System.out.println(Arrays.deepToString(matrix));
        // 输出: [[A, B], [C, D]]
    }
}
6. Predicate 接口增强

新增了静态方法 not(),用于对给定的 Predicate 取反,同时保留方法引用的可读性。在 Java 11 之前,如果想对一个方法引用取反,必须写成 Lambda 表达式(如 person -> !person.isAdult()),无法直接对方法引用使用 .negate()。Java 11 的 Predicate.not() 解决了这个问题。

arduino 复制代码
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class PredicateEnhancements {
    public static void main(String[] args) {
        List<String> items = Arrays.asList("Java", "", "Kotlin", "  ", "Scala", "");

        // ❌ Java 11 之前:无法直接对方法引用取反
        // long nonEmpty = items.stream()
        //         .filter(String::isEmpty)  // 只能筛选空的
        //         .count();
        // 使用 Lambda 取反(可读性较差)
        long nonEmpty1 = items.stream()
                .filter(s -> !s.isEmpty())
                .count();

        // 场景1:过滤非空字符串
        // ✅ Java 11:使用 Predicate.not() 对方法引用取反
        long nonEmpty2 = items.stream()
                .filter(Predicate.not(String::isEmpty))
                .count();
        System.out.println("非空字符串数量: " + nonEmpty2); // 3

        // 结合 isBlank 使用
        List<String> meaningful = items.stream()
                .filter(Predicate.not(String::isBlank))
                .collect(Collectors.toList());
        System.out.println(meaningful); // [Java, Kotlin, Scala]

        // 场景2:静态导入后更简洁
        import static java.util.function.Predicate.not;
        List<String> result = items.stream()
                .filter(not(String::isEmpty))
                .collect(Collectors.toList());
        
        // 场景3:与其他默认方法组合使用
        Predicate<String> isJava = "Java"::equals;
        Predicate<String> isNotJava = Predicate.not(isJava);
        System.out.println(isNotJava.test("Python")); // true
    }
}

三、工具与底层运行

1. 基于嵌套的访问控制(Nest-Based Access Control)

让 JVM 层面的访问控制规则与 Java 语言层面的语义保持一致,消除编译器生成桥接方法的需要。

什么是桥接方法?

Java 语言从 1.1 版本开始就支持嵌套类(内部类),允许内部类直接访问外部类的私有成员。例如:

csharp 复制代码
public class Outer {
    private void outerPrivate() { }
    
    class Inner {
        public void innerPublic() {
            outerPrivate(); // 源码层面合法:Inner 可以访问 Outer 的 private 方法
        }
    }
}

但从 JVM 的角度来看,每个嵌套类最终都会被编译成独立的 .class 文件,而 JVM 的访问控制规则规定:不同类之间不能直接访问彼此的私有成员(Outer 和 Outer$Inner 会被编译成两个独立的 .class 文件,JVM 视它们为两个不同的类)。

为了让代码能够正常运行,Java 编译器(javac)在 Java 11 之前采用了一种"变通方案"------------隐式生成包级可见的桥接方法(如 access$000),由这些桥接方法代为访问私有成员。例如:

scss 复制代码
// javac 为 Outer 生成的合成桥接方法(示意)
static void access$000(Outer outer) {
    outer.outerPrivate(); // 桥接方法调用 private 方法
}
// Inner 中实际调用的是:Outer.access$000(outer)

桥接方法带来三个问题:

  • 字节码膨胀:每个被跨类访问的 private 成员都会生成额外的桥接方法;
  • 工具干扰:调试器、反射工具和字节码分析工具会看到这些本不应存在的合成方法;
  • 反射行为不一致:源码层面合法的 private 访问,通过反射调用时却会抛出 IllegalAccessException。

解决方案:Nest(嵌套)的概念

  • Nest:一个顶层类及其内部所有嵌套类型共同组成的访问控制上下文。
  • NestHost(嵌套宿主):通常是顶层类,作为 Nest 的标识。
  • Nestmate(嵌套伙伴):同一个 Nest 中的所有类型互为 Nestmate。

关键规则:同一个 Nest 中的任意两个 Nestmate,可以直接访问对方的 private 成员,无需桥接方法。

java.lang.Class 类新增了三个 API 来支持嵌套关系的查询:

scss 复制代码
// 获取当前类的嵌套宿主 
Class<?> getNestHost() 

// 获取当前嵌套中的所有成员类 
Class<?>[] getNestMembers() 

// 判断两个类是否属于同一个嵌套 
boolean isNestmateOf(Class<?> other)

代码示例:

csharp 复制代码
public class Outer {
    private int outerInt = 10;
    private void outerPrivate() {
        System.out.println("Outer.outerPrivate() 被调用");
    }

    class Inner {
        public void callOuterPrivate() {
            outerPrivate(); // Java 11:直接调用,不再生成桥接方法
            System.out.println("outerInt = " + outerInt);
        }
    }

    public static void main(String[] args) throws Exception {
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();

        // 1. 源码层面的 private 访问
        inner.callOuterPrivate();

        // 2. 反射层面的 private 访问(Java 11 之前会抛 IllegalAccessException)
        // Java 11 中正常执行
        Method method = Outer.class.getDeclaredMethod("outerPrivate");
        method.invoke(outer);

        // 3. 查询 Nest 关系
        System.out.println("Inner 的 Nest Host: " + Inner.class.getNestHost().getSimpleName());
        System.out.println("Outer 的 Nest Members: ");
        for (Class<?> c : Outer.class.getNestMembers()) {
            System.out.println("  " + c.getName());
        }
        System.out.println("Inner 是否为 Outer 的 Nestmate: " + Inner.class.isNestmateOf(Outer.class));
    }
}

注意事项:

  • 仅限同一 Nest:Nest 关系由编译器在编译时确定,仅限于同一顶层类下的嵌套类型。不同顶层类之间即使有继承或包关系,也不构成 Nest;
  • 隐式 Nest:如果一个类既没有 NestHost 也没有 NestMembers 属性,则它隐式地以自身为 NestHost,且只有自己一个成员;
2. JFR 开源

Java Flight Recorder(飞行记录器)原本是 Oracle JDK 的商业付费特性,自 Java 11 起正式开源并免费开放,成为强大的生产环境性能诊断工具。

JFR 由两个模块组成:

  • jdk.jfr:核心 API 和内部实现,仅依赖 java.base;
  • jdk.management.jfr:提供 JMX 接口,依赖 jdk.jfr 和 jdk.management。

JFR 可以通过命令行启动:

ini 复制代码
# 启动应用并记录60秒的数据到 recording.jfr 文件
java -XX:StartFlightRecording=duration=60s,filename=recording.jfr -jar myapp.jar

# 1. 开始记录
jcmd <pid> JFR.start name=MyRecording duration=60s

# 2. 查看记录状态
jcmd <pid> JFR.check

# 3. 将记录数据导出到文件
jcmd <pid> JFR.dump name=MyRecording filename=recording.jfr

# 4. 停止记录
jcmd <pid> JFR.stop name=MyRecording
3. ZGC 与 Epsilon GC 引入

Java 11 引入了实验性的 ZGC(低延迟垃圾收集器,暂停时间不超过 10ms)和 Epsilon GC(无操作垃圾收集器,仅分配内存不实现任何内存回收机制,用于性能测试),两款全新的垃圾收集器,分别针对超低延迟和零 GC 开销两个极端场景。

(1)ZGC(Z Garbage Collector)核心技术:

  • 染色指针(Colored Pointers):利用 64 位指针的高位存储对象状态标记,无需额外的内存开销。
  • 读屏障(Load Barrier):在应用线程读取对象引用时,极低成本地检查并修正指针,实现并发重定位。
  • 多映射内存(Multi-Mapping):通过虚拟内存映射,让 GC 并发移动对象而不影响应用线程。

ZGC(Z Garbage Collector)启用方式:

ruby 复制代码
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -jar myapp.jar

适用场景:对响应延迟极其敏感的大内存应用,如实时交易系统、大型微服务等。

(2)Epsilon GC 是一款"No-Op"(无操作)垃圾收集器。它只负责内存分配,完全不执行任何垃圾回收。当堆内存耗尽时,JVM 直接抛出 OutOfMemoryError 并退出。

Epsilon GC启用方式:

ruby 复制代码
# 限制堆大小为 100MB,使用 Epsilon GC 
java -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xmx100m -jar myapp.jar

适用场景:

  • 性能基准测试:排除 GC 带来的干扰,纯粹测试应用的内存分配速率和吞吐量。
  • 内存压力测试:精确测定应用所需的最低内存阈值。
  • 极短生命周期任务:任务执行时间极短,还没来得及产生垃圾就退出了,GC 纯属浪费资源。
4. 安全与加密增强

(1)实现了 TLS 1.3 协议,提供更强的安全性和更快的握手速度,提升了网络通信的安全性。TLS 1.3 引入了新的标准算法名称:

  • 协议版本名:TLSv1.3
  • 密码套件:TLS_AES_128_GCM_SHA256、TLS_AES_256_GCM_SHA384

(2)引入 ChaCha20 & Poly1305 加密算法以及 Curve25519/Curve448 密钥协商协议。

  • ChaCha20 & Poly1305:实现 ChaCha20 流加密算法和 Poly1305 MAC 算法,适用于移动设备等场景;
  • Curve25519 / Curve448:支持新的椭圆曲线密钥协商协议,提供更强的安全性。
5. 单文件源代码程序启动

Java 11 允许直接使用 java 命令运行单个 .java 源文件,无需先显式调用 javac 进行编译。在 Java 11 之前,运行一个 Java 程序需要先 javac 编译生成 .class 文件,再 java 运行。

增强 java 启动器,包括在脚本中使用 shebang 文件和相关技术。

bash 复制代码
# Java 11 之前:需要两步 
# javac HelloWorld.java 
# java HelloWorld 

# Java 11 及之后:一步到位 
java HelloWorld.java 

Shebang 脚本模式(Linux/macOS):

csharp 复制代码
#!/usr/bin/java --source 11 
public class ScriptDemo { 
    public static void main(String[] args) { 
        System.out.println("我是一个 Java 脚本!"); 
    } 
}

# 赋予执行权限
chmod +x ScriptDemo.java

# 直接像脚本一样运行
./ScriptDemo.java

# 输出: 我是一个 Java 脚本!

使用限制:

  • 所有相关的类必须定义在同一个 .java 文件中,不能依赖外部其他源文件。
  • 如果引用了第三方库(非 JDK 自带类),需要通过 -cp 指定 classpath。
  • 该特性主要适用于快速测试和小型脚本,企业级项目仍建议使用标准的编译打包流程。
6. 移除与弃用
  • 移除了 Java EE 和 CORBA 模块;
  • 移除了 Applet API 和 Web Start 支持;
  • 移除了 JavaFX;
  • 弃用了 Nashorn JavaScript 引擎 和 Pack200 工具。

参考文献:

  1. openjdk.org/projects/jd...
  2. www.oracle.com/java/techno...
相关推荐
唐青枫1 小时前
Java ThreadLocal 详解:从线程隔离到线程池实战,彻底避开脏数据与内存泄漏
java
用户3721574261351 小时前
如何使用 Java 创建 Excel 数据透视表
java
用户3126874877201 小时前
JVM 内存结构全链路:堆、栈、方法区、元空间
java
IT枫斗者枫哥1 小时前
Spring Boot导入返回409,为什么第一行还是入库了?
java·spring boot·后端
xiaoqiMikko1 小时前
JVM 线上排查实战(五):升 JDK 17 后进程起不来,十几个老 GC 参数挨个实测(完结)
java·jvm
Flynt1 小时前
Java 27 悄悄改了 3 个默认值,我在 1 核小机器上逐个验证了一遍
java·jvm·性能优化
天天被压力1 小时前
【Python 量化取数指南 #09】Python 拿港股通数据:港股通成交与港股财报实测
java·人工智能·python
吃饱了得干活1 小时前
数据放哪儿:从 HashMap 到一致性哈希
java·后端
SL_staff1 小时前
四维穿透式齐套判断:JVS-APS在制造计划系统中的技术实现
java·spring boot·python