Java核心技术精要:从基础到分布式系统
Java基础核心概念
JDK与JRE的区别
JDK(Java Development Kit)是完整的开发环境,包含编译器(javac)、调试工具、文档生成器及JRE。JRE(Java Runtime Environment)仅提供运行Java程序所需的虚拟机和核心类库。若仅需执行程序,安装JRE即可;开发则必须使用JDK。
== 与 equals 的本质差异
== 对基本类型比较数值,对引用类型比较内存地址。而 equals() 默认行为与 == 相同,但多数包装类(如String、Integer)重写了该方法以实现值比较。例如:
String a = "hello";
String b = new String("hello");
System.out.println(a == b); // false(不同对象)
System.out.println(a.equals(b)); // true(内容相同)
hashCode() 与 equals() 的关系
两个对象的 hashCode 相同,不代表 equals 为 true。哈希碰撞是正常现象。例如:
String s1 = "通话";
String s2 = "重地";
System.out.println(s1.hashCode() == s2.hashCode()); // true
System.out.println(s1.equals(s2)); // false
这是由于哈希算法的特性,不同字符串可能映射到相同哈希值。
final 关键字的三种用途
- 修饰类:禁止继承
- 修饰方法:禁止子类重写
- 修饰变量:成为常量,初始化后不可更改
Math.round(-1.5) 的结果
返回 -1。Java 中 round() 的规则是"四舍五入到最接近整数,0.5 向正无穷方向取整",因此 -1.5 向 -1 舍入。
String 是否为基础类型
不是。Java 基础类型共8种:byte, short, int, long, float, double, boolean, char。String 是 java.lang.String 类的实例,属于引用类型。
String、StringBuilder、StringBuffer 的区别
- String:不可变,每次修改生成新对象
- StringBuilder:可变,非线程安全,性能高
- StringBuffer:可变,线程安全,性能较低
在单线程环境下推荐使用 StringBuilder,多线程场景使用 StringBuffer。
字符串反转实现
使用 StringBuilder 或 StringBuffer 的 reverse() 方法:
String reversed = new StringBuilder("abcdefg").reverse().toString();
抽象类与接口
- 抽象类可包含构造器、成员变量、非抽象方法;接口只能有 public static final 常量和 public abstract 方法(Java 8 后可有 default 和 static 方法)
- 类只能继承一个抽象类,但可实现多个接口
- 抽象类用于"is-a"关系,接口用于"can-do"能力定义
集合框架深度解析
HashMap 底层原理
JDK 8 之后,HashMap 采用数组 + 链表 + 红黑树结构。当链表长度 ≥ 8 且数组容量 ≥ 64 时,链表转换为红黑树以提升查询效率。键的哈希值通过 hash(key.hashCode()) 运算后与数组长度减一进行位运算,确定存储位置。冲突时使用链表或红黑树解决。
HashSet 实现机制
HashSet 内部使用 HashMap 实现,所有元素作为键存储,值统一为一个静态对象 PRESENT。其唯一性由 HashMap 的键唯一性保证。
ArrayList 与 LinkedList 性能对比
- 随机访问:ArrayList O(1),LinkedList O(n)
- 插入/删除(非首尾):ArrayList O(n),LinkedList O(1)
- 内存占用:LinkedList 每节点额外存储前后指针,开销更大
高频读取选 ArrayList,高频增删选 LinkedList。
线程安全集合
- 传统:Vector、Hashtable
- 并发包:ConcurrentHashMap、CopyOnWriteArrayList、BlockingQueue
推荐使用 java.util.concurrent 包中的并发集合,性能优于同步包装类。
不可变集合创建
使用 Collections.unmodifiableList/set/map() 创建只读视图,任何修改操作抛出 UnsupportedOperationException。
多线程与并发控制
线程状态生命周期
NEW → RUNNABLE → BLOCKED → WAITING/TIMED_WAITING → TERMINATED
- BLOCKED:等待获取锁
- WAITING:调用 wait()/join()/park() 无限等待
- TIMED_WAITING:调用 sleep()/wait(timeout)/parkNanos() 等待指定时间
sleep() 与 wait() 的核心差异
| 特性 | sleep() | wait() |
|------|---------|--------|
| 来源类 | Thread | Object |
| 是否释放锁 | 否 | 是 |
| 唤醒方式 | 时间到自动唤醒 | 需 notify()/notifyAll() |
| 使用场景 | 简单延时 | 线程协作、生产者消费者 |
线程池创建方式
推荐使用 ThreadPoolExecutor 手动配置,避免使用 Executors 提供的快捷方法(如 newFixedThreadPool),因其使用无界队列易导致 OOM。
ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, 8, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),
new ThreadPoolExecutor.CallerRunsPolicy()
);
synchronized 锁升级机制
Java 6+ 引入锁优化:偏向锁 → 轻量级锁 → 重量级锁
- 偏向锁:无竞争时,锁直接绑定线程,避免CAS
- 轻量级锁:竞争出现时,使用CAS替换对象头指针,自旋等待
- 重量级锁:自旋失败后,线程挂起进入阻塞队列
该机制大幅降低无竞争场景下的同步开销。
volatile 与 synchronized 对比
- volatile:保证可见性与有序性,不保证原子性
- synchronized:保证原子性、可见性、有序性,但会阻塞线程
适用于状态标记(如 shutdownFlag)用 volatile,复合操作(如 i++)必须用 synchronized 或 Lock。
ThreadLocal 使用场景
为每个线程提供独立变量副本,典型用途:数据库连接、事务上下文、用户会话信息。注意:使用后需手动 remove() 避免内存泄漏。
网络与Web开发
HTTP 301 与 302 区别
- 301:永久重定向,搜索引擎更新索引
- 302:临时重定向,搜索引擎保留原URL
SEO优化推荐使用301。
Forward 与 Redirect
| 特性 | Forward | Redirect |
|------|---------|----------|
| 地址栏变化 | 否 | 是 |
| 数据共享 | 可共享 request | 不可共享 |
| 请求次数 | 1次 | 2次 |
| 性能 | 更高 | 较低 |
跨域解决方案
- CORS:服务端设置响应头 Access-Control-Allow-Origin
- JSONP:仅支持GET,通过动态script标签实现(已逐步淘汰)
- 代理服务器:Nginx反向代理统一域名
数据库与ORM
MySQL索引优化原则
- 遵循最左前缀原则
- 避免在索引列上使用函数或表达式
- 联合索引中,区分度高的字段放左侧
- 尽量使用覆盖索引(查询字段全在索引中)
InnoDB 与 MyISAM 对比
| 特性 | InnoDB | MyISAM |
|------|--------|--------|
| 事务支持 | 支持 | 不支持 |
| 锁粒度 | 行锁 | 表锁 |
| 外键 | 支持 | 不支持 |
| COUNT(*)性能 | 需全表扫描 | 快(保存行数) |
| 适用场景 | 高并发写入 | 读多写少 |
事务隔离级别
- READ UNCOMMITTED:脏读允许
- READ COMMITTED:避免脏读,允许可重复读
- REPEATABLE READ(MySQL默认):避免脏读和不可重复读,允许多幻读
- SERIALIZABLE:完全串行化,性能最低
Redis核心机制
缓存穿透解决方案
对查询不存在的数据,缓存一个空值(如 null)并设置短过期时间(如5分钟),防止恶意请求持续穿透。
缓存击穿解决方案
对热点数据使用互斥锁(Redis SETNX)或逻辑过期策略:设置一个稍长的过期时间,但缓存中额外保存一个"逻辑过期时间",当请求发现逻辑过期时,由一个线程异步刷新数据,其他线程仍返回旧值。
Redis持久化策略
- RDB:定时快照,恢复快,数据可能丢失
- AOF:记录写操作日志,数据更安全,文件大,恢复慢
推荐开启 AOF + RDB 双重机制。
Redis淘汰策略
- volatile-lru:过期键中选最近最少用
- allkeys-lru:所有键中选最近最少用(推荐)
- volatile-ttl:优先淘汰剩余时间最短的
- noeviction:拒绝写入(默认)
JVM内存与调优
堆内存结构
- 新生代:Eden : S0 : S1 = 8:1:1
- 老年代:存放长期存活对象
- 元空间(Metaspace):存放类元数据,使用本地内存
垃圾回收算法
- 标记-清除:产生碎片
- 标记-整理:消除碎片,但移动成本高
- 复制算法:适用于新生代,效率高但浪费一半空间
- 分代收集:主流策略,新生代用复制,老年代用标记-整理
常用JVM参数
- -Xms4g -Xmx4g:堆初始与最大大小一致,避免动态扩容
- -XX:NewRatio=3:老年代/新生代比例为3:1
- -XX:SurvivorRatio=8:Eden:S0:S1 = 8:1:1
- -XX:+UseG1GC:JDK9+默认GC,适合大堆低延迟场景
- -XX:+PrintGCDetails:输出详细GC日志
设计模式与框架
Spring Bean作用域
- singleton(默认):容器中唯一实例
- prototype:每次请求创建新实例
- request/session:Web环境下绑定请求或会话
- application:ServletContext级别共享
- websocket:WebSocket会话级别
AOP实现原理
基于动态代理:JDK代理(接口)或 CGLIB(继承类)。Spring通过切点表达式匹配目标方法,在执行前后织入通知逻辑,实现横切关注点(日志、事务、权限)的解耦。
MyBatis #{} 与 ${} 区别
- #{} :预编译参数,自动转义,防SQL注入
- ${} :字符串替换,直接拼接,易受注入攻击
推荐所有参数使用 #{} ,动态表名/列名等特殊情况谨慎使用 ${} 。
ZooKeeper在分布式系统中的作用
- 服务注册与发现(如Dubbo)
- 配置中心(动态推送)
- 分布式锁(临时顺序节点)
- Leader选举(ZAB协议)
并发工具类
CountDownLatch 与 CyclicBarrier
- CountDownLatch:倒计时器,一次性使用,等待N个线程完成
- CyclicBarrier:栅栏,可重用,等待N个线程到达后同时继续
CompletableFuture 异步编程
支持链式调用、组合、异常处理:
CompletableFuture.supplyAsync(() -> fetchData())
.thenApply(data -> transform(data))
.exceptionally(ex -> handle(ex))
.thenAccept(result -> System.out.println(result));

