Appearance
Java 后端重返工作详细学习计划 - Day 1
目标:全面复苏 Java 语感,掌握从 Java 8 到 Java 17 的核心语法演进,并重温高并发编程的"深水区"与避坑指南。
☀️ 上午(3小时):Java 现代语法核心 (Java 8-17)
1. Lambda 表达式与函数式接口
核心概念:Lambda 是匿名内部类的语法糖,旨在将"行为"作为参数传递。
语法:(参数) -> { 逻辑 }
核心记忆:四大内置函数式接口
在 java.util.function 包下,死记这四个,涵盖 90% 场景:
| 接口 | 抽象方法 | 参数 | 返回值 | 典型用途 |
|---|---|---|---|---|
Function<T, R> | R apply(T t) | T | R | 类型转换 (Entity -> DTO) |
Predicate<T> | boolean test(T t) | T | boolean | 过滤判断 (Filter) |
Consumer<T> | void accept(T t) | T | void | 消费数据 (打印、写入数据库) |
Supplier<T> | T get() | 无 | T | 工厂方法、懒加载 |
2. Stream API (声明式处理)
Stream 是处理集合(Collection)的神器。
操作流程:Source (集合) -> Intermediate (中间操作: map/filter) -> Terminal (终止操作: collect/forEach)。
🔥 高频操作速查
java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 1. 过滤与映射
List<Integer> squares = numbers.stream()
.filter(n -> n % 2 == 0) // 谓词:保留偶数
.map(n -> n * n) // 转换:求平方
.collect(Collectors.toList());
// 2. 统计 (IntStream)
IntSummaryStatistics stats = numbers.stream()
.mapToInt(x -> x)
.summaryStatistics();
System.out.println("Max: " + stats.getMax()); // 输出最大值
System.out.println("Avg: " + stats.getAverage()); // 输出平均值💡 实战进阶:分组与转 Map
这是面试和工作中极其高频的场景。
java
// 假设有一个 User 对象列表
List<User> users = getUsers();
// 场景 A: 按部门分组 (Map<String, List<User>>)
Map<String, List<User>> byDept = users.stream()
.collect(Collectors.groupingBy(User::getDepartment));
// 场景 B: List 转 Map (id -> User),注意处理 Key 重复
Map<Long, User> idMap = users.stream()
.collect(Collectors.toMap(
User::getId,
Function.identity(),
(oldVal, newVal) -> newVal // 如果 Key 重复,保留新值
));3. Optional 与 Map 增强 (避免空指针)
Optional 最佳实践
避坑指南
永远不要直接调用 Optional.get(),否则和写 if (obj != null) 没区别,甚至抛出 NoSuchElementException。
java
// 正确姿势:
User user = findUserById(1); // 可能返回 null
Optional<User> opt = Optional.ofNullable(user);
// 1. 存在则打印
opt.ifPresent(u -> System.out.println(u.getName()));
// 2. 存在则转换,不存在则返回默认值
String name = opt.map(User::getName).orElse("Unknown");
// 3. 不存在则抛出异常
User validUser = opt.orElseThrow(() -> new RuntimeException("User not found"));Map 新特性 (Java 8)
computeIfAbsent:构建本地缓存的神器。
java
Map<String, List<String>> cache = new HashMap<>();
// 如果 "Java" 这个 key 不存在,则创建一个新 List 放入,并返回该 List
cache.computeIfAbsent("Java", k -> new ArrayList<>()).add("SpringBoot");4. 新日期时间 API (Java Time)
彻底抛弃 java.util.Date 和 SimpleDateFormat。
- LocalDateTime: 不带时区的日期时间。
- ZonedDateTime: 带时区的日期时间。
- DateTimeFormatter: 线程安全的格式化器。
java
// 获取当前时间
LocalDateTime now = LocalDateTime.now();
// 格式化
String s = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 计算时间差
LocalDateTime taskTime = LocalDateTime.of(2025, 11, 11, 10, 0);
long days = ChronoUnit.DAYS.between(now, taskTime);🌤️ 下午(3小时):Java 并发编程复习
1. 基础关键字回顾
| 关键字 | 作用 | 关键点 |
|---|---|---|
synchronized | 保证原子性、可见性 | 锁升级机制(偏向锁->轻量级锁->重量级锁),可修饰方法或代码块。 |
volatile | 保证可见性、禁止指令重排 | 不保证原子性。常用于状态标记量、单例模式双重检查锁。 |
2. 线程池 (ThreadPoolExecutor) [重中之重]
阿里巴巴开发规范
禁止使用 Executors.newFixedThreadPool() 或 newCachedThreadPool() 创建线程池。
原因:允许的请求队列长度为 Integer.MAX_VALUE,可能堆积大量请求,导致 OOM (Out Of Memory)。
标准创建方式
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // corePoolSize: 核心线程数(常驻)
5, // maximumPoolSize: 最大线程数(救急用)
60, TimeUnit.SECONDS, // keepAliveTime: 救急线程空闲存活时间
new LinkedBlockingQueue<>(100), // workQueue: 阻塞队列(必须要设置容量!)
Executors.defaultThreadFactory(),// threadFactory: 线程工厂(建议自定义名称方便排查)
new ThreadPoolExecutor.CallerRunsPolicy() // handler: 拒绝策略(队列满了谁来干?)
);
// CallerRunsPolicy: 由提交任务的线程自己去执行该任务(一种缓和的降级策略)3. CompletableFuture (异步编排)
Java 8 引入的异步编程神器,解决了 Future.get() 阻塞的问题。
场景:查询商品详情(同时查基本信息、库存、图片,三者并行,全部完成后返回)。
java
// 1. 异步任务 A
CompletableFuture<String> taskA = CompletableFuture.supplyAsync(() -> {
return "基本信息";
});
// 2. 异步任务 B
CompletableFuture<String> taskB = CompletableFuture.supplyAsync(() -> {
return "库存信息";
});
// 3. 组合结果
CompletableFuture<Void> allTasks = CompletableFuture.allOf(taskA, taskB);
// 4. 等待所有完成并处理
allTasks.thenRun(() -> {
try {
System.out.println(taskA.get() + " & " + taskB.get());
} catch (Exception e) { e.printStackTrace(); }
}).join();4. 并发集合与原子类
- ConcurrentHashMap: 必须要懂原理(CAS + synchronized,节点上锁)。
- AtomicInteger: 基于 CAS (Compare And Swap) 实现无锁自增,适合做计数器。
🌙 晚上(2小时):Java 17+ 新特性 (LTS)
Java 17 是目前主流的 LTS 版本,很多框架(如 Spring Boot 3)已强制要求 Java 17。
1. Record (记录类)
概念:专门用于承载数据的不可变类(Data Carrier),自动生成 getter、equals、hashCode、toString。
场景:DTO, VO, 配置类。
java
// 定义
public record Person(String name, int age) {}
// 使用
Person p = new Person("Alice", 25);
System.out.println(p.name()); // 注意:是 name() 不是 getName()2. Switch Expressions (增强版 Switch)
支持箭头语法,不再需要 break,且可以作为表达式返回值。
java
String dayType = switch (day) {
case MONDAY, FRIDAY -> "Working Day";
case SUNDAY -> "Weekend";
default -> {
System.out.println("Checking...");
yield "Unknown"; // 代码块中使用 yield 返回
}
};3. Text Blocks (文本块)
用 """ 包裹,原生支持多行字符串,写 SQL 和 JSON 极其舒服。
java
String json = """
{
"name": "Alice",
"role": "Admin"
}
""";4. Pattern Matching (模式匹配)
instanceof 模式匹配省去了强制类型转换的步骤。
java
Object obj = "Hello Java 17";
// 旧写法
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.length());
}
// 新写法 (Java 16+)
if (obj instanceof String s) {
System.out.println(s.length()); // s 直接可用
}📝 Day 1 总结与自我检测
请尝试回答以下问题,检验今日学习成果:
- [Stream]
map和flatMap有什么区别? - [Thread] 为什么
ThreadLocal容易导致内存泄漏?务必在使用完后调用什么方法? - [Thread] 线程池的
corePoolSize和maximumPoolSize在任务提交时的触发顺序是怎样的?(关键:先核心,再队列,最后最大) - [Java 17] 什么时候应该用
record代替class?
下一步行动
如果 Day 1 感觉良好,Day 2 我们将进入 Spring Boot 3 与 Spring Cloud 核心组件 的复习。
