1. HashMap 线程安全/不安全 & List 线程安全

核心考察点:集合框架的并发特性、线程安全实现方案。

HashMap 线程安全
  • 不安全场景:JDK 1.7及之前,多线程 put 可能导致环形链表(扩容时头插法导致死循环);JDK 1.8虽修复死循环,但仍可能因并发修改导致数据覆盖(如两个线程同时判断桶为空并插入)。
  • 线程安全替代
    • ConcurrentHashMap(推荐):JDK 1.8用 CAS + synchronized 实现,锁粒度细(仅锁当前桶),支持高并发;JDK 1.7用分段锁(Segment数组,默认16段,每段独立加锁)。
    • Hashtable:全表锁(synchronized修饰方法),效率低,已被淘汰。
List 线程安全
  • 不安全ArrayListLinkedList 均未实现同步,多线程增删改可能抛 ConcurrentModificationException(快速失败机制)。
  • 安全替代
    • Vector:全表锁(synchronized修饰方法),效率低。
    • Collections.synchronizedList(new ArrayList<>()):包装类,通过 synchronized 修饰所有方法,本质是“伪线程安全”(复合操作仍需额外同步,如 if(list.isEmpty()) list.add())。
    • CopyOnWriteArrayList(推荐):写时复制(读无锁,写时复制新数组),适合读多写少场景(如配置缓存),缺点是内存占用高、数据一致性弱(最终一致)。

2. HTTPS TLS 加密流程

核心考察点:网络安全基础、TLS握手机制。

TLS 握手核心步骤(以TLS 1.2为例):
  1. Client Hello:客户端发送支持的TLS版本、加密套件(如 ECDHE-RSA-AES256-GCM-SHA384)、随机数 ClientRandom
  2. Server Hello:服务端选择TLS版本和加密套件,返回随机数 ServerRandom,并发送证书(含公钥)。
  3. 证书验证:客户端验证证书合法性(通过CA链校验签名、有效期、域名匹配)。
  4. 密钥交换
    • 若用 RSA密钥交换:客户端用服务端公钥加密 PreMasterSecret(随机数)发送给服务端,双方通过 ClientRandom + ServerRandom + PreMasterSecret 生成会话密钥
    • 若用 ECDHE(前向安全):双方通过椭圆曲线算法交换临时公钥,生成 PreMasterSecret(即使私钥泄露,历史通信仍安全)。
  5. 加密通信:双方用会话密钥(对称加密,如AES)加密后续数据,通过 Finished 消息验证握手完整性。

3. Java 常用加密算法

核心考察点:加密算法分类、实际项目应用。

常见算法
类型 算法 特点与应用场景
对称加密 AES(推荐)、DES(淘汰)、3DES 速度快,适合大数据加密;如接口参数加密、数据库敏感字段存储(需管理密钥)。
非对称加密 RSA(2048位+)、ECC(椭圆曲线) 公钥加密私钥解密,适合密钥交换、签名;如JWT令牌签名、HTTPS证书验证。
哈希算法 SHA-256、MD5(不安全)、BCrypt 不可逆,用于密码存储(BCrypt自带盐值,防彩虹表攻击)、数据完整性校验。
实战示例
  • 密码存储:用 BCrypt.hashpw(password, BCrypt.gensalt()) 加密,避免明文或MD5(易被破解)。
  • 接口签名:用 HmacSHA256 对请求参数+时间戳+密钥生成签名,防篡改。

4. OpenFeign 远程调用实现原理

核心考察点:声明式HTTP客户端、动态代理、负载均衡。

核心流程
  1. 接口定义:通过 @FeignClient(name = "service-name") 注解标记接口,方法用 @GetMapping 等定义HTTP请求。
  2. 动态代理:启动时,OpenFeign扫描接口,通过 JDK动态代理 生成代理类(FeignInvocationHandler)。
  3. 请求模板:代理类将方法参数、注解解析为 RequestTemplate(包含URL、Header、Body)。
  4. 负载均衡:集成 Ribbon(或Spring Cloud LoadBalancer),从注册中心(如Nacos/Eureka)获取服务实例列表,选择一个实例(如轮询、随机)。
  5. HTTP调用:通过底层客户端(默认 URLConnection,可切换为 OkHttp/HttpClient)发送请求,解码响应(如JSON转对象)。

5. 接口加载慢(多表查询)的优化方案

核心考察点:全栈性能优化思维(前端+后端+数据库)。

前端优化
  • 分页/懒加载:避免一次性加载全量数据(如表格分页、滚动触底加载)。
  • 缓存:用 localStorage 缓存静态数据(如字典表),设置过期时间。
  • 骨架屏/占位符:减少用户等待焦虑(如Ant Design的 Skeleton 组件)。
后端优化
  • SQL优化
    • 避免 SELECT *,只查必要字段;
    • 给关联字段(JOIN条件)、查询条件(WHERE)建索引(用 EXPLAIN 分析执行计划);
    • 拆分大查询:将多表 JOIN 拆分为多次单表查询(减少锁竞争,适合MySQL)。
  • 缓存热点数据:用 Redis 缓存查询结果(如用户信息、配置项),设置合理过期时间(防缓存雪崩)。
  • 异步处理:非核心逻辑(如日志记录、通知)用 @Async 异步执行,缩短主流程耗时。
  • 数据库连接池:调优 HikariCP 参数(如 maximum-pool-size 匹配CPU核心数)。
数据库优化
  • 读写分离:主库写,从库读(用MyCat/Sharding-JDBC实现),减轻主库压力。
  • 分库分表:若单表数据超千万,按业务维度(如用户ID哈希)分表。

6. StringBuffer vs StringBuilder

核心考察点:字符串可变类、线程安全与性能。

特性 StringBuffer StringBuilder
线程安全 线程安全(synchronized修饰方法) 非线程安全
性能 低(锁竞争开销) 高(无锁)
适用场景 多线程环境(如全局日志拼接) 单线程环境(如方法内字符串拼接)

注意String 是不可变类(每次拼接生成新对象),频繁拼接时用 StringBuilder(单线程)或 StringBuffer(多线程),避免产生大量临时对象。

7. try-return-finally 执行顺序

核心考察点:异常处理流程、finally块特性。

结论
  1. try 中无异常:先执行 try 代码块,再执行 finally 代码块,最后执行 try 中的 return
  2. try 中有异常:跳过 try 剩余代码,执行 catch 块,再执行 finally,最后执行 catch 中的 return(若有)。
  3. finally 中有 return覆盖 try/catch 中的 return(不建议这样写,会破坏异常传播)。
示例
public static int test() {  
    try {  
        return 1;  
    } finally {  
        return 2; // 最终返回2,try中的return 1被覆盖  
    }  
}  

回答技巧

  • 结合项目:每个问题尽量关联自己的实战经验(如“我在优化XX接口时,用Redis缓存了多表查询结果,响应时间从2s降到200ms”)。
  • 突出重点:面试官关注“你如何解决实际问题”,而非死记概念(如讲TLS时,强调“前向安全”的重要性,而非背步骤)。
  • 承认边界:不确定的点可以说“实际项目中我主要用ConcurrentHashMap,因为XX场景…”,避免硬编。

按这个逻辑回答,既能体现技术深度,又能展示落地能力,面试通过率会大幅提升! 😊

更多推荐