Skip to content

java基础

Object 类的常见方法有哪些?

  1. equals(Object obj)
    判断两个对象是否相等。默认比较地址,子类可重写为值比较。
    📚 面试问法:重写 equals() 需要注意什么?(答:同时重写 hashCode()

  2. hashCode()
    返回对象的哈希值,用于哈希结构(HashMapHashSet)。
    📚 口诀:相等的对象必须有相等的 hashCode

  3. toString()
    返回对象的字符串表示。默认是类名@哈希码,一般建议重写。
    📚 面试问法:System.out.println(obj) 实际调用了什么?(答:obj.toString()

  4. getClass()
    获取运行时的 Class 对象。用于反射或类型判断。
    📚 小知识:obj.getClass() == AnotherClass.class 检查真正类型。

  5. clone()
    对象克隆。默认是浅拷贝,类需实现 Cloneable 接口。
    📚 面试问法:clone() 和序列化的区别?(答:clone() 是内存内拷贝,序列化可以跨 JVM)

  6. wait() / notify() / notifyAll()
    用于线程间通信,依赖于对象锁(synchronized)。
    📚 面试常考:wait()sleep() 区别?(答:wait() 会释放锁,sleep() 不会)

  7. finalize() (已废弃)
    垃圾回收前调用,不推荐使用。


✅ 面试口诀:

  • equals() + hashCode() 一对好兄弟
  • toString() 好帮手,调试打印少不了
  • clone() 复制自己,需谨慎
  • wait() / notify() 线程通信底层法宝

==equals() 有什么区别?

  • ==

    • 对于基本数据类型来说,== 比较的是值。
    • 对于引用数据类型来说,== 比较的是对象的内存地址。
  • equals()

    • 类没有重写 equals() 方法:通过 equals() 比较该类的两个对象时,等价于通过 “==” 比较这两个对象,使用的是默认的 Objectequals() 方法。
    • 类重写了 equals() 方法:一般我们都重写 equals() 方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true(即,认为这两个对象相等)。

HashCode

hashCode() 有什么用?

  • hashCode() 的作用是获取哈希码(int 整数),也称为散列码。这个哈希码的作用是确定该对象在哈希表中的索引位置。
  • 散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)

为什么要有 hashCode()

当你把对象加入 HashSet 时:

  • HashSet 会先计算对象的 hashCode 值来判断对象加入的位置;
  • 同时也会与其他已经加入的对象的 hashCode 值作比较;
  • 如果没有相符的 hashCodeHashSet 会假设对象没有重复出现;
  • 如果发现有相同 hashCode 值的对象,这时会调用 equals() 方法来检查是否真的相同;
  • 如果相同,则不会加入;
  • 如果不同,就会重新散列到其他位置。

这样可以大大减少 equals() 的次数,从而提高执行速度。

在一些容器(比如 HashMapHashSet)中,有了 hashCode() 之后,判断元素是否在对应容器中的效率会更高(参考添加元素进 HashSet 的过程)!

前面也提到了添加元素进 HashSet 的过程:如果 HashSet 在对比的时候,同样的 hashCode 有多个对象,它会继续使用 equals() 来判断是否真的相同。也就是说 hashCode 帮助我们大大缩小了查找成本。

为什么重写 equals() 还要重写 hashCode()

我们重写 equals() 是为了让对象能够根据属性值来比较是否相等,而 hashCode 默认使用的算法并没有和对象属性值相绑定(重写的 hashCode() 需要和对象属性值绑定,这样可以保证相等对象落在同一个桶,从而减少 equals() 的调用次数,提升集合性能)。

如果只重写 equals() 而不重写 hashCode() 的话就会出现:使用 equals() 比较时对象是相同的,而使用 hashCode() 比较时对象不一样。

而一些集合类(如 HashMapHashSet先用 hashCode() 判断对象是否相同,如果散列值不相同就默认它们不一样,就不会走 equals(),这会导致误判(即 equals() 相同的情况下,因为先判断 hashCode() 没走 equals() 导致集合中出现重复的元素)。

所以说需要:

两个相等的对象的 hashCode() 值必须是相等。也就是说如果 equals() 方法判断两个对象是相等的,那这两个对象的 hashCode() 值也要相等。


String

String 为什么是不可变的?

String 当中的 char[] 数组被 final 修饰不是 String 不可变的根本原因,因为这只是限制了不能指向其他数组,本身数组的值还是可以改变的。

String 真正不可变有下面几点原因:

  1. 保存字符串的数组被 final 修饰且为私有的,并且 String 类没有提供/暴露修改这个字符串的方法。
  2. String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变。

Java 9 为何要将 String 的底层实现由 char[] 改成了 byte[]?(更省空间)

新版的 String 实际支持两个编码方案:

  • Latin-1
  • UTF-16

如果字符串中包含的字符在 Latin-1 可表示范围内,就会使用 Latin-1 编码方案。

  • Latin-1:byte 占 1 个字节(8 位)
  • UTF-16:char 占 2 个字节(16 位)

因此:byte[] 相较 char[] 节省一半的内存空间。

JDK 官方指出绝大部分字符串对象只包含 Latin-1 可表示的字符。


字符串拼接用 “+” 还是 StringBuilder

  • + 底层还是使用 StringBuilder 来进行拼接的;
  • 如果在 循环中使用 + 拼接字符串,每次循环都会创建一个新的 StringBuilder,非常耗性能;
  • 使用 StringBuilder 就不会有这个问题,推荐使用。