深入浅出Java 8 Lambda表达式
一、Lambda 表达式的基本语法
语法格式:
(参数列表) -> { 执行语句 }
():参数列表,可为空,也可带参数;->:Lambda 表达式的标识;{}:代码块(表达式体)。
举例说明(城市版):
() -> System.out.println("欢迎来到北京!")
解释:
- 没有参数;
->表示这是一个 Lambda;- 执行的操作是输出“欢迎来到北京!”。
二、Lambda 表达式的典型使用场景
1️⃣ 替代匿名内部类(线程示例)
传统写法:
new Thread(new Runnable() {@Overridepublic void run() {System.out.println("上海,正在建设中...");}
}).start();
Lambda 写法:
new Thread(() -> System.out.println("上海,正在建设中...")).start();
是不是更简洁、更直观。
2️⃣ 作为变量使用
Runnable cityRunner = () -> System.out.println("广州欢迎你!");
cityRunner.run();
3️⃣ 作为返回值
import java.io.File;
import java.io.FileFilter;static FileFilter cityFilter(String suffix) {return (file) -> file.getName().endsWith(suffix);
}public static void main(String[] args) {File[] files = new File("cities").listFiles(cityFilter(".txt"));for (File file : files) {System.out.println(file.getName());}
}
这段代码返回一个过滤器,筛选出以 .txt 结尾的城市文件。
4️⃣ 作为数组元素
import java.nio.file.PathMatcher;PathMatcher[] matchers = {(path) -> path.toString().endsWith("beijing"),(path) -> path.toString().endsWith("shanghai")
};
5️⃣ 作为方法参数
public class CityApp {public static void main(String[] args) {showCity(() -> System.out.println("欢迎来到成都!"));}static void showCity(Runnable r) {r.run();}
}
三、Lambda 中的变量作用域
Lambda 中使用的外部变量必须是 final 或 effectively final(实际上没有改变的变量)。
错误示例:
int population = 1000;
Runnable r = () -> {population = 2000; // ❌ 编译错误:不能修改System.out.println(population);
};
解决方案(以城市为例):
方法 1:使用 static 变量
public class CityLambda {static int population = 1000;public static void main(String[] args) {Runnable r = () -> {population = 2000;System.out.println("北京人口:" + population);};r.run();}
}
方法 2:使用 AtomicInteger
import java.util.concurrent.atomic.AtomicInteger;public class CityLambda {public static void main(String[] args) {final AtomicInteger population = new AtomicInteger(1000);Runnable r = () -> {population.set(2000);System.out.println("上海人口:" + population.get());};r.run();}
}
方法 3:使用数组
public class CityLambda {public static void main(String[] args) {final int[] population = {1000};Runnable r = () -> {population[0] = 2000;System.out.println("广州人口:" + population[0]);};r.run();}
}
四、Lambda 与 this 的区别
Lambda 不会创建新的作用域,this 代表外层类对象。
示例:
public class CityLambda {public void show() {Runnable r1 = new Runnable() {@Overridepublic void run() {System.out.println("匿名类 this = " + this);}};Runnable r2 = () -> {System.out.println("Lambda this = " + this);};new Thread(r1).start();new Thread(r2).start();}public static void main(String[] args) {new CityLambda().show();}
}
输出结果:
匿名类 this = CityLambda$1@7852e922
Lambda this = CityLambda@4e25154f
匿名内部类的
this指的是匿名类对象;
Lambda 的this指的是外部类CityLambda对象。
| 功能 | 示例(城市主题) | 说明 |
|---|---|---|
| 基本语法 | () -> System.out.println("北京") | 无参数、单语句 |
| 变量形式 | Runnable r = () -> ... | Lambda 可作为变量 |
| 方法参数 | new Thread(() -> ...) | 常用于多线程 |
| 返回值 | return (path) -> path.endsWith("city") | 返回函数式接口实例 |
| 作用域 | 使用 final、AtomicInteger、数组 | 避免修改局部变量 |
| this 指向 | 外层类对象 | 不同于匿名内部类 |
接下来,我们进入实战部分,看看lambda在多线程并发编程中的应用:
下面我会列举 5 个层层递进的示例,从最基础的线程启动,到使用线程池、并行流和异步任务。
示例 1:使用 Lambda 启动多线程
最基础的写法,相当于用 Lambda 替代匿名内部类。
public class CityThreadDemo1 {public static void main(String[] args) {// 启动两个线程,分别打印城市信息new Thread(() -> System.out.println("北京线程正在运行...")).start();new Thread(() -> System.out.println("上海线程正在运行...")).start();}
}
关键点:
Runnable是函数式接口;- Lambda 直接替代
new Runnable() { ... }; - 简洁易读。
示例 2:多线程循环打印(并行执行任务)
public class CityThreadDemo2 {public static void main(String[] args) {String[] cities = {"北京", "上海", "广州", "深圳"};for (String city : cities) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + ":欢迎来到 " + city);}).start();}}
}
注意捕获变量的问题:
这里 city 必须是 effectively final,否则编译报错。解决方法是:
for (String city : cities) {final String c = city; // 创建一个 final 副本new Thread(() -> System.out.println(Thread.currentThread().getName() + ":欢迎来到 " + c)).start();
}
示例 3:使用线程池(ExecutorService)
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class CityThreadDemo3 {public static void main(String[] args) {ExecutorService pool = Executors.newFixedThreadPool(3);String[] cities = {"北京", "上海", "杭州", "广州", "深圳"};for (String city : cities) {pool.submit(() -> System.out.println(Thread.currentThread().getName() + " 负责建设:" + city));}pool.shutdown();}
}
Lambda 的优势:
- 更简洁地提交任务;
- 无需创建
Runnable类; - 结合线程池高效执行。
示例 4:使用 CompletableFuture 实现异步并发
import java.util.concurrent.CompletableFuture;public class CityThreadDemo4 {public static void main(String[] args) {CompletableFuture<Void> beijing = CompletableFuture.runAsync(() -> {System.out.println(" 北京地铁建设中...");});CompletableFuture<Void> shanghai = CompletableFuture.runAsync(() -> {System.out.println(" 上海外滩灯光秀升级中...");});CompletableFuture<Void> all = CompletableFuture.allOf(beijing, shanghai);all.join(); // 等待所有任务完成System.out.println(" 所有城市建设完成!");}
}
说明:
runAsync()接受 Lambda;- 适合并发执行独立任务;
- 使用
allOf()等待所有完成。
示例 5:结合并行流(Parallel Stream)
import java.util.Arrays;public class CityThreadDemo5 {public static void main(String[] args) {String[] cities = {"北京", "上海", "广州", "深圳", "杭州"};Arrays.stream(cities).parallel().forEach(city ->System.out.println(Thread.currentThread().getName() + " 正在巡查:" + city));}
}
.parallel()会自动使用 ForkJoinPool 并行执行;- 无需手动创建线程;
- 适合对数据集合进行并行处理。
小结
| 场景 | 示例 | 特点 |
|---|---|---|
| 基础线程启动 | new Thread(() -> ...) | 简洁替代匿名类 |
| 多线程任务 | 循环创建线程 | 注意 effectively final |
| 线程池并发 | ExecutorService.submit(() -> ...) | 可控、高效 |
| 异步编排 | CompletableFuture.runAsync() | 支持异步组合 |
| 并行流 | Arrays.stream(...).parallel() | 自动并行化数据处理 |
