java.lang.String 为什么是final 的?
之前只是模模糊糊的知道原因,今天在看另外一篇文章的时候,作者简单的说了下String
类为什么是final
的,但是他的理由说服不了我,这里我简单总结下为什么java.lang.String
类是final
的。
- 使用
final
生成不变类String
在Java
中是和基本数据类型差不多的存在,类似所有的基本数据类型的包装类一样,他们都是final
的,而基本数据类型作为不可变类是因为,int 3
的包装类value
就一定是3
而不会是其他。- 不变类在多线程中可以变量共享,不会存在多线程的线程安全的问题
- 很多情况下
HashMap
的key
都是String
,而key
仅仅存的是引用,如果String
是可变的,可能会导致key
丢失。
String
的intern
的特性,也是String
为final
的重要一点,因为String
需要缓存到常量池中,而如果String
是可变的,这样会导致一个地方的引用调用改变了String
对象,其他所有引用所指向的对象也跟着改变,这简直就是灾难。- 将
String
声明为final
,还有一个说法就是语义的问题,String
类是final
类,这意味着不允许任何人定义String
的子类。换言之,如果有一个String
的引用,它引用的一定是一个String
对象,而不可能是其他类的对象。
说简单一点,就是为了性能和安全。
下面来简单的解释下可变类
为什么最好不要做HashMap
,HashSet
的key
使用HashMap
的时候我们应该都听过一句话:重载hashCode()
方法的时候也要重载equals()
这是因为HashMap
的key
可能存在Hash
碰撞,也就不同的key
,hashcode
可能相同,因此HashMap
在获取某个key
对应的value
的时候,会先索引hashcode
,然后再使用equals()
验证是否是相等的对象。
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
JDK 1.8
hashMap#getNode()
部分源码
假如我们定义了一个普通的pojo
:Student
@Getter
@Setter
@EqualsAndHashCode
@AllArgsConstructor
@ToString
public class Student {
private String name;
private String sex;
}
然后通过业务系统将其放入了一个全局的HashMap
Student stuDcc=new Student("dcc","男");
map.put(stuDcc);
而在后来,因为其他的一些业务逻辑,我们修改了stuDcc
的name
stuDcc.setName("dcc2");
就这样,这stuDcc
所对应的value
永远都获取不到了,除非stuDcc
对象再将name
修改为dcc
.
map.get(new Student("dcc","男")); //null
map.get(stuDcc); //null
为什么呢?
因为现在的HashMap
中StuDcc
对象的hashCode
是通过name=dcc ,sex=男
来计算获得的,而equals
需要是name=dcc2,sex=男
。
因为HashMap
判断一个对象需要同时HashCode
和Equals
,但是hashCode
的获取是在存入对象的时候就固定了,而equals
是判断的对象目前的值,因此stuDcc
对象在修改了name
以后,其他任何一个对象都无法同时拥有stuDcc
存入map
的时候的hashCode
以及stuDcc
修改值的相同的值。
也就是下面的代码:
map.get(new Student("dcc","男")); //null hashCode() 匹配成功但是equals失败
map.get(stuDcc); //null equals()成功 但是hashCode匹配不到
因此,最好使用不可变类做Key
参考链接:
在java中String类为什么要设计成final? – 胖君的回答 – 知乎
在java中String类为什么要设计成final? – Reversal的回答 – 知乎
Why String is Immutable or Final in Java
Why is the String class declared final in Java? – stackoverflow