Java面试复习 Day 1

📅 2026/7/2 15:14:00
Java面试复习 Day 1
一、大纲分类必考题JVM内存分区/垃圾回收算法/GC调优思路/类加载机制并发线程池7参数及工作流程/synchronized与ReentrantLock区别/volatile可见性集合HashMap 1.7/1.8区别及put流程/ConcurrentHashMap分段锁-CAS/ArrayList扩容机制MySQL索引数据结构(B树)/最左匹配/覆盖索引/事务隔离级别/MVCCRedis5种数据结构/缓存穿透击穿雪崩/持久化RDBAOF/过期删除SpringIOC容器启动流程/AOP代理方式/事务传播机制基础和equals/重载重写/接口抽象类/String immutable二、基础1.和equals是运算符基本类型比值、引用类型比地址equals()是方法默认比地址但常用类已重写比内容。基本类型基本数据类型int、char、boolean等只能用比较直接比两个变量的实际值是否相等equals()无法用于基本类型。对象引用类型用比较的是两个引用是否指向堆内存中同一个对象即内存地址是否相同。默认和重写equals()默认继承自Object类默认行为与相同比地址但String、Integer等常用类已重写equals()此时比较的是对象内容是否相等。equals重写标准步骤必须遵循的五大契约重要注意事项同时重写hashCode()‌规则‌如果两个对象通过equals()判定为相等那么它们的hashCode()方法必须返回相同的整数值。‌原因‌Java 中的哈希集合如HashMap,HashSet首先通过hashCode()确定对象存储的位置桶然后再通过equals()确认对象是否真正相等。如果equals()相等但hashCode()不同集合将无法找到已存在的对象导致数据重复或查找失败。‌建议‌在使用 IDE如 IntelliJ IDEA 或 Eclipse时通常可以一键生成配套的equals()和hashCode()方法以确保逻辑的一致性。常见误区‌不要只比较部分字段‌如果两个对象被视为“相等”它们的所有关键业务字段都应该参与比较。‌慎用instanceof‌在存在继承关系时如果使用instanceof进行类型检查可能会破坏对称性。例如父类对象equals子类对象可能为true但子类对象equals父类对象可能为false如果子类增加了新字段。使用getClass()可以避免这个问题但意味着子类对象永远不等于父类对象即使父类字段完全相同。具体选择取决于业务需求。‌基本类型不能用equals‌int,double等基本数据类型没有equals方法必须使用。如果需要比较包装类如Integer可以使用Objects.equals()来安全地处理null。2.重载重写方法的重载Overloading和重写Overriding是面向对象编程中的两个核心概念它们允许开发者根据不同的需求和场景使用相同的方法名但两者间存在一些区别。方法的重载重载是指在同一个类中可以存在多个同名方法只要它们的参数列表不同参数的类型、数量或者顺序不同。返回值类型不是区分重载方法的依据。方法的重写重写也称为覆盖指的是在子类中可以定义一个与父类中具有相同方法名和参数列表的方法。子类方法会覆盖父类中的方法。重写时方法的返回类型、方法名和参数列表必须完全相同。访问修饰符的限制也更严格子类方法不能有更严格的访问权限。总结重载允许你创建多个同名方法只有它们的参数列表不同。重写允许你提供一个方法的新实现该方法在子类中替换父类中的方法。重载是编译时多态性静态多态性重写是运行时多态性动态多态性。重载使用相同的类内部重写在继承体系中从父类到子类之间进行。3.接口抽象类在Java中接口Interface和抽象类Abstract Class都是用于定义方法和属性的模板但是它们在设计和使用上有一些关键的区别。接口Interface1. 定义接口是完全抽象的它只包含抽象方法即没有方法体的方法和常量使用public static final修饰的字段默认即是如此。2. 实现一个类可以实现多个接口。3. 访问修饰符接口中的方法默认是public的而变量默认是public static final的。4. 示例public interface Animal { void eat(); void sleep(); }5. 实现接口public class Dog implements Animal { public void eat() { System.out.println(Dog is eating); } public void sleep() { System.out.println(Dog is sleeping); } }抽象类Abstract Class1. 定义抽象类可以包含抽象方法没有方法体的方法和具体方法有方法体的方法。它还可以包含字段、构造器等。2. 继承一个类只能继承一个抽象类。3. 访问修饰符方法和字段可以有不同的访问修饰符。4. 示例public abstract class Animal { public abstract void eat(); public void sleep() { System.out.println(Animal is sleeping); } }5. 继承抽象类public class Dog extends Animal { public void eat() { System.out.println(Dog is eating); } }区别总结- 继承一个类只能继承一个抽象类但可以实现多个接口。- 方法实现接口只包含抽象方法抽象类可以包含抽象方法和具体方法。- 字段接口中的字段默认是public static final而抽象类可以包含任意类型的字段。- 设计用途接口主要用于定义一套方法规范让不同的类去实现抽象类主要用于代码复用提供一个部分实现的基类。- 访问修饰符接口中的方法默认是public而抽象类中的方法和字段可以有不同的访问级别。选择使用接口还是抽象类- 当需要定义一套规范让多个类去实现时使用接口。- 当需要定义一个基类让其他类去继承并可能添加一些非抽象的方法实现时使用抽象类。例如当你有一个通用行为和一些通用属性但又希望某些特定行为由子类实现时可以使用抽象类。4.String immutable4.1含义Java String 的不可变性Immutable指对象创建后其内部字符序列无法被修改。任何看似“修改”的操作如拼接、替换实际都是返回新对象原对象保持不变 。核心实现机制- final 类String 被声明为 final禁止继承防止子类破坏不可变契约 。- final 字段底层存储数组JDK 8 前为 char[]JDK 9 为 byte[]被 private final 修饰引用地址不可变且未暴露修改方法 。- 防御性拷贝构造函数接收外部数组时会创建副本切断外部引用对内部数据的修改路径 。- 无 Setter 方法类中不提供任何能改变内部状态的方法所有操作均返回新实例 。设计不可变的核心优势- 安全性作为网络参数、文件路径、数据库连接串等敏感信息的载体不可变防止数据在传递中被恶意篡改保障类加载安全 。- 线程安全多线程并发访问无需同步锁天然避免竞态条件提升并发性能 。- 支持字符串常量池不可变是常量池存在的前提相同字面量共享同一实例大幅节省内存并优化 比较逻辑 。- 哈希值缓存hashCode() 结果首次计算后缓存于 private int hash 字段因内容不变后续直接返回缓存值显著提升 HashMap/HashSet 作为 Key 的性能 。常见误区澄清- 变量指向可变 ≠ 对象可变s s x 是让引用 s 指向新对象原 abc 对象并未改变 。- 反射可突破限制虽可通过反射修改 value 数组内容但这违反封装原则官方 API 不提供此类接口开发中应视为不可变 。- 频繁拼接需换工具循环中用 拼接会生成大量临时对象应改用 StringBuilder非线程安全或 StringBuffer线程安全 。4.2.Java中String为什么不可变Java 中String被设计为‌不可变Immutable‌是 Java 语言在‌安全性、性能优化、线程安全‌以及‌系统设计一致性‌之间做出的核心权衡。这种设计并非偶然而是为了支撑 JVM 底层机制和上层应用的高效运行。4.3.String 设计为不可变的四大核心原因1. 支持字符串常量池String Pool节省内存这是 String 不可变最直接的收益。JVM 在堆内存中开辟了一块特殊区域称为‌字符串常量池‌。‌机制‌当使用字面量创建字符串如String s abc;时JVM 会先检查常量池中是否已存在该字符串。如果存在直接返回引用如果不存在则创建新对象并放入池中。‌为什么必须不可变‌如果 String 是可变的多个变量指向同一个常量池对象时其中一个变量修改了内容会导致其他所有指向该对象的变量值发生“意外”改变造成数据污染和逻辑错误。‌收益‌大量重复的字符串如配置项、状态码只需存储一份极大节省了堆内存空间。2. 安全性保障SecurityString 在 Java 中被广泛用于存储敏感信息和关键参数不可变性是防止恶意篡改的基础。‌敏感数据保护‌用户名、密码、数据库连接 URL、网络请求参数等通常以 String 形式传递。如果 String 可变黑客或恶意代码可能在参数传递过程中修改其内容例如将指向合法服务器的 URL 改为恶意服务器导致严重的安全漏洞。‌类加载安全‌JVM 的类加载器通过字符串全限定名如java.lang.String来定位和加载类文件。如果类名字符串可被修改可能导致加载错误的类破坏双亲委派模型和沙箱安全机制。‌反射与网络操作‌许多底层 API如打开文件、网络连接接收 String 参数。不可变性确保了方法在执行前和执行期间参数值保持一致防止竞态条件下的参数篡改。3. 天然的线程安全Thread Safety‌无锁并发‌不可变对象的状态在创建后无法改变。因此多个线程可以同时读取同一个 String 实例而无需担心数据不一致或竞态条件。‌简化开发‌: 在多线程环境下共享 String 对象时不需要额外的同步措施如synchronized或锁降低了并发编程的复杂度和性能开销。4. 哈希值缓存提升集合性能String 是HashMap、HashSet、HashTable等哈希集合中最常用的 Key。‌哈希缓存机制‌String 类内部有一个private int hash字段。由于内容不可变hashCode()在首次计算后会将结果缓存起来。后续调用直接返回缓存值无需重新遍历字符数组计算。‌保证集合稳定性‌如果 String 可变作为 Key 存入 HashMap 后若内容被修改其hashCode也会变化导致无法在原桶位置找到该对象造成“数据丢失”实际上是被映射到了错误的桶中。不可变性彻底杜绝了这一问题。4.4.String不可变的实现原理Java 通过以下三重机制在代码层面强制保证了不可性1‌.final类声明‌public final class String ...String类被声明为final禁止被继承。这防止了子类通过重写方法来破坏不可变契约。2.private final底层存储数组‌‌JDK 8 及之前‌使用private final char value[]存储字符。‌JDK 9 及之后‌优化为private final byte[] value配合编码标识符进一步节省内存。final修饰保证了数组的‌引用地址‌不可变private修饰保证了外部无法直接访问该数组。3.防御性拷贝与无 Setter 方法‌String 类不提供任何修改内部value数组的方法如setCharAt。所有看似“修改”的方法如substring,concat,replace实际上都创建了‌新的 String 对象‌并返回原对象保持不变。在构造函数中如果传入的是外部数组String 会进行‌防御性拷贝‌Copy切断外部引用对内部数据的潜在修改路径。注意虽然通过 Java 反射机制可以强行修改String内部的value数组但这违反了封装原则和安全规范在实际开发中应严格避免。从 API 设计和语言规范角度看String 就是不可变的。4.5.总结String 的不可变性是以‌牺牲少量创建新对象的开销‌为代价换取了‌内存效率常量池、执行速度Hash缓存、并发安全无锁和系统稳健性防篡改‌的巨大收益。在Effective Java 等经典著作建议“‌只要可能就使用不可变对象‌”。