Java常见面试题3

Java 常见面试题(三)

适合人群 :正在准备 Java 后端面试的求职者

技术栈 :Java 21

涵盖方向 :集合框架进阶、并发工具 AQS、Stream API 与 Lambda、设计模式、注解原理

阅读建议:每道题按「面试官问法 → 求职者回答 → 核心知识点」三段式展开,建议先自己回答再对照


一、ArrayList 和 LinkedList 的底层区别是什么?什么场景选哪个?

面试官会怎么问

"你平时用 ArrayList 多还是 LinkedList 多?它们底层有什么区别?分别在什么场景下更合适?"

追问:"ArrayList 扩容的代价有多大?LinkedList 的 Node 节点在内存里是怎么分布的?"

求职者该怎么答

底层数据结构完全不同

对比维度 ArrayList LinkedList
底层结构 动态数组(Object\[\]) 双向链表(Node 节点链)
随机访问 O(1),支持 get(i) 直接定位 O(n),必须从头遍历
头部插入/删除 O(n),需搬移元素 O(1),只改指针
尾部插入 均摊 O(1),偶尔触发扩容 O(1)
内存占用 连续内存,CPU 缓存友好 离散内存,每个节点额外存 prev/next 指针
实现接口 List、RandomAccess List、Deque

ArrayList 扩容机制

java 复制代码
// JDK 源码(简化)
public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5 倍扩容
    elementData = Arrays.copyOf(elementData, newCapacity);
}
  • 每次扩容需要 新建数组 + 复制全部元素,代价 O(n)
  • 默认初始容量 10,之后每次扩大 1.5 倍
  • 如果能预估数据量,建议用 new ArrayList<>(expectedSize) 避免频繁扩容

LinkedList 的内存布局

java 复制代码
// JDK 源码(简化)
private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.prev = prev;
        this.item = element;
        this.next = next;
    }
}
  • 每个元素都是一个独立对象,堆上分散分配
  • CPU 缓存预取命中率低,遍历速度实际上慢于 ArrayList
  • 实现了 Deque 接口,可以当栈和队列使用

实战选择建议

复制代码
场景:频繁随机读取、尾部追加       → ArrayList(绝大多数场景)
场景:频繁头部插入/删除、当队列用   → LinkedList(或 ArrayDeque 更优)
场景:已知数据量很大               → ArrayList + 预设容量

面试加分 :实际开发中 LinkedList 用得极少,因为它既不快(内存不连续)也不省(额外指针),大多数场景 ArrayList + 预设容量就够了。如果确实需要队列,ArrayDeque 性能比 LinkedList 好得多。

核心知识点总结

  • 数据结构选型:动态数组 vs 双向链表的本质区别
  • CPU 缓存友好性:连续内存 vs 离散内存在现代硬件上的性能差距
  • 扩容策略:1.5 倍扩容的数学原理(等比数列求和 ≈ 2 倍初始容量)

二、AQS 是什么?CountDownLatch 和 Semaphore 是怎么基于 AQS 实现的?

面试官会怎么问

"你了解 AQS 吗?它解决了什么问题?"

追问:"CountDownLatch 和 Semaphore 底层是怎么用 AQS 的?它们的区别是什么?"

再追问:"AQS 的公平模式和非公平模式有什么区别?"

求职者该怎么答

AQS(AbstractQueuedSynchronizer)是 Java 并发包的基石

复制代码
AQS 核心思想:
┌──────────────────────────────────────────────┐
│  volatile int state     ← 共享资源状态       │
│  CLH 变体双向队列        ← 等待线程排队        │
│  acquire / release      ← 获取与释放的模板方法  │
│  park / unpark          ← 线程阻塞与唤醒       │
└──────────────────────────────────────────────┘

基于 AQS 实现的重要类:
  - ReentrantLock(独占锁,state = 重入次数)
  - CountDownLatch(计数器,state = 剩余计数)
  - Semaphore(信号量,state = 可用许可数)
  - ReentrantReadWriteLock(读写锁,state 高16位读、低16位写)

AQS 工作原理

java 复制代码
// 伪代码:AQS 获取锁的核心流程
public final void acquire(int arg) {
    // 1. 先让子类尝试获取(tryAcquire)
    if (!tryAcquire(arg)) {
        // 2. 失败则入队
        Node node = addWaiter(Node.EXCLUSIVE);
        // 3. 自旋 + 阻塞等待
        acquireQueued(node, arg);
    }
}

// 子类实现 tryAcquire ------ 这是 AQS 的模板方法模式
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}

CountDownLatch ------ 等待多个线程完成

java 复制代码
// 使用场景:主线程等待 N 个子任务全部完成
CountDownLatch latch = new CountDownLatch(3);

// 子线程完成任务后调用
latch.countDown();  // state - 1

// 主线程等待
latch.await();      // state == 0 时才放行

// 底层原理:
// - state 初始化为 N
// - countDown() → state 减 1(CAS 操作)
// - await() → 如果 state > 0,当前线程入队阻塞
// - state 减到 0 时,唤醒所有等待线程

Semaphore ------ 控制并发数量

java 复制代码
// 使用场景:限制同时访问某资源的线程数(如数据库连接池限流)
Semaphore semaphore = new Semaphore(5); // 5 个许可

// 线程获取许可
semaphore.acquire();   // state - 1,如果 state == 0 则阻塞

// 线程释放许可
semaphore.release();   // state + 1,唤醒队列中一个线程

// 底层原理:
// - state 表示可用许可数
// - acquire() → CAS 将 state 减 1,如果 < 0 则入队
// - release() → CAS 将 state 加 1,唤醒队列中等待最久的线程

CountDownLatch vs Semaphore 核心区别

维度 CountDownLatch Semaphore
用途 等待 N 个事件发生 控制并发访问数量
state 含义 剩余计数(递减到 0) 可用许可(可增可减)
是否可重用 不可重用(一次性) 可重用(acquire/release 循环)
谁等待 调用 await 的线程 调用 acquire 的线程
典型场景 启动多个服务后开始处理 限流、资源池

公平 vs 非公平模式

java 复制代码
// 非公平(默认):新来的线程可以先尝试 CAS 抢锁,抢不到再排队
// 优点:减少上下文切换,吞吐量高
// 缺点:可能导致某些线程一直抢不到(饥饿)

// 公平:严格按队列顺序获取
// 优点:不会饥饿
// 缺点:每次都要入队出队,性能较低

Semaphore sem = new Semaphore(5, true);  // 公平模式
ReentrantLock lock = new ReentrantLock(true); // 公平模式

核心知识点总结

  • AQS 模板方法模式:父类提供排队/阻塞/唤醒框架,子类只实现 tryAcquire/tryRelease
  • state + CLH 队列:AQS 的两大核心,state 代表资源状态,队列管理等待线程
  • CAS 保证原子性:state 的修改全部通过 CAS 完成,保证线程安全

三、Java Stream API 和 Lambda 表达式,你在实际项目中怎么用的?

面试官会怎么问

"Java 8 的 Stream API 你用过吗?说说它的核心操作和原理"

追问:"Lambda 表达式和匿名内部类有什么本质区别?"

再追问:"Stream 的 parallelStream 是线程安全的吗?什么情况下用并行流?"

求职者该怎么答

Lambda 表达式的本质

java 复制代码
// Lambda 是函数式接口的语法糖,编译后生成 invokedynamic 指令
// 不会每次创建新对象(除非捕获了外部变量)

// Lambda 写法
list.sort((a, b) -> a.compareTo(b));

// 等价于匿名内部类(但底层完全不同)
list.sort(new Comparator<Integer>() {
    @Override
    public int compare(Integer a, Integer b) {
        return a.compareTo(b);
    }
});

// 核心区别:
// 1. Lambda 编译为 invokedynamic,由 LambdaMetafactory 在运行时动态生成
// 2. 匿名内部类编译为独立的 .class 文件,每次 new 都创建新实例
// 3. Lambda 中 this 指向外部类,匿名内部类中 this 指向自己

Stream API 核心操作分类

复制代码
Stream 操作管道:
数据源 → [中间操作] → [中间操作] → ... → 终端操作

中间操作(惰性求值,不执行):
  - filter(Predicate)    过滤
  - map(Function)        映射转换
  - flatMap(Function)    扁平化映射
  - sorted(Comparator)   排序
  - distinct()           去重
  - limit(n)             截取
  - skip(n)              跳过

终端操作(触发执行):
  - collect(Collector)   收集结果
  - forEach(Consumer)    遍历
  - reduce(BinaryOp)     归约
  - count() / min() / max()  聚合
  - anyMatch / allMatch     匹配
  - findFirst()          查找

实际项目中的典型用法

java 复制代码
// 1. 过滤 + 映射 + 收集
List<String> activeUserNames = users.stream()
    .filter(User::isActive)           // 过滤活跃用户
    .map(User::getName)               // 提取名字
    .collect(Collectors.toList());    // 收集为 List

// 2. 分组统计
Map<String, Long> deptCountMap = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.counting()
    ));

// 3. 多字段排序
List<Employee> sorted = employees.stream()
    .sorted(
        Comparator.comparing(Employee::getDepartment)
            .thenComparing(Employee::getSalary)
            .reversed()
    )
    .collect(Collectors.toList());

// 4. 扁平化(嵌套集合展平)
List<String> allTags = articles.stream()
    .flatMap(article -> article.getTags().stream())
    .distinct()
    .collect(Collectors.toList());

// 5. 归约求和
BigDecimal totalSalary = employees.stream()
    .map(Employee::getSalary)
    .reduce(BigDecimal.ZERO, BigDecimal::add);

// 6. partitioningBy 分流
Map<Boolean, List<Employee>> partitioned = employees.stream()
    .collect(Collectors.partitioningBy(
        e -> e.getSalary().compareTo(new BigDecimal("10000")) >= 0
    ));
// partitioned.get(true)  → 薪资 >= 10000
// partitioned.get(false) → 薪资 < 10000

并行流 parallelStream 注意事项

java 复制代码
// 并行流底层使用 ForkJoinPool.commonPool()
List<Integer> result = list.parallelStream()
    .filter(x -> x > 100)
    .map(x -> x * 2)
    .collect(Collectors.toList());

// ⚠️ 注意:
// 1. 不是线程安全的!如果操作有副作用(如修改共享变量),会出问题
List<Integer> unsafe = new ArrayList<>();
list.parallelStream().forEach(unsafe::add); // 可能丢失数据!

// 正确做法:用线程安全集合或收集到不可变结果
// 2. 数据量小(< 1000)不要用并行流,线程切换开销 > 收益
// 3. 有状态操作(sorted、distinct)在并行流中代价更高
// 4. 如果涉及 IO 操作,不要用并行流(会阻塞公共线程池)

Stream 的执行原理

java 复制代码
// Stream 本质是一个迭代器模式的链式调用
// 每次中间操作返回新的 Stream,终端操作触发遍历

// 简化模型:
stream.filter(pred).map(func).collect(list);

// 等价于一次遍历中完成所有操作(短路优化):
for (T item : source) {
    if (pred.test(item)) {         // filter
        R result = func.apply(item); // map
        list.add(result);           // collect
    }
    // 遇到 limit/short-circuit 会提前退出
}

核心知识点总结

  • Lambda 底层原理:invokedynamic 指令 + LambdaMetafactory,不是匿名内部类
  • Stream 惰性求值:中间操作不执行,终端操作触发,一次遍历完成管道
  • 并行流陷阱:线程安全、数据量阈值、IO 阻塞、公共线程池污染

四、面试必考的设计模式:单例、工厂、观察者,你能手写吗?

面试官会怎么问

"你能手写一个单例模式吗?为什么要那样写?"

追问:"工厂模式和抽象工厂模式有什么区别?"

再追问:"观察者模式在实际项目中用过吗?Spring 里有哪些观察者模式的体现?"

求职者该怎么答

单例模式的多种写法

java 复制代码
// 1. 饿汉式(线程安全,类加载时创建)
public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

// 2. 懒汉式 + synchronized(简单但性能差)
public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

// 3. 双重检查锁 DCL(推荐)
public class Singleton {
    private static volatile Singleton instance; // ⚠️ 必须 volatile
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {                 // 第一次检查,避免不必要的同步
            synchronized (Singleton.class) {
                if (instance == null) {         // 第二次检查,防止重复创建
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
// 为什么必须 volatile?
// new Singleton() 不是原子操作:
// 1. 分配内存空间
// 2. 初始化对象
// 3. 将引用指向内存地址
// 指令重排序可能变成 1→3→2,另一个线程拿到未初始化的对象

// 4. 静态内部类(最优雅,推荐)
public class Singleton {
    private Singleton() {}
    
    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return Holder.INSTANCE; // 第一次调用时才触发 Holder 类加载
    }
}
// 利用类加载机制保证线程安全,懒加载,无同步开销

// 5. 枚举(最安全,防反射 + 防序列化破坏)
public enum Singleton {
    INSTANCE;
    // 业务方法
    public void doSomething() { }
}

单例被破坏的场景与防护

破坏方式 防护手段
反射调用私有构造器 构造器中抛异常或检查是否已创建
序列化反序列化 实现 readResolve() 方法
Clone 实现 Cloneable 并抛 CloneNotSupportedException
类加载器不同 使用统一类加载器(或枚举)

枚举天然防反射、防序列化,是 Effective Java 推荐的最佳写法

工厂模式

java 复制代码
// 简单工厂(不属于 GoF 23 种)
public class AnimalFactory {
    public static Animal create(String type) {
        return switch (type) {
            case "lion" -> new Lion();
            case "tiger" -> new Tiger();
            case "elephant" -> new Elephant();
            default -> throw new IllegalArgumentException("Unknown: " + type);
        };
    }
}

// 工厂方法模式(每种产品对应一个工厂)
public interface AnimalFactory {
    Animal create();
}
public class LionFactory implements AnimalFactory {
    @Override
    public Animal create() { return new Lion(); }
}
public class TigerFactory implements AnimalFactory {
    @Override
    public Animal create() { return new Tiger(); }
}
// 优点:符合开闭原则,新增产品只需新增工厂
// 缺点:类数量膨胀

// 抽象工厂模式(创建产品族)
public interface ZooFactory {
    Animal createAnimal();
    Cage createCage();
    Food createFood();
}
public class SafariZooFactory implements ZooFactory {
    @Override public Animal createAnimal() { return new Lion(); }
    @Override public Cage createCage() { return new OpenCage(); }
    @Override public Food createFood() { return new MeatFood(); }
}
// 优点:保证产品族一致性
// 缺点:新增产品等级(如新增 Toy)需要修改接口

观察者模式

java 复制代码
// Java 原生支持(但已过时,推荐自己实现或用 Spring Event)

// 自定义观察者模式
// 1. 定义事件
public record AnimalEvent(String animalName, String action, LocalDateTime time) {}

// 2. 定义监听器接口
@FunctionalInterface
public interface AnimalEventListener {
    void onEvent(AnimalEvent event);
}

// 3. 事件发布者
public class AnimalEventManager {
    private final List<AnimalEventListener> listeners = new CopyOnWriteArrayList<>();
    
    public void register(AnimalEventListener listener) {
        listeners.add(listener);
    }
    
    public void unregister(AnimalEventListener listener) {
        listeners.remove(listener);
    }
    
    public void publish(AnimalEvent event) {
        listeners.forEach(listener -> listener.onEvent(event));
    }
}

// 4. 使用
AnimalEventManager manager = new AnimalEventManager();
manager.register(event -> 
    System.out.println("日志记录: " + event.animalName() + " " + event.action()));
manager.register(event -> 
    System.out.println("通知管理员: " + event.animalName() + " " + event.action()));

manager.publish(new AnimalEvent("狮子", "进食", LocalDateTime.now()));

Spring 中的观察者模式体现

java 复制代码
// Spring ApplicationEvent 机制
// 1. 定义事件
public class AnimalCreatedEvent extends ApplicationEvent {
    private final String animalName;
    public AnimalCreatedEvent(Object source, String animalName) {
        super(source);
        this.animalName = animalName;
    }
}

// 2. 发布事件
@Service
public class AnimalService {
    @Autowired
    private ApplicationEventPublisher publisher;
    
    public void createAnimal(String name) {
        // 业务逻辑...
        publisher.publishEvent(new AnimalCreatedEvent(this, name));
    }
}

// 3. 监听事件
@Component
public class AnimalCreatedListener {
    @EventListener
    public void handleAnimalCreated(AnimalCreatedEvent event) {
        System.out.println("动物已创建: " + event.getAnimalName());
    }
}

// 其他体现:
// - Spring 容器生命周期回调(ContextRefreshedEvent 等)
// - @Transactional 的提交/回滚事件
// - ApplicationReadyEvent 等启动事件

核心知识点总结

  • 单例:volatile + DCL 或静态内部类,枚举最安全,理解指令重排序
  • 工厂:简单工厂 → 工厂方法 → 抽象工厂,递进关系,开闭原则
  • 观察者:发布-订阅解耦,Spring Event 是生产级实现

五、注解(Annotation)是什么?怎么自定义注解 + AOP 实现切面?

面试官会怎么问

"Java 注解的底层原理是什么?它和注释有什么区别?"

追问:"你写过自定义注解吗?怎么配合 AOP 实现一个权限校验?"

再追问:"元注解有哪些?注解的保留策略是什么意思?"

求职者该怎么答

注解的本质

java 复制代码
// 注解本质上是一个继承了 java.lang.annotation.Annotation 的接口
// 编译后注解信息保存在 class 文件的 RuntimeVisibleAnnotations 属性中
// 运行时可以通过反射获取注解信息

// 定义一个注解:
public @interface MyAnnotation {
    String value();  // 注解属性
}

// 编译后等价于:
public interface MyAnnotation extends Annotation {
    String value();
}

// 使用注解:
@MyAnnotation("hello")
public class MyClass { }

// 运行时获取注解:
Class<?> clazz = MyClass.class;
MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
System.out.println(annotation.value()); // "hello"

四个元注解

java 复制代码
// 1. @Target ------ 注解可以用在什么地方
@Target({
    ElementType.TYPE,           // 类、接口、枚举
    ElementType.METHOD,         // 方法
    ElementType.FIELD,          // 字段
    ElementType.PARAMETER,      // 参数
    ElementType.CONSTRUCTOR,    // 构造器
    ElementType.LOCAL_VARIABLE, // 局部变量
    ElementType.ANNOTATION_TYPE,// 注解类型
    ElementType.PACKAGE         // 包
})

// 2. @Retention ------ 注解的生命周期
@Retention(RetentionPolicy.SOURCE)   // 只存在于源码,编译后丢弃
@Retention(RetentionPolicy.CLASS)    // 存在于 class 文件,运行时不可获取(默认)
@Retention(RetentionPolicy.RUNTIME)  // 运行时可通过反射获取(最常用)

// 3. @Documented ------ 注解是否包含在 Javadoc 中
// 4. @Inherited ------ 子类是否继承父类的注解(仅对类注解有效)

实战:自定义权限校验注解 + AOP

java 复制代码
// 第一步:定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequireRole {
    String[] value();  // 允许的角色列表
}

// 第二步:编写 AOP 切面
@Aspect
@Component
public class RoleCheckAspect {
    
    @Around("@annotation(requireRole)")
    public Object checkRole(ProceedingJoinPoint joinPoint, RequireRole requireRole) throws Throwable {
        // 1. 获取当前用户角色(实际从 Token/Session 中获取)
        String currentRole = getCurrentUserRole();
        
        // 2. 检查是否在允许的角色列表中
        boolean hasPermission = Arrays.stream(requireRole.value())
            .anyMatch(role -> role.equals(currentRole));
        
        if (!hasPermission) {
            throw new UnauthorizedException(
                "权限不足,需要角色: " + Arrays.toString(requireRole.value()) 
                + ",当前角色: " + currentRole
            );
        }
        
        // 3. 权限通过,执行原方法
        return joinPoint.proceed();
    }
    
    private String getCurrentUserRole() {
        // 模拟:从 SecurityContext 或 JWT 中解析
        return "admin";
    }
}

// 第三步:在 Controller 上使用
@RestController
public class ZooController {
    
    @RequireRole({"admin", "zookeeper"})
    @PostMapping("/animals")
    public ResponseEntity<?> addAnimal(@RequestBody AnimalDTO dto) {
        // 业务逻辑...
        return ResponseEntity.ok().build();
    }
    
    @RequireRole({"visitor"})
    @GetMapping("/animals")
    public ResponseEntity<?> listAnimals() {
        // 游客也能看...
        return ResponseEntity.ok().build();
    }
}

注解 + 参数校验实战

java 复制代码
// 自定义日志注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
    String module() default "";
    String action() default "";
}

// AOP 切面自动记录日志
@Aspect
@Component
public class OperationLogAspect {
    @Autowired
    private LogService logService;
    
    @Around("@annotation(operationLog)")
    public Object recordLog(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().toShortString();
        Object[] args = joinPoint.getArgs();
        
        try {
            Object result = joinPoint.proceed();
            long cost = System.currentTimeMillis() - startTime;
            
            logService.save(new LogRecord(
                operationLog.module(),
                operationLog.action(),
                methodName,
                Arrays.toString(args),
                "SUCCESS",
                cost
            ));
            return result;
        } catch (Exception e) {
            logService.save(new LogRecord(
                operationLog.module(),
                operationLog.action(),
                methodName,
                Arrays.toString(args),
                "FAIL: " + e.getMessage(),
                System.currentTimeMillis() - startTime
            ));
            throw e;
        }
    }
}

// 使用
@OperationLog(module = "动物管理", action = "新增动物")
@PostMapping("/animals")
public ResponseEntity<?> addAnimal(@RequestBody AnimalDTO dto) {
    // ...
}

Java 内置常用注解

java 复制代码
@Override          // 编译期检查方法覆写
@Deprecated        // 标记已过时
@SuppressWarnings   // 抑制编译器警告

// Java 8+ 新增的类型注解(用于类型检查框架)
@FunctionalInterface  // 标记函数式接口
@SafeVarargs          // 抑制堆污染警告

// Java 14+ 预览特性
// 注解可以放在 record 的组件上
public record Animal(@NotBlank String name, @Min(0) int age) {}

核心知识点总结

  • 注解本质是接口:编译后保存在 class 文件,运行时通过反射读取
  • 四个元注解:@Target(作用域)、@Retention(生命周期)、@Documented、@Inherited
  • 注解 + AOP = 声明式编程:把横切关注点(日志、权限、事务)从业务代码中解耦
  • Spring 大量使用注解:@Autowired、@Service、@Transactional 都是注解驱动

知识图谱总结

复制代码
Java 常见面试题(三)
│
├── 集合框架进阶
│   ├── ArrayList:动态数组、1.5倍扩容、连续内存
│   ├── LinkedList:双向链表、离散内存、Node节点
│   └── 选型建议:绝大多数场景 ArrayList + 预设容量
│
├── 并发工具 AQS
│   ├── AQS 核心:state + CLH队列 + 模板方法
│   ├── CountDownLatch:计数器,等待N个事件,不可重用
│   ├── Semaphore:信号量,控制并发数,可重用
│   └── 公平 vs 非公平:吞吐量 vs 不饥饿
│
├── Stream API 与 Lambda
│   ├── Lambda 本质:invokedynamic + LambdaMetafactory
│   ├── Stream 管道:中间操作惰性 + 终端操作触发
│   ├── 常用操作:filter/map/collect/groupBy/reduce
│   └── 并行流陷阱:线程安全、数据量阈值、IO阻塞
│
├── 设计模式
│   ├── 单例:DCL + volatile / 静态内部类 / 枚举
│   ├── 工厂:简单工厂 → 工厂方法 → 抽象工厂
│   ├── 观察者:发布-订阅解耦
│   └── Spring Event:生产级观察者模式
│
└── 注解(Annotation)
    ├── 本质:继承 Annotation 的接口
    ├── 元注解:@Target / @Retention / @Documented / @Inherited
    ├── 自定义注解 + AOP:声明式权限校验 / 日志记录
    └── Spring 注解驱动:@Autowired / @Transactional 等
相关推荐
云栖梦泽1 小时前
从 IMX415 摄像头驱动理解 Media Controller 框架
linux·开发语言·c++·嵌入式硬件
tachibana21 小时前
hot100 排序链表(148)
java·数据结构·算法·leetcode·链表
初阳7851 小时前
【Qt】界面优化(1)——QSS
开发语言·qt
wear工程师1 小时前
synchronized 锁升级:多数人还在背 JDK8 的答案,偏向锁早在 JDK18 就没了
java·面试
荣码1 小时前
LangGraph多步工作流:Agent不够用的时候,我踩了4个坑
java·python
影寂ldy1 小时前
C# WinForms 窗体继承
开发语言·c#
是小李呀1 小时前
Java集合扩容机制解析:ArrayList与HashMap底层原理及性能差异
java
SimonKing1 小时前
FreeLLMAPI:24 家厂商、128 个模型、1 个 Key智能切换所有可用的模型
java·后端·程序员