Java基础回顾(三)

6 阅读3分钟

一如既往,每日2小时的学习,回顾下今日的知识点。

为什么要有 hashCode?

  • 用于哈希集合(如 HashMapHashSet)的高效查找。
  • 必须与 equals() 保持一致:equals 相等则 hashCode 必相等;反之不一定。
  • 重写 equals() 时必须重写 hashCode(),否则会导致哈希集合逻辑错误。

这个很少用到,对我来说基本没用过,甚至翻阅过以往的项目不管自己写的还是别人写的,都见到过调用这个方法的,算是个新的知识点吧。

String、StringBuffer、StringBuilder 的区别

  • String:不可变,线程安全(天然),适合少量操作或常量。
  • StringBuffer:可变,线程安全(方法同步),适合多线程频繁修改。
  • StringBuilder:可变,非线程安全,性能最高,适合单线程频繁修改。

这个真的是高频使用率,最多的还是StringStringBuffer

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,习惯成自然。

有时候,慢下来补补基础,反而是走得更远的开始。