一如既往,每日2小时的学习,回顾下今日的知识点。
为什么要有 hashCode?
- 用于哈希集合(如
HashMap、HashSet)的高效查找。 - 必须与
equals()保持一致:equals相等则hashCode必相等;反之不一定。 - 重写
equals()时必须重写hashCode(),否则会导致哈希集合逻辑错误。
这个很少用到,对我来说基本没用过,甚至翻阅过以往的项目不管自己写的还是别人写的,都见到过调用这个方法的,算是个新的知识点吧。
String、StringBuffer、StringBuilder 的区别
- String:不可变,线程安全(天然),适合少量操作或常量。
- StringBuffer:可变,线程安全(方法同步),适合多线程频繁修改。
- StringBuilder:可变,非线程安全,性能最高,适合单线程频繁修改。
这个真的是高频使用率,最多的还是String和StringBuffer。
String 为什么是不可变的?
- 优点:常量池共享、安全性、哈希缓存、线程安全。
- 实现:类
final修饰;字符数组private final;不提供修改方法;所有修改返回新对象。
总结下--String 的不可变性是指:一旦一个 String 对象被创建,它的值(字符序列)就不能被改变。任何对 String 的修改操作(如拼接、替换、截取)都会返回一个新的 String 对象,而原对象保持不变。
其底层代码就保证了其不变性:
- 类用
final修饰,防止被继承破坏不可变性。 - 底层字符数组用
private final修饰,且不提供修改该数组的方法。 - 所有“修改”操作(如
concat()、replace())都返回新对象,不改变原对象。 - 所有字段私有且无 setter 方法。 曾经在对某一个String变量做截取操作时就有过疑问,为什么截取后的变量需要用新的String去接,现在算是明白了。
新的名词‘常量折叠’(Constant Folding)
- 编译器在编译期计算常量表达式,用结果替换原表达式。
- 例如:
"Hello" + " " + "World"编译为"Hello World";10 + 20编译为30。 - 仅对编译时常量生效(字面量、
final基本类型等)。
String s1 = new String("abc"); 创建了几个对象?
在回答这个问题时第一想法时两个,一个在堆中,一个在字符串常量池,实际需要区分情况,准确来说是1个或者2个。
如果常量池中存在值相等的,则只在堆中生成一个新对象;没有则两个。
字符串拼接用 + 还是 StringBuilder
- 单行拼接:用
+,编译器自动优化为StringBuilder。 - 循环拼接:必须手动使用
StringBuilder,避免反复创建对象。 - 多线程环境:用
StringBuffer或加锁。 - 纯字面量拼接:常量折叠,无需
StringBuilder。
在以前公司内部的编码规范文档中,明令要求使用append;(因之前出现过批量发送会员短信时使用+拼接用户名+“#”+用户ID+会员等级所造成的内存事故)
看似是代码规范问题,实际为性能问题,刚入行时图方便,用+拼接,后来随着代码量的增多改用append,习惯成自然。
有时候,慢下来补补基础,反而是走得更远的开始。