7月26日讨论关于String的不可变性质。
前几天和朋友相互面试(为了秋招作准备),然后它问了我一个问题,说说String和StringBuffer以及StringBuilder之间的区别。
我回答的很官方,也可以说很浅显吧,因为关于那三者之间的联系和区别我也确实没有深究,知识按照面经题目上理解了三者的性质。String类是用final修饰,然后它底层是用final private char[] value实现的,所以它是不可变的,而StringBuffer由于用了锁机制所以它是线程安全的,StringBuilder由于没有用锁来约束,所以它是线程不安全的,但也由于它没有用锁,所以它的性能要优于String和StringBuffer。
如果面试的时候这么答确实也没啥问题(前提是面试官不继续深究问下去)。
然后朋友又问了为什么说String是不可变的呢?
我说因为它用final修饰了哇。
然后他又问,难道用final修饰了就是不可变了吗?
我竟无言以对,然后他说因为String是final修饰的,所以String创建的对象是不可变的,但String创建的引用变量是可以改变的,它可以重新指向其他的对象。
听完他的解释我总感觉哪儿不对,但因为毕竟理解的不到位,所以也不好反驳他。
所以今天晚上我特意抽时间去查了一下相关资料,但是我发现网上的资料对这方面的解释并不是很到位,他们的解释也是停留在,那种很标准的回答上,即由于String类本身是final修饰的,而且它底层的数组也是final修饰的,所以它是不可变的。
然后举个栗子:
其中一个老哥是这样解释String的不可变性的。虽然他的解释理论上没啥问题,但是感觉看了他的解释我的疑惑进一步加深了,因为说String是不可变类型,但是String类型的变量却可以随意指向其他的对象(这一点其实还好)。最让我无法理解的是:这个引用不断可以随意指向其他变量,还可以随意改变地址。
最后想了很久,终于想通了,之所以说String类型的对象是不可变的,它实际上是说String的实例化对象
例如String s = new String("加油");
这个例子中new String("加油")创建并实例化了一个“加油”的String类型的变量,并且用引用类型的变量指向了“加油”这个String对象。所以此处的不可变只是“加油”不可变,而引用类型的变量s随便你怎么变没什么关系。
而为什么“加油”这个实例化的String类型的对象为什么是不可变的呢?我觉得这个和private final char value[]没什么关系。而主要原因是final关键字修饰了String这个类。下面看看String这个类的数据结构:
下面看看java中对不可变类的解释:
不可变类只是其实例不能被修改的类。每个实例中包含的所有信息都必须在创建该实例的时候就提供,并且在对象的整个生命周期内固定不变。为了使类不可变,要遵循下面五条规则:
1.不要提供任何修改对象状态的方法。
2.保证不会被扩展。一般的做法是让这个类称为final的,防止子类化,破坏该类的不可变行为。
3.使得所有的域都是final的。
4.使得所有的域都成为私有的。方式客户端获得访问被域引用的可变对象的权限,并防止客户端直接修改这些对象。
5.确保对于任何可变性组件的互斥访问。如果类具有指向可变对象的域,则必须确保该类的客户端无法指向这些对象的引用。
下面是我进行的一些实验:
本文地址:https://blog.csdn.net/qq_34473178/article/details/107599646