全栈手账 订阅 RSS

Java 知识总结与面试题

Java 是一门面向对象的编程语言,以其"一次编写,到处运行"的跨平台特性长期占据企业级开发主流地位。本文涵盖从基础到进阶的核心知识点。

读了 50 分钟 更新于 8 月 5 日

一、Java 基础

1. 基本数据类型

类型 大小 默认值 范围 示例
byte 1 字节 0 -128 ~ 127 byte a = 10;
short 2 字节 0 -32768 ~ 32767 short b = 100;
int 4 字节 0 -2^31 ~ 2^31-1 int c = 1000;
long 8 字节 0L -2^63 ~ 2^63-1 long d = 1000L;
float 4 字节 0.0f 约 ±3.4E-38 ~ ±3.4E+38 float e = 3.14f;
double 8 字节 0.0d 约 ±1.7E-308 ~ ±1.7E+308 double f = 3.14;
char 2 字节 '\u0000' 0 ~ 65535 char g = 'A';
boolean 未明确定义 false true / false boolean h = true;

注意:

  • long 赋值结尾要加 L,float 加 f
  • char 在 Java 中是 2 字节(Unicode),不是 1 字节

2. 访问修饰符

修饰符 同类 同包 子类(不同包) 所有类
private ✅ ❌ ❌ ❌
default(不写) ✅ ✅ ❌ ❌
protected ✅ ✅ ✅ ❌
public ✅ ✅ ✅ ✅

3. OOP 三大特性

封装

  • 将数据(属性)和操作数据的方法绑定在一起
  • 对外隐藏内部实现,通过 getter/setter 暴露访问
  • 用 private + public 方法实现
java
public class User {    private String name;  // 隐藏内部数据        public String getName() {        return name;    }        public void setName(String name) {        if (name != null && name.length() > 0) {            this.name = name;        }    }}

继承

  • 用 extends 关键字实现,Java 只支持单继承
  • 子类继承父类的非 private 成员
  • 通过 super 调用父类构造器和方法
java
public class Animal {    protected String name;        public void eat() {        System.out.println("Animal is eating");    }}public class Dog extends Animal {    public void bark() {        System.out.println("Dog is barking");    }        @Override    public void eat() {        super.eat();  // 调用父类方法        System.out.println("Dog is eating");    }}

多态

  • 同一个方法在不同对象上有不同的行为
  • 三要素:继承、方法重写、父类引用指向子类对象
java
Animal animal = new Dog();   // 父类引用指向子类对象animal.eat();                 // 调用的是 Dog 的 eat() 方法// animal.bark();            // ❌ 编译错误,父类引用看不到子类特有方法

4. 接口与抽象类

对比项 抽象类 接口
关键字 abstract class interface
构造方法 可以有 不能有
成员变量 任何类型 public static final(常量)
方法实现 可以有抽象方法和普通方法 Java 8+ 可以有 default/static 方法
继承方式 extends(单继承) implements(多实现)
语义 "是什么"(is-a) "能做什么"(has-a/can-do)
java
// 抽象类:定义一种类型和公共行为public abstract class Shape {    protected String color;        public abstract double area();  // 抽象方法,子类必须实现        public void setColor(String color) {        this.color = color;    }}// 接口:定义能力public interface Drawable {    void draw();  // 默认 public abstract        default void print() {  // Java 8+ default 方法        System.out.println("Printing...");    }}

选择原则:

  • 有关系(is-a)、有共享状态 → 抽象类
  • 没关系(can-do)、只定义行为 → 接口
  • Java 8 后接口可以有 default 方法,但建议接口仍以"行为契约"为主

5. 重载 vs 重写

对比 重载(Overload) 重写(Override)
定义 同类中方法名相同,参数不同 子类重新实现父类方法
参数 必须不同(个数/类型/顺序) 必须相同
返回类型 可以不同 必须相同或子类(协变返回)
访问权限 无限制 不能比父类更严格
异常 无限制 不能抛出比父类更宽泛的异常

二、集合框架

1. 集合体系结构

文本
Collection(接口)├── List(有序、可重复)│   ├── ArrayList(数组实现,查询快)│   └── LinkedList(链表实现,增删快)├── Set(无序、不可重复)│   ├── HashSet(哈希表,最快)│   ├── TreeSet(红黑树,有序)│   └── LinkedHashSet(链表+哈希,保持插入顺序)└── Queue(队列)    ├── LinkedList    └── PriorityQueue(优先队列)Map(接口,键值对)├── HashMap(哈希表,最快)├── TreeMap(红黑树,键有序)├── LinkedHashMap(保持插入顺序)└── ConcurrentHashMap(线程安全)

2. ArrayList vs LinkedList

对比项 ArrayList LinkedList
底层结构 动态数组 双向链表
随机访问(get(index)) O(1) O(n)
尾部插入 O(1) O(1)
中间插入/删除 O(n) O(n)(但找到位置后操作快)
内存占用 少(只存数据) 多(存前后指针)
适用场景 查询多、尾部追加 频繁头尾操作、队列/栈场景
java
// 常用 APIList<String> list = new ArrayList<>();list.add("A");               // 添加list.get(0);                 // 取list.remove(0);              // 删list.contains("A");          // 是否包含list.size();                 // 大小

3. HashMap 原理

特性 说明
底层结构 数组 + 链表 + 红黑树(JDK 8+)
默认容量 16
负载因子 0.75(超过 75% 容量时扩容)
扩容倍数 2 倍(位移操作优化性能)
key 为 null 允许,放在 table[0]
线程安全 ❌ 非线程安全,需用 ConcurrentHashMap

JDK 8+ 优化:

  • 链表长度 ≥ 8 时转为红黑树(TREEIFY_THRESHOLD = 8)
  • 红黑树节点 ≤ 6 时转回链表(UNTREEIFY_THRESHOLD = 6)
  • 引入红黑树是为了解决哈希碰撞严重时链表查询 O(n) 的性能问题
java
Map<String, Integer> map = new HashMap<>();map.put("key", 1);map.get("key");               // 1map.getOrDefault("key", 0);   // 1map.containsKey("key");       // truemap.forEach((k, v) -> System.out.println(k + "=" + v));

4. 线程安全集合

集合 说明
ConcurrentHashMap 分段锁/CAS,高并发首选
CopyOnWriteArrayList 读多写少的场景,写时复制
BlockingQueue 阻塞队列,生产者-消费者模式
Collections.synchronizedList() 同步包装器,性能较低
java
// 推荐用法Map<String, Object> concurrentMap = new ConcurrentHashMap<>();Queue<String> queue = new LinkedBlockingQueue<>();

5. Collections 工具类

java
List<Integer> list = Arrays.asList(3, 1, 4, 1, 5);Collections.sort(list);               // 排序Collections.reverse(list);            // 反转Collections.shuffle(list);            // 打乱Collections.max(list);                // 最大值Collections.min(list);                // 最小值Collections.binarySearch(list, 3);    // 二分查找Collections.unmodifiableList(list);   // 返回不可修改的视图

三、异常处理

1. 异常体系

code
Throwable├── Error(不可恢复)│   ├── OutOfMemoryError│   ├── StackOverflowError│   └── NoClassDefFoundError└── Exception(可处理)    ├── RuntimeException(非受检异常)    │   ├── NullPointerException    │   ├── ArrayIndexOutOfBoundsException    │   ├── IllegalArgumentException    │   └── ArithmeticException    └── 受检异常(Checked Exception)        ├── IOException        ├── SQLException        └── FileNotFoundException

2. try-catch-finally

java
try {    // 可能抛出异常的代码    FileInputStream fis = new FileInputStream("test.txt");    int data = fis.read();} catch (FileNotFoundException e) {    // 处理特定异常    System.err.println("文件未找到: " + e.getMessage());} catch (IOException e) {    // 处理更通用的异常    System.err.println("IO 异常: " + e.getMessage());} finally {    // 无论是否异常都执行(释放资源)    if (fis != null) {        try { fis.close(); } catch (IOException e) { /* ignore */ }    }}

3. try-with-resources(JDK 7+)

自动关闭实现了 AutoCloseable 的资源:

java
try (FileInputStream fis = new FileInputStream("test.txt");     BufferedReader br = new BufferedReader(new InputStreamReader(fis))) {    String line = br.readLine();    System.out.println(line);} catch (IOException e) {    System.err.println("IO 异常: " + e.getMessage());}// 无需在 finally 中手动 close,资源自动释放

4. 自定义异常

java
public class BusinessException extends RuntimeException {    private Integer code;        public BusinessException(Integer code, String message) {        super(message);        this.code = code;    }        public Integer getCode() {        return code;    }}// 使用throw new BusinessException(40001, "用户名已存在");

5. 异常处理最佳实践

  • 不要捕获 Exception 或 Throwable:捕获具体的异常类型
  • 不要吞没异常:至少打印日志
  • 不要用异常控制业务流程:if-else 能解决的问题不要用 try-catch
  • 尽早抛出,延迟捕获:底层抛异常,上层统一处理
  • 在合适层级处理:Web 项目用 @ControllerAdvice(Spring Boot)统一处理

四、泛型与注解

1. 泛型基础

java
// 泛型类public class Box<T> {    private T content;        public void set(T content) {        this.content = content;    }        public T get() {        return content;    }}// 泛型方法public static <T> T getMiddle(T... args) {    return args[args.length / 2];}// 使用Box<String> stringBox = new Box<>();stringBox.set("Hello");String str = stringBox.get();  // 无需强转

2. 泛型擦除

Java 的泛型是编译期的,运行时会被擦除为原始类型:

java
// 编译时List<String> list1 = new ArrayList<>();List<Integer> list2 = new ArrayList<>();// 运行时(擦除后)// list1 和 list2 都是 ArrayList,类型信息丢失System.out.println(list1.getClass() == list2.getClass());  // true

影响:不能 new T()、不能用 instanceof T、不能创建泛型数组。

3. 通配符

java
// ? extends T — 上界通配符(读数据时用)List<? extends Number> list = new ArrayList<Integer>();Number n = list.get(0);     // 可以读,最大到 Number// list.add(1);             // ❌ 不能写,因为具体类型未知// ? super T — 下界通配符(写数据时用)List<? super Integer> list2 = new ArrayList<Number>();list2.add(1);               // 可以写 Integer// Integer i = list2.get(0); // ❌ 不能读,因为可能是 Object// ? — 无界通配符List<?> list3 = new ArrayList<String>();// list3.add("test");        // ❌ 不能写(null 除外)Object obj = list3.get(0);   // 只能读为 Object

PECS 原则:Producer Extends, Consumer Super(生产者用 extends,消费者用 super)。

4. 常用注解

java
// 编译时检查@Override          // 重写父类方法@SuppressWarnings  // 抑制警告@Deprecated        // 标记已弃用// Spring 常用@Component         // 通用组件@Autowired         // 依赖注入@Service           // 业务层@Repository        // 持久层@Controller        // 控制层@Configuration     // 配置类@Value             // 注入属性值@Transactional     // 事务管理// Lombok@Data              // 生成 getter/setter/toString/equals/hashCode@Slf4j             // 生成 log 对象@Builder           // 建造者模式@NoArgsConstructor // 无参构造@AllArgsConstructor// 全参构造

五、I/O 与 NIO

1. 字节流与字符流

流类型 抽象基类 常用实现 适用场景
字节输入流 InputStream FileInputStream 二进制文件(图片、音频)
字节输出流 OutputStream FileOutputStream 写入二进制文件
字符输入流 Reader FileReader、BufferedReader 文本文件
字符输出流 Writer FileWriter、BufferedWriter 写入文本文件
java
// 文本文件读取(推荐)try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))) {    String line;    while ((line = reader.readLine()) != null) {        System.out.println(line);    }}// 二进制文件复制try (FileInputStream in = new FileInputStream("source.jpg");     FileOutputStream out = new FileOutputStream("dest.jpg")) {    byte[] buffer = new byte[8192];    int len;    while ((len = in.read(buffer)) != -1) {        out.write(buffer, 0, len);    }}

2. BIO / NIO / AIO 对比

模型 全称 特点 适用场景
BIO Blocking I/O 同步阻塞,一个连接一个线程 连接数少、固定架构
NIO Non-blocking I/O 同步非阻塞,多路复用(Selector) 高并发、短连接
AIO Asynchronous I/O 异步非阻塞,回调机制 文件 I/O 密集型
java
// NIO 核心组件// Channel — 通道(FileChannel、SocketChannel)// Buffer — 缓冲区(ByteBuffer、CharBuffer)// Selector — 多路复用器// NIO 快速读取文件示例try (RandomAccessFile file = new RandomAccessFile("test.txt", "r");     FileChannel channel = file.getChannel()) {    ByteBuffer buffer = ByteBuffer.allocate(1024);    int bytesRead = channel.read(buffer);    while (bytesRead != -1) {        buffer.flip();  // 切换为读模式        while (buffer.hasRemaining()) {            System.out.print((char) buffer.get());        }        buffer.clear();  // 清空,准备下次写入        bytesRead = channel.read(buffer);    }}

六、多线程与并发

1. 线程创建方式

java
// 方式 1:继承 Threadclass MyThread extends Thread {    @Override    public void run() {        System.out.println("Thread running");    }}new MyThread().start();// 方式 2:实现 Runnable(推荐)class MyRunnable implements Runnable {    @Override    public void run() {        System.out.println("Runnable running");    }}new Thread(new MyRunnable()).start();// 方式 3:实现 Callable(有返回值)class MyCallable implements Callable<String> {    @Override    public String call() {        return "Callable result";    }}FutureTask<String> task = new FutureTask<>(new MyCallable());new Thread(task).start();String result = task.get();  // 获取返回值// 方式 4:Lambda 简化new Thread(() -> System.out.println("Lambda thread")).start();

2. synchronized 与 Lock

java
// synchronized — 隐式锁public class Counter {    private int count = 0;        // 同步方法    public synchronized void increment() {        count++;    }        // 同步代码块    public void method() {        synchronized (this) {            // 临界区        }    }}// Lock — 显式锁(更灵活)public class CounterWithLock {    private int count = 0;    private final Lock lock = new ReentrantLock();        public void increment() {        lock.lock();        try {            count++;        } finally {            lock.unlock();  // 必须在 finally 中释放        }    }}
对比 synchronized Lock
用法 关键字,自动加解锁 API,手动加解锁
锁释放 执行完自动释放 必须在 finally 中手动释放
可中断 ❌ ✅ lockInterruptibly()
超时 ❌ ✅ tryLock(timeout, unit)
公平锁 ❌ ✅ ReentrantLock(true)
条件变量 wait/notify Condition.await/signal

3. 线程池

java
// 推荐:通过 ThreadPoolExecutor 创建ThreadPoolExecutor executor = new ThreadPoolExecutor(    2,                    // corePoolSize — 核心线程数    5,                    // maximumPoolSize — 最大线程数    60L,                  // keepAliveTime — 空闲线程存活时间    TimeUnit.SECONDS,     // 时间单位    new LinkedBlockingQueue<>(100),  // 工作队列    Executors.defaultThreadFactory(), // 线程工厂    new ThreadPoolExecutor.AbortPolicy()  // 拒绝策略);// 用法executor.execute(() -> System.out.println("Task executed"));executor.shutdown();  // 关闭线程池// 不建议用 Executors 创建(阿里巴巴 Java 规范禁止)// Executors.newFixedThreadPool(10);      // ❌ 队列无限大,可能 OOM// Executors.newCachedThreadPool();       // ❌ 线程无限多// Executors.newSingleThreadExecutor();   // ❌ 同上

拒绝策略:

策略 行为
AbortPolicy(默认) 抛出 RejectedExecutionException
CallerRunsPolicy 提交任务的线程自己执行
DiscardPolicy 丢弃,不抛异常
DiscardOldestPolicy 丢弃最老的任务,再尝试提交

4. volatile 关键字

java
public class Flag {    private volatile boolean running = true;        public void stop() {        running = false;  // volatile 保证修改对其他线程立即可见    }        public void run() {        while (running) {            // 不加 volatile,此循环可能永远不会停止(指令重排)        }    }}

volatile 保证:

  • 可见性:一个线程修改,其他线程立即看到
  • 有序性:禁止指令重排
  • 不保证原子性:count++ 这种操作仍需要同步

5. ThreadLocal

每个线程有自己的独立副本,互不干扰:

java
// 常用于存储当前用户信息public class UserContext {    private static final ThreadLocal<User> currentUser = new ThreadLocal<>();        public static void set(User user) {        currentUser.set(user);    }        public static User get() {        return currentUser.get();    }        public static void remove() {        currentUser.remove();  // 必须清理,否则内存泄漏    }}

注意:使用完必须调用 remove() 清理,否则在线程池场景下会造成内存泄漏或数据错乱。


七、Java 8+ 新特性

1. Lambda 表达式

java
// 传统写法button.addActionListener(new ActionListener() {    @Override    public void actionPerformed(ActionEvent e) {        System.out.println("Clicked");    }});// Lambda 写法button.addActionListener(e -> System.out.println("Clicked"));// 多参数Comparator<String> comparator = (a, b) -> a.length() - b.length();

2. Stream API

java
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");// 链式操作List<String> result = names.stream()    .filter(name -> name.length() > 3)    // 过滤    .map(String::toUpperCase)              // 映射    .sorted()                              // 排序    .collect(Collectors.toList());         // 收集// 常用操作names.stream()    .filter(n -> n.startsWith("A"))    .forEach(System.out::println);    long count = names.stream()    .filter(n -> n.length() > 3)    .count();    boolean anyMatch = names.stream()    .anyMatch(n -> n.equals("Bob"));    // 分组Map<Integer, List<String>> grouped = names.stream()    .collect(Collectors.groupingBy(String::length));

3. Optional

java
// 传统写法 — 大量 null 检查public String getCity(User user) {    if (user != null) {        Address address = user.getAddress();        if (address != null) {            return address.getCity();        }    }    return "Unknown";}// Optional 链式写法public String getCity(User user) {    return Optional.ofNullable(user)        .map(User::getAddress)        .map(Address::getCity)        .orElse("Unknown");}

4. 日期时间 API

java
// 新的日期 API(线程安全,不可变)LocalDate today = LocalDate.now();                    // 2024-01-15LocalTime now = LocalTime.now();                      // 14:30:00LocalDateTime dateTime = LocalDateTime.now();          // 2024-01-15T14:30// 格式化DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");String formatted = dateTime.format(formatter);         // 2024-01-15 14:30:00LocalDateTime parsed = LocalDateTime.parse("2024-01-15 14:30:00", formatter);// 时间计算LocalDateTime tomorrow = dateTime.plusDays(1);LocalDateTime nextWeek = dateTime.plusWeeks(1);long hoursBetween = ChronoUnit.HOURS.between(dateTime, tomorrow);// 时间差Duration duration = Duration.between(LocalTime.now(), LocalTime.of(18, 0));Period period = Period.between(LocalDate.of(2024, 1, 1), LocalDate.now());

八、JVM 基础

1. 内存区域划分

区域 线程共享 存放内容 异常
堆(Heap) ✅ 对象实例、数组 OutOfMemoryError
方法区(Metaspace) ✅ 类信息、常量、静态变量 OutOfMemoryError
虚拟机栈(Stack) ❌ 局部变量表、操作数栈 StackOverflowError
本地方法栈 ❌ native 方法 StackOverflowError
程序计数器 ❌ 当前线程执行的字节码行号 无

JDK 8+ 变化:

  • 方法区从"堆内永久代" → "元空间(Metaspace)"
  • 元空间使用本地内存,不再受 JVM 堆大小限制

2. GC 算法

算法 原理 适用区域 特点
标记-清除 标记存活对象 → 清除未标记的 老年代 有碎片
标记-复制 将存活对象复制到另一半区域 新生代 无碎片,但浪费空间
标记-整理 标记存活 → 向一端移动 → 清理边界外 老年代 无碎片,但效率低
分代收集 新生代复制、老年代标记-整理 整个堆 综合最优

垃圾回收器对比:

回收器 适用代 算法 特点
Serial 新生代 复制 单线程,暂停所有用户线程
ParNew 新生代 复制 Serial 的多线程版本
Parallel Scavenge 新生代 复制 关注吞吐量
CMS 老年代 标记-清除 最短停顿时间
G1(JDK 9+默认) 全堆 分区 可预测停顿时间

3. 类加载机制

文本
加载 → 验证 → 准备 → 解析 → 初始化 → 使用 → 卸载       ↑ 连接阶段(双亲委派模型发生在加载阶段)

双亲委派模型:

文本
自定义类加载器    ↓ 委托应用程序类加载器(Application ClassLoader)    ↓ 委托扩展类加载器(Extension ClassLoader)    ↓ 委托启动类加载器(Bootstrap ClassLoader)

作用:保证核心类库的安全性(比如自己写的 java.lang.String 不会被加载)。


九、常用框架

1. Spring Boot 核心

概念 说明
IoC(控制反转) 对象创建权交给容器,由 Spring 管理 Bean 生命周期
DI(依赖注入) 容器自动注入依赖,不需要自己 new
AOP(面向切面编程) 将日志、事务、权限等横切关注点与业务逻辑分离
Bean Spring 管理的对象
自动配置 @EnableAutoConfiguration 自动装配
java
@SpringBootApplication  // 组合注解:@SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScanpublic class Application {    public static void main(String[] args) {        SpringApplication.run(Application.class, args);    }}// 分层结构@RestController          // 控制层@RequestMapping("/api")public class UserController {        @Autowired           // 依赖注入    private UserService userService;        @GetMapping("/users/{id}")    public Result<User> getUser(@PathVariable Long id) {        return Result.success(userService.getById(id));    }}

2. MyBatis

java
// Mapper 接口@Mapperpublic interface UserMapper {    @Select("SELECT * FROM user WHERE id = #{id}")    User selectById(Long id);        @Insert("INSERT INTO user(username, password) VALUES(#{username}, #{password})")    @Options(useGeneratedKeys = true, keyProperty = "id")    int insert(User user);        @Update("UPDATE user SET username = #{username} WHERE id = #{id}")    int update(User user);        @Delete("DELETE FROM user WHERE id = #{id}")    int deleteById(Long id);}

3. MyBatis-Plus(增强版)

java
// 继承 BaseMapper,内置 CRUD 方法@Mapperpublic interface UserMapper extends BaseMapper<User> {    // 无需写任何 SQL,自动拥有增删改查}// 使用@Servicepublic class UserService {    @Autowired    private UserMapper userMapper;        public User getById(Long id) {        return userMapper.selectById(id);    }        public Page<User> page(int page, int pageSize) {        return userMapper.selectPage(new Page<>(page, pageSize), null);    }}

4. 事务管理

java
@Servicepublic class OrderService {    @Autowired    private OrderMapper orderMapper;        @Transactional(rollbackFor = Exception.class)    public void createOrder(Order order) {        orderMapper.insert(order);                // 如果这里抛出异常,上面的 insert 会自动回滚        inventoryService.deductStock(order.getProductId(), order.getQuantity());    }}

十、常见面试题

Java 基础

Q1:== 和 equals() 的区别?

  • ==:比较引用(内存地址)或基本数据类型的值
  • equals():默认比较引用,但可以重写为比较内容(String、Integer 等已经重写)
java
String a = new String("hello");String b = new String("hello");a == b;          // false,不同对象a.equals(b);     // true,内容相同

Q2:String、StringBuilder、StringBuffer 的区别?

  • String:不可变,每次修改创建新对象
  • StringBuilder:可变,线程不安全,性能最好
  • StringBuffer:可变,线程安全(方法有 synchronized),性能稍低

Q3:final 关键字的作用?

  • final class:不能被继承
  • final method:不能被重写
  • final variable:值不可变(基本类型值不变,引用类型指向不变)

集合

Q4:HashMap 和 Hashtable 的区别?

对比 HashMap Hashtable
线程安全 ❌ ✅(方法有 synchronized)
允许 null key ✅ ❌
性能 高 低(同步开销)
迭代器 fail-fast fail-fast

Q5:ArrayList 扩容机制?

  • 默认容量 10
  • 每次扩容为原来的 1.5 倍(oldCapacity + (oldCapacity >> 1))
  • 通过 Arrays.copyOf() 将原数组复制到新数组

Q6:HashSet 怎么保证元素不重复?

  • 底层是 HashMap,元素作为 HashMap 的 key
  • 先算 hashCode() 定位桶,再用 equals() 判断是否已存在

多线程

Q7:sleep() 和 wait() 的区别?

对比 sleep() wait()
所属类 Thread 的静态方法 Object 的方法
是否释放锁 ❌ 不释放 ✅ 释放
唤醒方式 时间到自动唤醒 notify()/notifyAll()
使用前提 不需要 必须在同步块中

Q8:线程池的核心参数有哪些?

  • corePoolSize:核心线程数
  • maximumPoolSize:最大线程数
  • keepAliveTime:空闲线程存活时间
  • workQueue:任务队列
  • threadFactory:线程工厂
  • handler:拒绝策略

Q9:synchronized 的锁升级过程? 无锁 → 偏向锁 → 轻量级锁 → 重量级锁

  • 偏向锁:只有一个线程访问,减少 CAS 开销
  • 轻量级锁:少量线程竞争,自旋等待
  • 重量级锁:大量线程竞争,操作系统互斥量

JVM

Q10:Java 内存泄漏的场景?

  • 静态集合类持有对象引用未释放
  • 未关闭的资源(数据库连接、文件流)
  • ThreadLocal 使用后未 remove()
  • 内部类持有外部类的隐式引用
  • 缓存中的对象一直不被淘汰

Q11:OOM 的常见原因和处理?

  • 堆溢出:对象太多,增加 -Xmx
  • 栈溢出:递归太深,检查代码或增加 -Xss
  • 元空间溢出:类加载太多,增加 -XX:MaxMetaspaceSize
  • 直接内存溢出:NIO 使用不当

框架

Q12:Spring Boot 自动配置的原理?

  • @EnableAutoConfiguration 导入 AutoConfigurationImportSelector
  • 读取 META-INF/spring.factories 中的配置类
  • 通过 @ConditionalOnClass、@ConditionalOnProperty 等条件注解按需加载

Q13:@Autowired 和 @Resource 的区别?

对比 @Autowired @Resource
来源 Spring 注解 JSR-250 标准
装配方式 按 type 按 name
配合 可配合 @Qualifier 指定 name 属性

Q14:什么是循环依赖?Spring 怎么解决?

  • A 依赖 B,B 依赖 A,形成循环
  • Spring 通过三级缓存解决:
    1. 一级缓存:完整对象
    2. 二级缓存:早期对象(未完全初始化)
    3. 三级缓存:对象工厂

Q15:MyBatis 中 #{} 和 ${} 的区别?

  • #{}:预编译(参数化查询),防 SQL 注入
  • ${}:字符串拼接,有 SQL 注入风险,仅用于动态表名/列名

进阶

Q16:什么是不可变类?如何创建?

  • 创建后状态不可变(如 String、Integer)
  • 规则:class 用 final、所有字段 final、不提供 setter、构造器深度拷贝可变对象

Q17:序列化时 serialVersionUID 的作用?

  • 版本标识,确保反序列化时类版本一致
  • 不指定时 JVM 自动生成,但类变更后 UID 会变,导致反序列化失败

Q18:动态代理的原理?

  • JDK 动态代理:基于接口,Proxy.newProxyInstance()
  • CGLIB 动态代理:基于类继承,生成子类
  • Spring AOP 默认:有接口用 JDK,无接口用 CGLIB
java
// JDK 动态代理示例public class LogProxy implements InvocationHandler {    private Object target;        public LogProxy(Object target) {        this.target = target;    }        @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("调用前: " + method.getName());        Object result = method.invoke(target, args);        System.out.println("调用后: " + method.getName());        return result;    }        public static <T> T createProxy(T target, Class<T> interfaceType) {        return (T) Proxy.newProxyInstance(            target.getClass().getClassLoader(),            new Class[]{interfaceType},            new LogProxy(target)        );    }}

Q19:try { return } finally { } 的执行顺序?

  • finally 在 return 之前执行
  • 如果 finally 中有 return,会覆盖 try 中的 return
  • finally 修改基本类型的返回值不影响结果,修改引用类型会影响
java
public int test() {    int x = 1;    try {        return x++;  // 先保存 x=1 作为返回值,然后 x=2    } finally {        System.out.println(x);  // 输出 2        // return 100;          // 如果这里 return 100,会覆盖返回值    }}// 结果是 1,不是 2

Q20:Java 中如何实现单例模式?

java
// 饿汉式(线程安全)public class Singleton1 {    private static final Singleton1 INSTANCE = new Singleton1();    private Singleton1() {}    public static Singleton1 getInstance() { return INSTANCE; }}// 懒汉式(双重检查锁定)public class Singleton2 {    private static volatile Singleton2 instance;    private Singleton2() {}    public static Singleton2 getInstance() {        if (instance == null) {            synchronized (Singleton2.class) {                if (instance == null) {                    instance = new Singleton2();                }            }        }        return instance;    }}// 静态内部类(推荐)public class Singleton3 {    private Singleton3() {}    private static class Holder {        private static final Singleton3 INSTANCE = new Singleton3();    }    public static Singleton3 getInstance() { return Holder.INSTANCE; }}

十一、最佳实践

  1. 命名规范:类名大驼峰(UserService),方法名小驼峰(getUserById),常量全大写(MAX_COUNT)
  2. 阿里巴巴 Java 规范:禁止使用 Executors 创建线程池、谨慎使用魔法值、日志至少保留 15 天
  3. 异常处理:不捕获 Exception,不吞没异常,用自定义异常传递错误信息
  4. 集合初始化:指定初始容量避免频繁扩容(new HashMap<>(initialCapacity))
  5. 字符串拼接:循环中用 StringBuilder,不要在循环体里用 +
  6. 资源释放:用 try-with-resources,不要在 finally 里手动 close
  7. 判空:用 Optional 替代多层 if-null 判断
  8. 并发:用 ConcurrentHashMap 替代 HashMap + synchronized
  9. 线程池:手动创建 ThreadPoolExecutor,明确核心参数
  10. 日志:使用 SLF4J + Logback,占位符 {} 替代字符串拼接

十二、总结

Java 生态系统庞大而成熟,核心知识点可以归纳为三层:

  • 基础能力:OOP、集合、异常、泛型、I/O —— 日常编码的"建筑材料"
  • 进阶能力:多线程、JVM、Lambda/Stream —— 写出高效、可靠的代码
  • 框架能力:Spring Boot、MyBatis —— 企业级开发的"脚手架"

掌握这些核心知识,再加上良好的编码习惯,就能胜任绝大部分企业级 Java 开发工作。

前端、后端、部署、客服都是我一个人。写这里的东西都是真踩过的坑,不是抄来的教程。