Java基础Object中常见问题解析
今天复习了一下Java基础知识,以下是复习的内容:
问题1:
== 与 equals 有什么区别?
==
运算符(永远比较引用):- 对于任何对象类型的变量(包括
String
和其他所有类),==
比较的是两个变量指向的对象在内存中的地址(引用)是否相同。它检查两个引用是否指向堆内存中的 同一个对象。 - 对于基本数据类型(
int
,double
,char
,boolean
等),==
比较的是两个变量存储的实际值是否相等。因为基本数据类型存储在栈内存中,直接包含值本身,没有引用的概念。
- 对于任何对象类型的变量(包括
.equals()
方法(比较内容 - 具体行为由类决定):.equals()
是定义在Object
类中的一个方法。所有Java对象都继承了这个方法。- 默认行为(未重写): 在
Object
类中,.equals()
的默认实现 就是使用==
来比较引用地址!所以默认情况下,对于任何自定义类(没有重写.equals()
),obj1.equals(obj2)
和obj1 == obj2
是完全等价的,都是比较引用地址。 - 重写行为: 很多Java内置类(尤其是那些表示值的类)重写了
.equals()
方法 来提供“逻辑相等”或“内容相等”的比较。String
类: 这正是你所理解的:str1.equals(str2)
会比较两个String
对象包含的字符序列是否完全相同,与它们的内存地址无关。- 包装器类 (
Integer
,Double
,Boolean
,Character
等): 这些类也重写了.equals()
来比较它们包装的基本类型的值是否相等。 - 集合类 (
List
,Set
,Map
等): 这些类通常重写.equals()
来比较集合中的元素是否相同(顺序、数量、元素相等性)。 - 自定义类: 默认比较引用地址。 如果你希望根据对象的某些属性(字段值)来定义两个对象是否“相等”,你必须自己重写
.equals()
(和.hashCode()
)方法来实现这种“内容比较”逻辑。否则,默认行为就是==
。
关键原则:
==
永远比较:- 对象 -> 引用地址
- 基本类型 -> 值
.equals()
比较什么由对象的类决定:- 默认 (
Object
实现) -> 引用地址 (等同于==
) - 重写后 (如
String
, 包装类, 集合类, 或你自定义的类) -> 内容/逻辑相等性 (具体看实现)
- 默认 (
结论总结:
比较类型 | == 运算符 | .equals() 方法 |
---|---|---|
字符串 (String ) | 比较引用地址 (是否同一个对象) | 比较内容 (字符序列是否相同) |
非字符串对象 | 比较引用地址 (是否同一个对象) | 取决于类是否重写:<br> * 未重写 (Object 默认):比较引用地址 (等同于 == )<br> * 已重写:比较内容/逻辑相等 (由重写逻辑决定) |
基本数据类型 | 比较值本身 | 不适用 (基本类型不是对象,没有方法) |
问题2:
hashcode和equals方法有什么关系?
强制关系(Java规范规定)
一致性
- 如果两个对象通过
equals()
比较是相等的 → 它们的hashCode()
必须返回相同的整数值。 - 如果两个对象通过
equals()
比较是不相等的 → 它们的hashCode()
不一定要不同(但不同可提升哈希表性能)。
- 如果两个对象通过
反向不成立
- 如果两个对象的
hashCode()
相同 → 它们不一定要通过equals()
比较相等(哈希冲突是允许的)。 - 如果
hashCode()
不同 → 对象一定不相等(可直接跳过equals()
比较)。
- 如果两个对象的
那么为什么需要这种关系呢?
主要是为了基于哈希的集合(如 HashMap
, HashSet
, Hashtable
)的高效运作:
hashCode()
的作用- 快速定位对象在哈希表中的桶(bucket)。
- 减少后续
equals()
调用的次数(只需比较同一桶内的对象)。
equals()
的作用- 在哈希冲突时精确比较对象的逻辑相等性。
为了不破坏这个关系,我们必须做到:
- 重写
equals()
时必须重写hashCode()
- 使用相同字段:计算
hashCode()
的字段必须与equals()
比较的字段完全一致。 - 保证不可变性:参与计算的字段不应在对象生命周期内改变(或避免用作哈希键)。
问题3:
简单谈一下String、StringBuffer、StringBuilder的区别和联系
特性 | String | StringBuffer | StringBuilder |
---|---|---|---|
可变性 | ❌ 不可变 | ✅ 可变 | ✅ 可变 |
线程安全 | ✅ 安全(天生不可变) | ✅ 安全(方法用 synchronized 修饰) | ❌ 不安全 |
性能 | 低(频繁操作时) | 较高(线程安全场景) | 最高(无锁开销) |
适用场景 | 常量、不需修改的字符串 | 多线程环境字符串操作 | 单线程环境字符串操作 |