在 Java 的世界里,Object 类是一切类的始祖。理解它的核心方法,掌握 String 及其相关类的特性,是每一个 Java 开发者从入门到精通的必经之路。本文将系统地梳理 Object 类的 11 个核心方法,深入探讨 equals 与 hashCode 的约定,并全面对比 String、StringBuffer 与 StringBuilder 的区别。
一、Object 类有哪些核心方法?
Java Object 类是所有类的超类,默认提供 11 个核心方法,涵盖了对象比较、哈希、字符串表示、线程同步等方面。
1. equals(Object obj)
-
作用:比较两个对象是否相等。
-
说明 :默认实现比较内存地址(与
==相同)。实际开发中通常需要重写,按对象的内容(如字段值)判断相等性。
2. hashCode()
-
作用:返回对象的哈希码值。
-
说明 :与
equals配套使用。若两个对象equals返回true,则hashCode必须相等;若hashCode不等,则equals必为false。如果只重写equals不重写hashCode,会导致对象在HashMap、HashSet等集合中无法正确存储。
3. toString()
-
作用:返回对象的字符串表示。
-
说明 :默认格式为"类名 + @ + 对象的十六进制哈希码"(如
User@1b6d3586),可读性差。通常重写为返回对象的具体信息,便于日志打印和调试。
4. getClass()
-
作用:返回对象运行时的实际类对象。
-
说明 :
final方法,不可重写。常用于反射,获取类的名称、方法、字段等信息,也可用于运行时类型检查。
5. clone()
-
作用:创建并返回对象的一个副本。
-
说明 :用于对象的浅拷贝。使用
clone需要实现Cloneable接口,否则抛出CloneNotSupportedException。
6. notify()
-
作用:随机唤醒一个等待当前对象锁的线程。
-
说明:必须在同步块中使用。
7. notifyAll()
-
作用:唤醒所有等待当前对象锁的线程。
-
说明:必须在同步块中使用。
8. wait()
-
作用 :让当前线程释放对象锁并进入等待状态,直到被
notify/notifyAll唤醒。 -
说明:必须在同步块中调用,通常放在循环中检查等待条件以避免"虚假唤醒"。
9. wait(long timeout)
-
作用:让当前线程等待指定的毫秒数,超时后自动唤醒。
-
说明 :
timeout为 0 表示无限等待。
10. wait(long timeout, int nanos)
-
作用:提供更精确的时间控制(毫秒 + 纳秒)。
-
说明:实际超时时间可能受系统调度影响。
11. finalize()
-
作用:垃圾回收器回收对象前调用,用于执行清理工作。
-
说明 :已过时(Deprecated since Java 9) 。执行时机不确定,可能影响性能,甚至导致对象复活。替代方案是使用
try-with-resources或PhantomReference。
二、== 与 equals 有什么区别?
-
==:对于基本数据类型,比较的是值是否相同;对于引用数据类型,比较的是引用地址是否相同。
-
equals() :不能用于基本数据类型。
Object类中的equals()默认使用==比较地址。但String类重写了equals()方法,先比较地址,地址不同则比较内容是否相同。
三、为什么重写 equals 方法时必须重写 hashCode 方法?
在 Java 中,equals 和 hashCode 遵循以下约定:
-
一致性 :如果
obj1.equals(obj2)返回true,那么obj1.hashCode()必须等于obj2.hashCode()。 -
非一致性 :如果两个对象的
hashCode相同,equals不一定为true(哈希冲突)。
如果不重写 hashCode,可能导致对象在哈希集合(如 HashMap、HashSet)中无法正确存储。例如,两个 id 相同的 User 对象,equals 返回 true,但 hashCode 不同,会被当成两个不同元素存入集合。
四、Java 中 String 的常用方法有哪些?
-
int length():返回字符串长度。 -
boolean equals(Object obj):比较字符串内容是否相同(区分大小写)。 -
String substring(int beginIndex):从指定索引开始截取子串。 -
String trim():去除字符串首尾空白字符。 -
String replace(char oldChar, char newChar):替换所有指定字符。 -
boolean isEmpty():判断字符串长度是否为 0(注意:null调用会报错)。
五、String、StringBuffer、StringBuilder 的区别与联系
1. 可变性
-
String:不可变(Immutable),每次修改都会生成新对象。
-
StringBuilder / StringBuffer:可变(Mutable),可直接修改内容。
2. 线程安全性
-
String:天然线程安全。
-
StringBuilder:非线程安全,适合单线程。
-
StringBuffer :线程安全(方法使用
synchronized),适合多线程。
3. 性能
-
String:性能最低,频繁修改会产生大量临时对象。
-
StringBuilder:性能最高,无同步开销。
-
StringBuffer :性能略低于
StringBuilder,因有同步开销。
4. 使用场景
-
String:字符串内容固定或变化少。
-
StringBuilder:单线程下频繁修改字符串。
-
StringBuffer:多线程下频繁修改字符串。
| 特性 | String | StringBuffer | StringBuilder |
|---|---|---|---|
| 不可变性 | 不可变 | 可变 | 可变 |
| 线程安全 | 是 | 是 | 否 |
| 性能 | 低 | 中 | 高 |
| 适用场景 | 少量修改 | 多线程 | 单线程 |
六、String 为什么是不可变的?
因为 String 底层使用 final 修饰的 char[] 数组存储字符。数组本身不可变,且 String 类没有提供修改数组内容的方法,所有"修改"操作都会返回新对象。
java
Java Object 类深度解析
这种设计带来了线程安全、字符串常量池复用、哈希值缓存等好处。
总结
-
Object类是 Java 的根类,掌握其核心方法是理解 Java 面向对象的基础。 -
equals和hashCode的约定是哈希集合正确工作的关键。 -
String、StringBuilder、StringBuffer的选择直接影响程序性能和线程安全性。