Skip to content

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)TR类型转换 (Entity -> DTO)
Predicate<T>boolean test(T t)Tboolean过滤判断 (Filter)
Consumer<T>void accept(T t)Tvoid消费数据 (打印、写入数据库)
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.DateSimpleDateFormat

  • 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 总结与自我检测

请尝试回答以下问题,检验今日学习成果:

  1. [Stream] mapflatMap 有什么区别?
  2. [Thread] 为什么 ThreadLocal 容易导致内存泄漏?务必在使用完后调用什么方法?
  3. [Thread] 线程池的 corePoolSizemaximumPoolSize 在任务提交时的触发顺序是怎样的?(关键:先核心,再队列,最后最大)
  4. [Java 17] 什么时候应该用 record 代替 class

下一步行动

如果 Day 1 感觉良好,Day 2 我们将进入 Spring Boot 3 与 Spring Cloud 核心组件 的复习。