全套面试题精简背诵版(面试官口语化作答,简洁踩分)
1. 题目:String、StringBuffer、StringBuilder 三者区别
面试回答
- 可变性:String 不可变,底层 final 字符数组,修改字符串会生成新对象;StringBuffer 和 StringBuilder 底层字符数组可变,修改不会频繁创建对象,适合频繁拼接。
- 线程安全:StringBuffer 方法加了 synchronized 同步锁,多线程安全,性能较差;StringBuilder 没有加锁,线程不安全,单线程性能最优。
- 使用场景:少量字符串操作用 String;多线程拼接用 StringBuffer;单线程大量拼接优先用 StringBuilder。
2. 题目:String 为什么要设计为不可变
面试回答
- 支持字符串常量池,实现字符串复用,节约内存;若可变,一处修改会导致所有引用全部变动。
- hashCode 值可以缓存,存入 HashMap、HashSet 不用重复计算哈希,提升哈希集合效率。
- 天然线程安全,多线程并发读取无需加锁,不会出现并发修改异常。
- 保证参数安全,常作为类加载路径、密码、连接地址,不可变避免恶意篡改。
3. 题目:String str = new String("aa")一共创建几个对象
面试回答分两种情况:
- 常量池没有
aa:创建2 个对象。常量池生成 "aa",堆内存 new 一个 String 对象;栈中 str 只是引用,不算对象。 - 常量池已经存在 "aa":只在堆中创建 1 个对象,直接复用常量池字符串。
4. 题目:String a = "ab"; String b = "a"+"b";,a==b 结果?原因?
面试回答结果为 true。 因为编译期优化,两个都是字符串字面常量,编译器直接把"a"+"b"编译成"ab",a 和 b 都指向字符串常量池同一个地址,地址相等,所以 == 为 true。 补充:如果是变量相加,比如String m="a";b=m+"b",无法编译优化,会在堆新建对象,== 结果是 false。
5. 题目:Java 动态代理如何实现
面试回答Java 有两种动态代理,均在运行期动态生成代理类,不用手动编写代理代码。
- JDK 动态代理:依托
Proxy和InvocationHandler实现,要求目标类必须实现接口。运行时生成实现目标接口的代理类;调用代理方法会进入 invoke 方法,在这里完成原有方法调用和额外逻辑增强。 - CGLIB 动态代理:依靠 ASM 字节码框架,不需要目标类实现接口,运行时继承目标类生成子类当作代理;重写目标类非 final 方法做功能增强。 日常 Spring AOP 默认:有接口用 JDK 代理,无接口用 CGLIB。
6. 题目:String 靠哪些机制实现不可变
面试回答(四点踩分)
- 存储字符的 value 数组被 private 修饰,外部无法直接访问修改数组。
- value 数组加 final,数组引用地址不能更改,无法指向新数组。
- String 类被 final 修饰,不能被继承,避免子类重写方法破坏不可变。
- 所有修改类方法(substring、replace、concat 等)都不会改动原数组,全部返回全新 String 对象。
7. 题目:final、finally、finalize 三者区别
面试回答
- final 是修饰关键字:修饰类不能继承;修饰方法子类不能重写;修饰基本变量值不可改,修饰引用变量地址不可变。
- finally 是 try-catch 配套代码块:绝大多数情况一定会执行,常用来关闭流、数据库连接释放资源;只有调用 System.exit 退出 JVM、机器宕机不会执行。即便 try 中有 return,也会先执行 finally 再返回。
- finalize 是 Object 的 protected 方法:对象被 GC 回收前会被调用,用于回收资源;执行时机完全不可控,Java9 开始废弃,开发禁止使用。
8. 题目:既然有 equals,为什么还需要 hashCode 方法
面试回答为了适配 HashMap、HashSet 等哈希容器,二者分工不同:
- hashCode 负责算出哈希值,划分哈希桶,快速定位元素位置,把大范围查找缩小到单个桶,保证查询效率 O (1)。
- 哈希会出现碰撞,多个对象 hashCode 相同;此时靠 equals 精准对比内容,判断对象是否真正相等。 硬性规范:equals 返回 true 的两个对象,hashCode 必须一致;重写 equals 必须重写 hashCode。只靠 equals 效率极低,只靠 hashCode 无法区分碰撞对象。