深入了解Java中的Lambda表达式

发布时间: 2024-01-21 01:07:12 阅读量: 50 订阅数: 39
PDF

深入理解Java中的Lambda表达式

目录
解锁专栏,查看完整目录

1. 什么是Lambda表达式

1.1 Lambda表达式的定义

Lambda表达式是一种匿名函数,可以像数据一样传递,可用来替代复杂的匿名内部类。

1.2 Lambda表达式的语法

Lambda表达式的语法由参数列表、箭头符号和函数体组成,如下所示:

  1. (parameter1, parameter2) -> expression

或者

  1. (parameter1, parameter2) -> { code block }

1.3 Lambda表达式的优势

  • 简洁:去掉样板代码,只留下核心逻辑,使代码更加精炼。
  • 高效:可以便捷地进行并行计算和多线程处理。
  • 便利:使代码更易读、易写、易维护。

2. Lambda表达式的基本用法

Lambda表达式是Java 8引入的一项重大特性,它使得代码变得更加简洁和易读。Lambda表达式允许我们以更简洁的方式定义和使用匿名函数,从而使得我们可以将代码逻辑作为参数传递给方法或者函数,使得代码更加灵活和可复用。

2.1 使用Lambda表达式实现接口的匿名内部类

Lambda表达式最常用的场景就是替代接口的匿名内部类,我们可以使用Lambda表达式来创建接口对象,并且实现接口中的抽象方法。下面是一个简单的例子:

  1. public class LambdaDemo {
  2. public static void main(String[] args) {
  3. // 使用Lambda表达式实现Runnable接口的匿名内部类
  4. Runnable runnable = () -> {
  5. for (int i = 0; i < 5; i++) {
  6. System.out.println("Hello, Lambda!");
  7. }
  8. };
  9. // 创建线程并启动
  10. Thread thread = new Thread(runnable);
  11. thread.start();
  12. }
  13. }

上面的代码使用Lambda表达式实现了Runnable接口的匿名内部类,可以看到Lambda表达式写法非常简洁。代码中创建了一个Runnable接口的实例,并实现了其中的run方法。然后使用该实例创建了一个线程并启动,线程运行时会执行Lambda表达式中的代码。

2.2 Lambda表达式的参数列表和返回值

Lambda表达式的参数列表指的是在小括号内声明的参数,而返回值则是Lambda表达式的返回结果。对于只有一个参数的Lambda表达式,可以省略小括号;对于没有参数的Lambda表达式,小括号也可以省略。下面是一些示例:

  • 不带参数的Lambda表达式:
  1. // Lambda表达式没有参数
  2. Runnable runnable = () -> {
  3. System.out.println("Hello, Lambda!");
  4. };
  • 带一个参数的Lambda表达式:
  1. // Lambda表达式带一个参数(可以省略小括号)
  2. Consumer<String> consumer = s -> {
  3. System.out.println("Hello, " + s);
  4. };
  • 带多个参数的Lambda表达式:
  1. // Lambda表达式带多个参数
  2. BinaryOperator<Integer> add = (a, b) -> {
  3. return a + b;
  4. };
  • 带参数并且有返回值的Lambda表达式:
  1. // Lambda表达式带参数并且有返回值(可以省略return和大括号)
  2. Function<Integer, String> convert = num -> num.toString();

2.3 Lambda表达式的方法引用

除了直接使用Lambda表达式来实现接口的匿名内部类之外,还可以使用方法引用来简化代码。方法引用是Lambda表达式的一种缩写形式,它可以直接引用已经定义好的方法,从而使得代码变得更加简洁和易读。

在Java中,方法引用可以分为以下几种形式:

  • 静态方法引用:
  1. // 引用String类的静态方法valueOf
  2. Function<Integer, String> converter = String::valueOf;
  • 实例方法引用:
  1. // 引用线程对象的start方法
  2. Thread thread = new Thread(runnable::start);
  • 对象方法引用:
  1. // 引用字符串对象的toUpperCase方法
  2. Function<String, String> func = String::toUpperCase;
  • 构造方法引用:
  1. // 引用ArrayList的构造方法
  2. Supplier<List<Integer>> supplier = ArrayList::new;

方法引用能够使得代码更加简洁和可读,同时也能提高代码的可维护性和可复用性。在实际开发中,我们应该根据具体情况选择使用Lambda表达式还是方法引用。

通过以上介绍,我们了解了Lambda表达式的基本用法,包括使用Lambda表达式实现接口的匿名内部类、Lambda表达式的参数列表和返回值、以及Lambda表达式的方法引用。在接下来的章节中,我们将介绍Lambda表达式在集合框架中的应用。

3. Lambda表达式在集合框架中的应用

Lambda表达式在集合框架中的应用非常广泛,可以简化代码并提高代码的可读性和可维护性。下面将介绍Lambda表达式在集合框架中的几种常见应用:

3.1 使用Lambda表达式对集合进行迭代和过滤

Lambda表达式可以利用Stream API对集合进行迭代和过滤操作。通过使用Lambda表达式,我们可以通过一种更简洁和易于理解的方式来处理集合中的元素。

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. public class LambdaDemo {
  4. public static void main(String[] args) {
  5. List<Integer> numbers = new ArrayList<>();
  6. numbers.add(1);
  7. numbers.add(2);
  8. numbers.add(3);
  9. numbers.add(4);
  10. numbers.add(5);
  11. // 使用Lambda表达式遍历集合
  12. numbers.forEach(number -> System.out.println(number));
  13. // 或者使用方法引用
  14. numbers.forEach(System.out::println);
  15. // 使用Lambda表达式过滤集合中的元素
  16. List<Integer> evenNumbers = numbers.stream()
  17. .filter(number -> number % 2 == 0)
  18. .collect(Collectors.toList());
  19. System.out.println(evenNumbers);
  20. }
  21. }

代码解释:

首先,我们创建了一个ArrayList对象并向其添加了一些整数。然后,我们使用Lambda表达式通过forEach方法遍历集合并打印出每个元素的值。可以看到,Lambda表达式可以作为参数传递给forEach方法,并且在每次遍历时被调用。

接下来,我们使用Lambda表达式对集合进行过滤,保留只有偶数的元素。通过调用stream方法获取集合的流,然后使用filter方法选择满足条件的元素,最后使用collect方法将过滤后的元素收集到一个新的List中。运行上述代码,将会输出:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. [2, 4]

3.2 使用Lambda表达式对集合进行排序

Lambda表达式还可以轻松实现对集合进行排序的功能,使代码更加简洁和可读。

  1. import java.util.ArrayList;
  2. import java.util.Comparator;
  3. import java.util.List;
  4. public class LambdaDemo {
  5. public static void main(String[] args) {
  6. List<Integer> numbers = new ArrayList<>();
  7. numbers.add(5);
  8. numbers.add(2);
  9. numbers.add(1);
  10. numbers.add(4);
  11. numbers.add(3);
  12. // 使用Lambda表达式对集合进行排序
  13. numbers.sort((a, b) -> a.compareTo(b));
  14. System.out.println(numbers);
  15. // 使用Comparator的Lambda表达式进行排序
  16. numbers.sort(Comparator.reverseOrder());
  17. System.out.println(numbers);
  18. }
  19. }

代码解释:

首先,我们创建了一个ArrayList对象并向其添加了一些整数。然后,我们使用Lambda表达式通过sort方法对集合进行排序。通过定义一个Lambda表达式作为比较器,我们可以指定排序的规则。在这个例子中,我们将使用整数的自然顺序进行排序。

接下来,我们使用Comparator的Lambda表达式对集合进行逆序排序。通过使用reverseOrder方法得到一个逆序的比较器,我们可以轻松地实现逆序排序。

运行上述代码,将会输出:

  1. [1, 2, 3, 4, 5]
  2. [5, 4, 3, 2, 1]

3.3 使用Lambda表达式实现自定义的集合操作

Lambda表达式可以与集合的其他方法结合使用,实现更复杂的集合操作。我们可以使用Lambda表达式来进行元素的查找、转换、分割等操作。

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import java.util.Optional;
  4. public class LambdaDemo {
  5. public static void main(String[] args) {
  6. List<Integer> numbers = new ArrayList<>();
  7. numbers.add(1);
  8. numbers.add(2);
  9. numbers.add(3);
  10. numbers.add(4);
  11. numbers.add(5);
  12. // 使用Lambda表达式查找集合中的最大值
  13. Optional<Integer> max = numbers.stream()
  14. .max((a, b) -> a.compareTo(b));
  15. if (max.isPresent()) {
  16. System.out.println("Max value: " + max.get());
  17. }
  18. // 使用Lambda表达式对集合中的每个元素进行转换
  19. List<String> strings = numbers.stream()
  20. .map(number -> "Number: " + number)
  21. .collect(Collectors.toList());
  22. System.out.println(strings);
  23. // 使用Lambda表达式对集合进行分割
  24. List<List<Integer>> chunks = new ArrayList<>();
  25. numbers.forEach(number -> {
  26. if (chunks.isEmpty() || chunks.get(chunks.size() - 1).size() >= 3) {
  27. chunks.add(new ArrayList<>());
  28. }
  29. chunks.get(chunks.size() - 1).add(number);
  30. });
  31. System.out.println(chunks);
  32. }
  33. }

代码解释:

首先,我们创建了一个ArrayList对象并向其添加了一些整数。然后,我们使用Lambda表达式通过stream方法获取集合的流,并通过max方法查找集合中的最大值。通过定义一个Lambda表达式作为比较器,我们可以指定在比较操作中使用的规则。

接下来,我们使用Lambda表达式对集合中的每个元素进行转换。通过使用map方法,我们可以将每个元素都转换为一个新的值,并收集到一个新的List中。

最后,我们使用Lambda表达式对集合进行分割。通过使用forEach方法,我们遍历集合的每个元素,并根据特定的条件来分割集合。在这个例子中,我们将集合分割成包含最多3个元素的子集合。

运行上述代码,将会输出:

  1. Max value: 5
  2. [Number: 1, Number: 2, Number: 3, Number: 4, Number: 5]
  3. [[1, 2, 3], [4, 5]]

以上是Lambda表达式在集合框架中的几种常见应用。通过使用Lambda表达式,我们可以以一种更加简洁和直观的方式处理集合,使代码更易读、易维护。

4. Lambda表达式与函数式接口

在本章中,我们将深入探讨Lambda表达式与函数式接口的相关内容。首先,我们会对函数式接口进行定义和解释,然后介绍如何使用Lambda表达式实现函数式接口,最后会讨论函数式接口常见的类型和用途。

4.1 函数式接口的定义

函数式接口(Functional Interface)是指内部只包含一个抽象方法的接口。基于Lambda表达式的语法特性,函数式接口可以通过Lambda表达式实例化。函数式接口的定义为了支持Lambda表达式的使用,使得在编写函数式代码时更加简洁、灵活。

考虑到Java 8之前的版本并没有直接支持函数式编程,函数式接口的引入使得Java能够更好地支持函数式编程范式,从而更好地满足当今编程领域的需求。

4.2 使用Lambda表达式实现函数式接口

下面我们通过一个简单的例子来说明如何使用Lambda表达式实现函数式接口。假设我们有一个简单的函数式接口 Calculator,其中包含一个抽象方法 int calculate(int a, int b),我们可以使用Lambda表达式来实现该接口。

  1. @FunctionalInterface
  2. interface Calculator {
  3. int calculate(int a, int b);
  4. }
  5. public class LambdaDemo {
  6. public static void main(String[] args) {
  7. // 使用Lambda表达式实现函数式接口
  8. Calculator addition = (a, b) -> a + b;
  9. System.out.println("Addition: " + addition.calculate(3, 5));
  10. Calculator subtraction = (a, b) -> a - b;
  11. System.out.println("Subtraction: " + subtraction.calculate(8, 4));
  12. }
  13. }

在上面的例子中,我们定义了一个名为 Calculator 的函数式接口,并使用Lambda表达式分别实现了加法和减法运算。这使得我们可以直接使用Lambda表达式来创建接口的实例,而无需显式地编写匿名内部类。

4.3 函数式接口的常见类型和用途

函数式接口有许多常见的类型和用途,例如 SupplierConsumerPredicateFunction 等,它们分别代表不同的函数式接口类型,并提供了丰富的工具类方法来支持Lambda表达式的使用。这些函数式接口在Java标准库中被广泛应用,在各种场景下都有着重要的作用。

通过使用这些函数式接口,我们可以更加简洁地实现一些常见的功能,例如数据的获取与处理、集合的筛选与转换等。在日常的Java编程中,函数式接口和Lambda表达式已经成为了不可或缺的工具,极大地提高了代码的可读性和灵活性。

在下一章节中,我们将探讨Lambda表达式的局限性和解决方案,以及Lambda表达式在并发编程中的应用。

5. 第五章 Lambda表达式的局限性与解决方案

Lambda表达式在Java编程中具有许多强大的特性,但同时也存在一些局限性。本章节将介绍Lambda表达式的局限性,并提供相应的解决方案。

5.1 Lambda表达式对闭包的支持

Lambda表达式支持闭包的概念,即它可以访问外部的变量。但是,Lambda表达式对闭包的支持是有限的,只能访问被final或实质上final修饰的局部变量和类的成员变量。下面的示例演示了Lambda表达式对闭包的局限性:

  1. public class LambdaClosureExample {
  2. private int outerVariable = 10;
  3. public void testClosure() {
  4. int localVariable = 20;
  5. Runnable runnable = () -> {
  6. // 可以访问外部的成员变量
  7. System.out.println("Outer Variable: " + outerVariable);
  8. // 可以访问被final或实质上final修饰的局部变量
  9. System.out.println("Local Variable: " + localVariable);
  10. };
  11. runnable.run();
  12. }
  13. }

以上代码中,Lambda表达式中可以访问外部类的成员变量outerVariable,以及被final修饰的局部变量localVariable。如果localVariable不被final或实质上final修饰,编译器将会报错。

5.2 Lambda表达式对异常的处理

Lambda表达式中的异常处理相对简单,只能在Lambda表达式内部进行try-catch处理,无法将异常传递给外部调用者。下面的示例演示了Lambda表达式对异常的处理:

  1. public class LambdaExceptionExample {
  2. public static void divideByZero() {
  3. IntBinaryOperator division = (a, b) -> {
  4. try {
  5. return a / b;
  6. } catch (ArithmeticException e) {
  7. System.err.println("Error: Division by zero");
  8. return 0;
  9. }
  10. };
  11. int result = division.applyAsInt(10, 0);
  12. System.out.println("Result: " + result);
  13. }
  14. }

以上代码中,Lambda表达式计算两个数相除的结果。在Lambda表达式内部进行了try-catch处理,捕获了除以零的算术异常,并返回了默认值0。

5.3 Lambda表达式对变量的访问

Lambda表达式对变量的访问还存在一些限制。与匿名内部类不同,Lambda表达式中的局部变量不能进行修改,它们相当于被隐式声明为final。下面的示例演示了Lambda表达式对变量的访问限制:

  1. public class LambdaVariableExample {
  2. public static void printNums() {
  3. int num = 10;
  4. Runnable runnable = () -> {
  5. // 编译错误:Local variable num defined in an enclosing scope must be final or effectively final
  6. num = 20;
  7. System.out.println(num);
  8. };
  9. runnable.run();
  10. }
  11. }

以上代码中,尝试在Lambda表达式中修改局部变量num的值,但编译器报错提示必须将该变量声明为final或实质上final。

总结

本章介绍了Lambda表达式的局限性以及相应的解决方案。我们学习了Lambda表达式对闭包的支持限制、异常处理以及变量的访问限制。通过合理的使用Lambda表达式和对局限性的解决方案,我们可以更好地利用Lambda表达式的优势,并避免一些潜在的问题。

6. Lambda表达式在并发编程中的应用

在并发编程中,Lambda表达式可以简化线程的创建和启动,实现并发任务的分割与合并,并结合使用并发集合。接下来将介绍Lambda表达式在并发编程中的应用技巧和注意事项。

  1. 使用Lambda表达式简化线程的创建和启动

    1. // 使用Lambda表达式创建和启动线程
    2. Thread thread = new Thread(() -> {
    3. // 线程执行的任务
    4. System.out.println("Hello from Lambda Thread!");
    5. });
    6. thread.start();

    通过Lambda表达式,可以直接在Thread构造函数中传入需要执行的任务,简化了匿名内部类的编写方式。

  2. 使用Lambda表达式实现并发任务的分割与合并

    1. // 使用Lambda表达式实现并发任务的分割与合并
    2. List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    3. int sum = numbers.parallelStream().mapToInt(i -> i).sum();
    4. System.out.println("Sum of numbers: " + sum);

    在上述代码中,通过parallelStream()方法并结合Lambda表达式,实现了将任务分割成多个子任务并行执行,然后合并计算结果。

  3. Lambda表达式和并发集合的结合使用

    1. // 使用Lambda表达式和并发集合
    2. ConcurrentMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
    3. concurrentMap.put("A", 1);
    4. concurrentMap.put("B", 2);
    5. concurrentMap.forEach((key, value) ->
    6. System.out.println("Key: " + key + ", Value: " + value)
    7. );

    通过Lambda表达式,可以直接在并发集合的forEach方法中传入需要执行的任务,简化了遍历操作的代码和逻辑。

通过以上方式,Lambda表达式在并发编程中简化了代码编写,并提高了代码的可读性和可维护性。

corwn 最低0.47元/天 解锁专栏
买1年送3月
点击查看下一篇
profit 百万级 高质量VIP文章无限畅学
profit 千万级 优质资源任意下载
profit C知道 免费提问 ( 生成式Al产品 )

相关推荐

SW_孙维

开发技术专家
知名科技公司工程师,开发技术领域拥有丰富的工作经验和专业知识。曾负责设计和开发多个复杂的软件系统,涉及到大规模数据处理、分布式系统和高性能计算等方面。
专栏简介
《Java高级编程架构》专栏深入探讨了Java语言高级编程技术,涵盖了异步编程、Lambda表达式、函数式编程、泛型编程、反射编程、注解应用、类加载器与动态代理、内存管理、性能调优、多线程编程、设计模式应用、集合框架操作、流式处理、序列化与反序列化、数据库连接池应用、RESTful架构设计以及Spring框架搭建企业级应用等内容。本专栏旨在帮助Java开发者深入理解各项高级编程技术,并掌握在实际项目中的应用技巧,从而提升编程水平和开发效率。无论您是初学者还是有一定经验的开发者,都能从中获得宝贵的经验和知识,为自己的Java编程之路增添新的技术解决方案。
最低0.47元/天 解锁专栏
买1年送3月
百万级 高质量VIP文章无限畅学
千万级 优质资源任意下载
C知道 免费提问 ( 生成式Al产品 )

最新推荐

【Fluent框架终极指南】:从业余到专家,12个案例揭秘性能提升与问题解决

![【Fluent框架终极指南】:从业余到专家,12个案例揭秘性能提升与问题解决](https://opengraph.githubassets.com/38d45438fbd12b69b2c8c043b1ae074222059c73d1dab2be9924b6c45ce52255/AliPooladmast/e-commerce-api) # 摘要 Fluent框架是广泛应用于流体动力学仿真领域的软件工具,本文全面介绍Fluent的基本理论、高级模拟技巧、案例分析以及性能调优和问题诊断。首先,本文介绍了Fluent框架的安装配置和基础理论,包括网格划分技术、物理模型和边界条件的设置,以及数

【Windows下的HID设备接入全面攻略】:从0到1实现高效通信

![【Windows下的HID设备接入全面攻略】:从0到1实现高效通信](http://helicaltech.com/wp-content/uploads/2017/04/b7.png) # 摘要 HID设备因其易用性和广泛的应用领域而成为计算机外设的标准选择。本文首先概述了HID设备及其在Windows系统中的工作原理,接着深入探讨了HID类设备的通信协议,包括协议规范、报告描述符解析以及数据传输机制。本文还详细介绍了如何搭建HID设备接入的开发环境,包括软件工具准备、编写通信程序和进行设备通信调试。此外,文章着重阐述了实现HID设备接入和数据交换的具体过程,包括设备的枚举、连接和数据交

Matlab HOSA函数全解析:每个函数背后的秘密及最佳实践

![Matlab HOSA函数全解析:每个函数背后的秘密及最佳实践](https://i0.hdslb.com/bfs/archive/e393ed87b10f9ae78435997437e40b0bf0326e7a.png@960w_540h_1c.webp) # 摘要 本文全面介绍Matlab中高阶谱分析(HOSA)函数的理论基础、使用方法和性能优化。首先概述HOSA函数的核心概念及其在信号处理中的理论基础,然后详细指导如何在实际操作中应用这些函数,包括基本使用、高级应用及问题解决实例。此外,还分析了HOSA函数在性能优化方面的瓶颈问题,提出相应的计算复杂度降低和内存使用优化策略。最后,

【PCB焊盘设计误区大揭秘】:避免这些致命错误来提高设计成功率

![【PCB焊盘设计误区大揭秘】:避免这些致命错误来提高设计成功率](https://www.pcba-manufacturers.com/wp-content/uploads/2022/10/PCB-routing-rules.jpg) # 摘要 PCB焊盘设计对于电路板的可靠性和性能至关重要,本论文首先强调了焊盘设计的重要性,并回顾了基础知识,包括设计原则、焊盘类型以及行业标准。文章接着剖析了设计中常见的误区,并对焊盘尺寸、热管理和布局的优化策略进行了讨论。通过案例分析,作者展示了焊盘设计优化前后的显著差异,并分享了成功的设计经验。最后,论文展望了焊盘设计的未来趋势,包括高密度互连技术和

服务发现机制详解:微服务架构中的关键通信技术

![服务发现机制详解:微服务架构中的关键通信技术](http://bootcamptoprod.com/wp-content/uploads/2024/02/Client-Side-vs-Server-Side-Load-Balancing.png) # 摘要 服务发现机制是微服务架构中的核心组成部分,它支持微服务间的动态集成与通信。本文概述了服务发现的理论基础,包括微服务架构原则及其优势与挑战,服务发现的重要作用,以及客户端与服务器端发现模式的分类。进一步,文章探讨了服务发现的关键技术实践,例如服务注册与健康检查、负载均衡策略以及网络配置和故障转移。通过介绍常见的服务发现工具如Eureka

MyEclipse汉化从入门到精通:新手必须掌握的详细步骤与注意事项

![MyEclipse汉化从入门到精通:新手必须掌握的详细步骤与注意事项](https://ucc.alicdn.com/pic/developer-ecology/ajpxbl4ljzs5k_9cbe41a1e6e34e6a87c7f49567bdef32.jpeg?x-oss-process=image/resize,s_500,m_lfit) # 摘要 本文全面探讨了MyEclipse集成开发环境的汉化过程,涵盖了汉化的基本概念、意义、工具选择、操作步骤、高级技巧以及未来的发展趋势。文章首先阐述了汉化对于MyEclipse用户的重要性,然后详细介绍了如何选择合适的汉化工具,并指导读者进

PFC5.0中文指南:框架基础与应用深入解析

![PFC5.0中文指南:框架基础与应用深入解析](https://i0.hdslb.com/bfs/archive/a00c4b2187ec46f902173af595f5f816fc4efb52.jpg@960w_540h_1c.webp) # 摘要 PFC5.0框架作为当前先进的软件开发工具,提供了全面的开发和维护解决方案。本文旨在介绍PFC5.0框架的概述和核心组件,深入分析其在业务逻辑中的应用、前后端交互机制,以及高级应用技巧。通过对核心组件的配置、优化和交互机制的研究,探讨了PFC5.0在不同业务逻辑层的设计与实施。本文还包括了PFC5.0框架在安全性和性能调优方面的考量,以及框

【Simulink断路器仿真实例解析】:从零开始的仿真构建步骤

![simulink 断路器仿真 对于初学者学习MATLAB中simulink仿真有很强的指导作用_rezip1.zip](https://img-blog.csdnimg.cn/9e000f75660d442ab777107bc3c24d76.jpeg) # 摘要 本文系统地介绍了使用Simulink进行断路器仿真模型的建立、实验与结果分析、模型优化与验证以及拓展应用。文章首先概述了Simulink仿真工具的基本界面操作和建立断路器仿真模型的基本原理与需求。接着详细叙述了断路器动作特性和故障模式的仿真过程,并对仿真结果进行了详细的数据处理与分析。之后,文章探讨了如何对仿真模型进行参数优化和
手机看
程序员都在用的中文IT技术交流社区

程序员都在用的中文IT技术交流社区

专业的中文 IT 技术社区,与千万技术人共成长

专业的中文 IT 技术社区,与千万技术人共成长

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

客服 返回
顶部