Object 类是 Java 中所有类的祖先(根类)。面试中主要考察其中的核心方法以及与 String 相关的延伸问题。
1. Object 类有哪些常见方法?
面试官常让你列举 5-6 个,并问其中的细节。
getClass(): 获取对象的运行时类(反射入口)。- 它返回对象运行时的实际类对象,和编译时类型可能不同,而且这个方法不能重写。比如父类 Animal 的子类 Dog,Animal animal = new Dog,animal.getClass 返回的是 Dog 的类对象,
hashCode(): 获取对象的哈希码(用于 HashMap 等)。equals(Object obj): 比较两个对象是否相等(默认比较地址)。clone(): 创建并返回对象的一个副本(默认浅拷贝)。2. 如何实现深拷贝?toString(): 返回对象的字符串表示(默认是类名@哈希码)。wait(): 让当前线程等待(释放锁)。- 同样需要在 synchronized 同步块或方法中使用,否则会抛 IllegalMonitorStateException.
notify()/notifyAll(): 唤醒等待的线程。- 比如生产者消费者模式中,生产者生产完数据后调用 notifyAll,唤醒等待的消费者线程。
finalize(): 垃圾回收前被调用(已不推荐使用)。
2** == 和 equals 的区别?(必考)
==(操作符):- 基本数据类型:比较值是否相等。
- 引用数据类型:比较内存地址是否相等(是不是同一个对象)。
equals(方法):- 默认行为(Object类):等价于
==,比较内存地址。 - 重写后(如 String, Integer):比较对象的内容是否相等。
- 面试话术: “
==对于引用类型比较的是地址,而equals默认也是比较地址,但通常我们会重写它来比较对象的内容,比如String就重写了equals来比较字符串里的字符。”StringBuilder / StringBuffer没重写哦
- 默认行为(Object类):等价于
3.** hashCode 和 equals 的关系?(高频死穴)
面试官问:“为什么重写 equals 必须重写 hashCode?” 所谓“重写 hashCode”的意思是: 我们废弃掉 Java 原生那个“按内存地址生成整数”的逻辑,改为**“按对象的属性(比如 ID、名字)来生成整数”。 目的: 确保逻辑上相等的两个对象(比如 ID 都是 1 的两个 User 对象),在 HashMap 中能算出相同的索引位置**,从而能正确地存取数据。 这就是为什么面试官总是说:“重写 equals 时必须重写 hashCode”。如果不重写,你的对象在 HashMap 里就是个“隐形人”,存进去就再也找不到了。
- 规定:
- 如果两个对象 equals() 为 true,那么它们的 hashCode() 必须相等。
- 如果两个对象 hashCode() 相等,它们 equals() 不一定相等(这就叫哈希冲突)。
- 为什么必须同时重写?
- 这是为了配合 HashMap / HashSet 等散列表集合使用。
- HashMap 存取数据是先算
hashCode确定桶的位置,再用equals比较 key 是否相同。 - 后果: 如果只重写
equals不重写hashCode,会导致“逻辑上相等”的两个对象(比如两个内容一样的 User 对象)生成的哈希码不同,被 HashMap 存到了不同的坑里,导致无法根据 key 取出 value,或者 HashSet 存入了重复数据。
class Student {
String name;
Student(String name) { this.name = name; }
// 只重写了 equals,没重写 hashCode
public boolean equals(Object o) {
Student s = (Student) o;
return this.name.equals(s.name);
}
}
// 测试
HashMap<Student, String> map = new HashMap<>();
Student s1 = new Student("小林");
map.put(s1, "这是小林的数据");
Student s2 = new Student("小林"); // 新建一个一模一样的对象
System.out.println(map.get(s2)); // 输出 null (找不到!)
@Override
public int hashCode() {
// 只要名字一样,算出来的整数就一定一样
return Objects.hash(name);
}
**结果:** `map.get(s2)` 就能成功输出 "这是小林的数据"。4. String 相关面试题
A. String 为什么是不可变的 (Immutable)?
- 底层实现: String 内部使用
private final char[]数组存储字符(JDK9 后改为byte[]),且没有暴露修改数组的方法。 - 安全性: 保证 HashMap 的 Key 不会被修改,保证线程安全,保证字符串常量池的运作。
B. String, StringBuffer, StringBuilder 的区别?
| 特性 | String | StringBuilder | StringBuffer |
|---|---|---|---|
| 可变性 | 不可变 | 可变 | 可变 |
| 线程安全 | 安全 (常量) | 不安全 (快) | 安全 (因为加了 synchronized) |
| 性能 | 差 (拼接产生新对象) | 最快 | 中等 |
| 适用场景 | 少量的字符串操作 | 单线程大量拼接 (最常用) | 多线程大量拼接 |
C. intern() 方法有什么用?
- 将字符串对象手动加入字符串常量池。
- 如果常量池中已经有了这个字符串,就直接返回常量池里的引用;如果没有,就放进去再返回。
5. Java 里 string的常用方法有哪些?
1. 比较与判断 (最常用)
equals(Object anObject): 比较两个字符串的内容是否完全相同(区分大小写)。equalsIgnoreCase(String anotherString): 比较内容是否相同(忽略大小写)。startsWith(String prefix): 判断是否以指定字符串开头。endsWith(String suffix): 判断是否以指定字符串结尾。contains(CharSequence s): 判断是否包含某个子串。isEmpty(): 判断是否为空字符串""(长度为0)。- 注意: 开发中常用
StringUtils.isBlank()(Apache工具类) 来同时判断 null 和空字符串。
- 注意: 开发中常用
2. 获取与查找
length(): 返回字符串的长度。charAt(int index): 获取指定索引位置的字符。indexOf(String str): 查找子串第一次出现的索引(找不到返回 -1)。lastIndexOf(String str): 查找子串最后一次出现的索引。substring(int beginIndex): 截取从指定位置开始到末尾的子串。substring(int beginIndex, int endIndex): 截取指定范围的子串(包头不包尾,即[begin, end))。
3. 转换与替换
toLowerCase(): 转为全小写。toUpperCase(): 转为全大写。trim(): 去除字符串首尾的空格。- JDK 11 新特性:
strip()(能去除更多种类的空白字符)。
- JDK 11 新特性:
replace(CharSequence target, CharSequence replacement): 替换所有匹配的子串。- 注意:
replace也是替换所有,不是替换第一个。
- 注意:
replaceAll(String regex, String replacement): 基于正则表达式替换所有匹配项。split(String regex): 根据分隔符(支持正则)将字符串拆分成String[]数组。toCharArray(): 将字符串转换为字符数组char[]。getBytes(): 将字符串编码为字节数组byte[](网络传输、IO常用)。
4. 格式化与静态方法
String.format(String format, Object... args): 类似 C 语言的printf,用于格式化字符串(如%s,%d)。String s = String.format("Hi, %s", "Tom");
String.valueOf(Object obj): 将各种数据类型转为字符串(推荐用这个,因为它处理了 null,不会报空指针,而是返回字符串 “null”)。
6.总结表格
| 知识点 | 核心回答要点 |
|---|---|
| Object 方法 | 重点掌握 equals, hashCode, wait, notify |
| == vs equals | 地址 vs 内容 (重写后) |
| Hash 契约 | equals 相等则 hash 必相等;hash 相等 equals 未必 |
| String 三兄弟 | String 不可变;Builder 快但不安全;Buffer 慢但安全 |