java基础
Object 类的常见方法有哪些?
equals(Object obj)
判断两个对象是否相等。默认比较地址,子类可重写为值比较。
📚 面试问法:重写equals()需要注意什么?(答:同时重写hashCode())hashCode()
返回对象的哈希值,用于哈希结构(HashMap、HashSet)。
📚 口诀:相等的对象必须有相等的hashCode。toString()
返回对象的字符串表示。默认是类名@哈希码,一般建议重写。
📚 面试问法:System.out.println(obj)实际调用了什么?(答:obj.toString())getClass()
获取运行时的Class对象。用于反射或类型判断。
📚 小知识:obj.getClass() == AnotherClass.class检查真正类型。clone()
对象克隆。默认是浅拷贝,类需实现Cloneable接口。
📚 面试问法:clone()和序列化的区别?(答:clone()是内存内拷贝,序列化可以跨 JVM)wait()/notify()/notifyAll()
用于线程间通信,依赖于对象锁(synchronized)。
📚 面试常考:wait()和sleep()区别?(答:wait()会释放锁,sleep()不会)finalize()(已废弃)
垃圾回收前调用,不推荐使用。
✅ 面试口诀:
equals()+hashCode()一对好兄弟toString()好帮手,调试打印少不了clone()复制自己,需谨慎wait()/notify()线程通信底层法宝
== 和 equals() 有什么区别?
==:- 对于基本数据类型来说,
==比较的是值。 - 对于引用数据类型来说,
==比较的是对象的内存地址。
- 对于基本数据类型来说,
equals():- 类没有重写
equals()方法:通过equals()比较该类的两个对象时,等价于通过 “==” 比较这两个对象,使用的是默认的Object类equals()方法。 - 类重写了
equals()方法:一般我们都重写equals()方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回true(即,认为这两个对象相等)。
- 类没有重写
HashCode
hashCode() 有什么用?
hashCode()的作用是获取哈希码(int整数),也称为散列码。这个哈希码的作用是确定该对象在哈希表中的索引位置。- 散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)
为什么要有 hashCode()?
当你把对象加入
HashSet时:
HashSet会先计算对象的hashCode值来判断对象加入的位置;- 同时也会与其他已经加入的对象的
hashCode值作比较;- 如果没有相符的
hashCode,HashSet会假设对象没有重复出现;- 如果发现有相同
hashCode值的对象,这时会调用equals()方法来检查是否真的相同;- 如果相同,则不会加入;
- 如果不同,就会重新散列到其他位置。
这样可以大大减少
equals()的次数,从而提高执行速度。
在一些容器(比如 HashMap、HashSet)中,有了 hashCode() 之后,判断元素是否在对应容器中的效率会更高(参考添加元素进 HashSet 的过程)!
前面也提到了添加元素进 HashSet 的过程:如果 HashSet 在对比的时候,同样的 hashCode 有多个对象,它会继续使用 equals() 来判断是否真的相同。也就是说 hashCode 帮助我们大大缩小了查找成本。
为什么重写 equals() 还要重写 hashCode()?
我们重写 equals() 是为了让对象能够根据属性值来比较是否相等,而 hashCode 默认使用的算法并没有和对象属性值相绑定(重写的 hashCode() 需要和对象属性值绑定,这样可以保证相等对象落在同一个桶,从而减少 equals() 的调用次数,提升集合性能)。
如果只重写 equals() 而不重写 hashCode() 的话就会出现:使用 equals() 比较时对象是相同的,而使用 hashCode() 比较时对象不一样。
而一些集合类(如 HashMap、HashSet)先用 hashCode() 判断对象是否相同,如果散列值不相同就默认它们不一样,就不会走 equals(),这会导致误判(即 equals() 相同的情况下,因为先判断 hashCode() 没走 equals() 导致集合中出现重复的元素)。
所以说需要:
两个相等的对象的 hashCode() 值必须是相等。也就是说如果 equals() 方法判断两个对象是相等的,那这两个对象的 hashCode() 值也要相等。
String
String 为什么是不可变的?
String 当中的 char[] 数组被 final 修饰不是 String 不可变的根本原因,因为这只是限制了不能指向其他数组,本身数组的值还是可以改变的。
String 真正不可变有下面几点原因:
- 保存字符串的数组被
final修饰且为私有的,并且String类没有提供/暴露修改这个字符串的方法。 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就不会有这个问题,推荐使用。