Java并发编程高级技巧:CompletableFuture异步编程与线程池优化实战

Yvonne31
Yvonne31 2026-02-03T14:13:09+08:00
0 0 2

引言

在现代Java应用开发中,高并发和高性能是系统设计的核心要求。随着业务复杂度的增加,传统的同步编程方式已经无法满足日益增长的性能需求。Java 8引入的CompletableFuture作为异步编程的重要工具,为开发者提供了强大的异步处理能力。同时,合理的线程池配置对于系统性能优化至关重要。

本文将深入探讨CompletableFuture的高级使用技巧,包括异步任务处理、组合操作、异常处理等核心特性,并结合实际场景分析线程池参数调优的最佳实践,帮助开发者构建高性能、高并发的Java应用系统。

CompletableFuture基础概念与核心特性

什么是CompletableFuture

CompletableFuture是Java 8引入的异步编程工具类,实现了Future接口和CompletionStage接口。它提供了一种优雅的方式来处理异步操作,支持链式调用、组合操作和异常处理等高级特性。

CompletableFuture的核心优势在于:

  • 非阻塞式执行:异步任务不会阻塞主线程
  • 链式编程:支持方法链式调用,代码可读性强
  • 组合能力:可以轻松组合多个异步任务
  • 异常处理:完善的异常处理机制

CompletableFuture的主要方法分类

CompletableFuture提供了丰富的API,主要分为以下几类:

// 异步执行方法
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
public static void runAsync(Runnable runnable)
public static void runAsync(Runnable runnable, Executor executor)

// 转换和组合方法
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenRun(Runnable action)

// 组合操作
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
public <U> CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
public CompletableFuture<T> applyToEither(CompletionStage<? extends T> other, Function<? super T, T> fn)

// 异常处理
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)

CompletableFuture异步编程实战

基础异步任务执行

让我们从一个简单的异步任务开始:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class CompletableFutureExample {
    public static void main(String[] args) throws Exception {
        // 异步执行无返回值的任务
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            System.out.println("任务1开始执行,线程: " + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("任务1执行完成");
        });
        
        // 异步执行有返回值的任务
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2开始执行,线程: " + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return "任务2的结果";
        });
        
        // 等待所有任务完成
        CompletableFuture<Void> allFuture = CompletableFuture.allOf(future1, future2);
        allFuture.join();
        
        System.out.println("所有任务执行完毕");
    }
}

链式调用与结果处理

CompletableFuture的链式调用是其核心特性之一:

public class ChainExample {
    public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("第一步:获取用户信息");
            return "张三";
        })
        .thenApply(name -> {
            System.out.println("第二步:处理用户信息 - " + name);
            return name.toUpperCase();
        })
        .thenCompose(name -> {
            System.out.println("第三步:查询用户详情");
            return CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                return "用户:" + name + ",年龄:25岁";
            });
        })
        .thenApply(detail -> {
            System.out.println("第四步:格式化输出");
            return "详细信息:" + detail;
        });
        
        // 获取最终结果
        String result = future.join();
        System.out.println("最终结果:" + result);
    }
}

异常处理机制

CompletableFuture提供了多种异常处理方式:

public class ExceptionHandlingExample {
    public static void main(String[] args) {
        // 1. 使用exceptionally处理异常
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            if (Math.random() > 0.5) {
                throw new RuntimeException("随机异常");
            }
            return "成功获取数据";
        }).exceptionally(throwable -> {
            System.out.println("捕获到异常:" + throwable.getMessage());
            return "默认值";
        });
        
        // 2. 使用whenComplete处理异常
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            if (Math.random() > 0.5) {
                throw new RuntimeException("随机异常2");
            }
            return "成功获取数据2";
        }).whenComplete((result, throwable) -> {
            if (throwable != null) {
                System.out.println("whenComplete捕获到异常:" + throwable.getMessage());
            } else {
                System.out.println("whenComplete结果:" + result);
            }
        });
        
        // 3. 组合多个异步任务并处理异常
        CompletableFuture<String> combinedFuture = CompletableFuture
            .supplyAsync(() -> {
                try {
                    Thread.sleep(1000);
                    return "数据1";
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            })
            .thenCombine(
                CompletableFuture.supplyAsync(() -> {
                    try {
                        Thread.sleep(2000);
                        return "数据2";
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }),
                (data1, data2) -> data1 + " + " + data2
            )
            .exceptionally(throwable -> {
                System.out.println("组合任务异常:" + throwable.getMessage());
                return "默认组合结果";
            });
        
        System.out.println("最终结果:" + combinedFuture.join());
    }
}

高级异步编程模式

异步任务并行执行与结果聚合

在实际应用中,经常需要并行执行多个任务然后聚合结果:

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

public class ParallelProcessingExample {
    // 模拟耗时的业务操作
    private static String processUser(int userId) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return "用户" + userId + "的数据";
    }
    
    public static void main(String[] args) {
        // 方式1:使用CompletableFuture并行处理
        List<CompletableFuture<String>> futures = 
            java.util.stream.IntStream.rangeClosed(1, 5)
                .mapToObj(userId -> CompletableFuture.supplyAsync(() -> processUser(userId)))
                .collect(Collectors.toList());
        
        // 等待所有任务完成并收集结果
        CompletableFuture<Void> allDone = CompletableFuture.allOf(
            futures.toArray(new CompletableFuture[0])
        );
        
        List<String> results = futures.stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList());
        
        System.out.println("并行处理结果:" + results);
        
        // 方式2:使用自定义线程池
        ExecutorService executor = Executors.newFixedThreadPool(5);
        try {
            List<CompletableFuture<String>> futuresWithCustomPool = 
                java.util.stream.IntStream.rangeClosed(1, 5)
                    .mapToObj(userId -> CompletableFuture.supplyAsync(() -> processUser(userId), executor))
                    .collect(Collectors.toList());
            
            CompletableFuture<Void> allDone2 = CompletableFuture.allOf(
                futuresWithCustomPool.toArray(new CompletableFuture[0])
            );
            
            List<String> results2 = futuresWithCustomPool.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());
            
            System.out.println("自定义线程池处理结果:" + results2);
        } finally {
            executor.shutdown();
        }
    }
}

异步任务超时控制

在生产环境中,异步任务的超时控制非常重要:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class TimeoutExample {
    // 模拟耗时操作
    private static String longRunningTask() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return "长时间任务完成";
    }
    
    public static void main(String[] args) {
        // 方式1:使用timeout方法
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> longRunningTask())
            .orTimeout(2, TimeUnit.SECONDS)
            .exceptionally(throwable -> {
                System.out.println("任务超时或异常:" + throwable.getMessage());
                return "默认值";
            });
        
        try {
            String result = future.get(3, TimeUnit.SECONDS);
            System.out.println("结果:" + result);
        } catch (Exception e) {
            System.out.println("获取结果时发生异常:" + e.getMessage());
        }
        
        // 方式2:手动实现超时控制
        CompletableFuture<String> manualTimeoutFuture = CompletableFuture.supplyAsync(() -> {
            try {
                return longRunningTask();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        
        CompletableFuture<String> timeoutFuture = CompletableFuture
            .supplyAsync(() -> {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                return "超时结果";
            })
            .applyToEither(manualTimeoutFuture, result -> result);
        
        String manualResult = timeoutFuture.join();
        System.out.println("手动超时控制结果:" + manualResult);
    }
}

线程池优化与配置

线程池参数详解

线程池的合理配置对系统性能至关重要。主要参数包括:

import java.util.concurrent.*;

public class ThreadPoolConfiguration {
    public static void main(String[] args) {
        // 1. 核心线程数:线程池创建时的线程数量
        int corePoolSize = 4;
        
        // 2. 最大线程数:线程池允许的最大线程数量
        int maximumPoolSize = 8;
        
        // 3. 空闲时间:超过核心线程数的线程在空闲时等待任务的最大时间
        long keepAliveTime = 60L;
        
        // 4. 阻塞队列:用于存储等待执行的任务
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100);
        
        // 5. 线程工厂:创建新线程时使用的工厂
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        
        // 6. 拒绝策略:当任务无法被处理时的策略
        RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
        
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            corePoolSize,
            maximumPoolSize,
            keepAliveTime,
            TimeUnit.SECONDS,
            workQueue,
            threadFactory,
            handler
        );
        
        // 配置建议
        System.out.println("线程池配置建议:");
        System.out.println("- 核心线程数:CPU核心数 + 1");
        System.out.println("- 最大线程数:CPU核心数 * 2");
        System.out.println("- 阻塞队列大小:根据任务特性调整");
        System.out.println("- 拒绝策略:根据业务需求选择合适的策略");
    }
}

不同场景下的线程池配置

import java.util.concurrent.*;

public class ThreadPoolScenarios {
    
    // CPU密集型任务线程池配置
    public static ExecutorService cpuIntensivePool() {
        int processors = Runtime.getRuntime().availableProcessors();
        return new ThreadPoolExecutor(
            processors,
            processors * 2,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }
    
    // IO密集型任务线程池配置
    public static ExecutorService ioIntensivePool() {
        int processors = Runtime.getRuntime().availableProcessors();
        return new ThreadPoolExecutor(
            processors * 2,
            processors * 4,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }
    
    // 混合型任务线程池配置
    public static ExecutorService mixedPool() {
        return new ThreadPoolExecutor(
            8,
            16,
            60L, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(1000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }
    
    // 异步任务执行示例
    public static void executeTasks() {
        ExecutorService cpuPool = cpuIntensivePool();
        ExecutorService ioPool = ioIntensivePool();
        
        // CPU密集型任务
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            cpuPool.submit(() -> {
                System.out.println("CPU任务 " + taskId + " 执行中");
                // 模拟CPU密集型计算
                long sum = 0;
                for (int j = 0; j < 1000000; j++) {
                    sum += j;
                }
                System.out.println("CPU任务 " + taskId + " 完成,结果:" + sum);
            });
        }
        
        // IO密集型任务
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            ioPool.submit(() -> {
                System.out.println("IO任务 " + taskId + " 执行中");
                try {
                    Thread.sleep(1000); // 模拟IO等待
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("IO任务 " + taskId + " 完成");
            });
        }
        
        cpuPool.shutdown();
        ioPool.shutdown();
    }
    
    public static void main(String[] args) {
        executeTasks();
    }
}

线程池监控与性能调优

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

public class ThreadPoolMonitor {
    private static final AtomicLong taskCount = new AtomicLong(0);
    
    public static void monitorThreadPool(ThreadPoolExecutor executor) {
        ScheduledExecutorService monitor = Executors.newScheduledThreadPool(1);
        
        monitor.scheduleAtFixedRate(() -> {
            System.out.println("=== 线程池监控信息 ===");
            System.out.println("核心线程数:" + executor.getCorePoolSize());
            System.out.println("当前线程数:" + executor.getPoolSize());
            System.out.println("活动线程数:" + executor.getActiveCount());
            System.out.println("已完成任务数:" + executor.getCompletedTaskCount());
            System.out.println("总任务数:" + executor.getTaskCount());
            System.out.println("队列大小:" + executor.getQueue().size());
            System.out.println("最大线程数:" + executor.getMaximumPoolSize());
            System.out.println("=====================");
        }, 0, 5, TimeUnit.SECONDS);
    }
    
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            4,
            8,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy()
        );
        
        // 启动监控
        monitorThreadPool(executor);
        
        // 提交测试任务
        for (int i = 0; i < 20; i++) {
            final int taskId = i;
            executor.submit(() -> {
                try {
                    System.out.println("任务 " + taskId + " 开始执行");
                    Thread.sleep(2000);
                    System.out.println("任务 " + taskId + " 执行完成");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
        
        // 保持程序运行
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

CompletableFuture与线程池结合实战

自定义线程池配置

import java.util.concurrent.*;

public class CustomThreadPoolExample {
    private static final ExecutorService CUSTOM_EXECUTOR = 
        new ThreadPoolExecutor(
            4,                    // 核心线程数
            8,                    // 最大线程数
            60L, TimeUnit.SECONDS, // 空闲时间
            new LinkedBlockingQueue<>(100), // 阻塞队列
            Executors.defaultThreadFactory(), // 线程工厂
            new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
        );
    
    public static void main(String[] args) {
        // 使用自定义线程池执行CompletableFuture任务
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("异步任务在自定义线程池中执行");
            return "处理结果";
        }, CUSTOM_EXECUTOR);
        
        String result = future.join();
        System.out.println("最终结果:" + result);
        
        // 关闭线程池
        CUSTOM_EXECUTOR.shutdown();
    }
}

实际业务场景应用

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class BusinessScenarioExample {
    private static final ExecutorService BUSINESS_EXECUTOR = 
        Executors.newFixedThreadPool(10);
    
    // 模拟业务服务
    public static CompletableFuture<String> getUserInfo(int userId) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(500); // 模拟数据库查询
                return "用户" + userId + "的详细信息";
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException(e);
            }
        }, BUSINESS_EXECUTOR);
    }
    
    public static CompletableFuture<String> getOrders(int userId) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(800); // 模拟订单查询
                return "用户" + userId + "的订单列表";
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException(e);
            }
        }, BUSINESS_EXECUTOR);
    }
    
    public static CompletableFuture<String> getPreferences(int userId) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(300); // 模拟偏好设置查询
                return "用户" + userId + "的偏好设置";
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException(e);
            }
        }, BUSINESS_EXECUTOR);
    }
    
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        
        // 并行获取用户信息、订单和偏好设置
        CompletableFuture<String> userInfoFuture = getUserInfo(12345);
        CompletableFuture<String> ordersFuture = getOrders(12345);
        CompletableFuture<String> preferencesFuture = getPreferences(12345);
        
        // 组合所有结果
        CompletableFuture<String> combinedResult = CompletableFuture.allOf(
                userInfoFuture, ordersFuture, preferencesFuture)
            .thenApply(v -> {
                try {
                    String userInfo = userInfoFuture.get();
                    String orders = ordersFuture.get();
                    String preferences = preferencesFuture.get();
                    return "组合结果:\n" + userInfo + "\n" + orders + "\n" + preferences;
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
        
        String result = combinedResult.join();
        long endTime = System.currentTimeMillis();
        
        System.out.println(result);
        System.out.println("总耗时:" + (endTime - startTime) + "ms");
        
        BUSINESS_EXECUTOR.shutdown();
    }
}

性能优化最佳实践

任务拆分与并行化策略

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.IntStream;

public class PerformanceOptimization {
    private static final ExecutorService OPTIMIZED_EXECUTOR = 
        new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors() * 2,
            Runtime.getRuntime().availableProcessors() * 4,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy()
        );
    
    // 大数据量处理优化
    public static void processDataOptimization(int[] data) {
        int chunkSize = Math.max(1, data.length / 10); // 分块大小
        
        List<CompletableFuture<Integer>> futures = new ArrayList<>();
        
        for (int i = 0; i < data.length; i += chunkSize) {
            final int start = i;
            final int end = Math.min(i + chunkSize, data.length);
            
            CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
                int sum = 0;
                for (int j = start; j < end; j++) {
                    sum += data[j];
                }
                return sum;
            }, OPTIMIZED_EXECUTOR);
            
            futures.add(future);
        }
        
        // 等待所有分块任务完成并聚合结果
        CompletableFuture<Integer> result = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0]))
            .thenApply(v -> futures.stream()
                .mapToInt(CompletableFuture::join)
                .sum());
        
        System.out.println("计算结果:" + result.join());
    }
    
    public static void main(String[] args) {
        // 创建测试数据
        int[] testData = IntStream.range(0, 10000).toArray();
        
        long startTime = System.currentTimeMillis();
        processDataOptimization(testData);
        long endTime = System.currentTimeMillis();
        
        System.out.println("优化处理耗时:" + (endTime - startTime) + "ms");
    }
}

内存管理与资源回收

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MemoryManagement {
    private static final ExecutorService MEMORY_SAFE_EXECUTOR = 
        Executors.newFixedThreadPool(4);
    
    // 使用WeakReference避免内存泄漏
    public static void safeAsyncProcessing() {
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            // 执行一些处理
            System.out.println("任务1执行");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, MEMORY_SAFE_EXECUTOR);
        
        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
            // 执行一些处理
            System.out.println("任务2执行");
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, MEMORY_SAFE_EXECUTOR);
        
        // 等待所有任务完成
        CompletableFuture<Void> allDone = CompletableFuture.allOf(future1, future2);
        
        allDone.thenRun(() -> {
            System.out.println("所有任务完成,准备清理资源");
            // 在这里可以执行清理工作
        });
        
        try {
            allDone.join();
        } catch (Exception e) {
            System.err.println("任务执行异常:" + e.getMessage());
        }
    }
    
    public static void main(String[] args) {
        safeAsyncProcessing();
        
        // 优雅关闭线程池
        MEMORY_SAFE_EXECUTOR.shutdown();
        try {
            if (!MEMORY_SAFE_EXECUTOR.awaitTermination(5, TimeUnit.SECONDS)) {
                MEMORY_SAFE_EXECUTOR.shutdownNow();
            }
        } catch (InterruptedException e) {
            MEMORY_SAFE_EXECUTOR.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

总结与展望

CompletableFuture作为Java 8异步编程的核心工具,为开发者提供了强大的异步处理能力。通过合理使用其链式调用、组合操作和异常处理机制,我们可以构建出高性能、高并发的应用程序。

在实际应用中,线程池的配置和优化同样重要。合理的线程池参数设置能够有效提升系统性能,避免资源浪费和系统过载。同时,结合具体的业务场景选择合适的异步模式和执行策略,是实现高性能系统的关键。

未来随着Java版本的演进,CompletableFuture的功能会不断完善,我们还需要持续关注新的特性和最佳实践。在高并发、大数据量处理的场景下,异步编程将成为构建高性能应用的必备技能。

通过本文的介绍和示例,希望读者能够深入理解CompletableFuture的核心概念和高级用法,并能够在实际项目中灵活运用这些技术来提升系统性能和用户体验。记住,在使用异步编程时要平衡好并发度、资源消耗和系统稳定性之间的关系,这是构建高质量Java应用的重要原则。

相关推荐
广告位招租

相似文章

    评论 (0)

    0/2000