> 文章列表 > 面试必问:Java中String类型为什么是不可变的?

面试必问:Java中String类型为什么是不可变的?

引言:Java面试八股文里,String的不可变性堪称经典送分(或送命)题。你以为你懂了?来,先看段代码:

问题:代码里a = "什么是牛马",String对象真被修改了吗?错!这好比把杯子里的可乐倒掉换成雪碧,杯子还是那个杯子,但饮料早就换了——a只是个引用,真正的String对象(可乐/雪碧)在堆里纹丝不动。

深入:String的不可变,本质是它的value[]数组被final+private双锁封印,还没钥匙(set方法)。你以为final锁的是数组内容?天真!它锁的是数组的地址,至于数组里的字符,想改?门儿都没有——除非你祭出反射这把“物理外挂”。

脑洞时刻:为啥Java要这么设计?举个栗子:假设String可变,你往HashSet里丢了个"java",然后偷偷把它改成"PHP",结果哈希值突变,Set直接原地爆炸——找不着对象了!再比如多线程下,String不可变才能安心当个“共享渣男”,否则分分钟数据错乱。

终极骚操作:用反射强行改value[]?行啊!但这就好比用锤子修瑞士手表,改完的String会让JVM字符串池里的所有相同字面量集体躺枪,堪称“一人作死,全网陪葬”。面试时说完这句,记得补一句:“当然,这么干会被同事祭天”——格局瞬间打开!

面试必问:Java中String类型为什么是不可变的?

面试必问:Java中的String为什么是不可变的?
面试必问:Java中String类型为什么设计成不可变的?

省流:String是一个类,通过value[]存储字符串,但是value被private和final修饰,也没有set方法,所以字符串无法修改,硬要修改的话,可以利用反射机制修改value数组元素

明明可以修改字符串,为什么就说是不能修改呢?

根据下面实例,我发现了String对象明明可以修改!请问你真的修改的了String对象吗?

public class Test{public static void main(String[] args){String a = "牛马问题";System.out.println(a);a = "什么是牛马";System.out.println(a);}
}	
输出:
牛马问题
什么是牛马

其实我在上面已经两次对String对象进行标注了,那你觉得上述示例中哪个才是String对象呢?是a?还是“牛马问题”?还是“什么是牛马”?下面我来给你们再细化一下这段代码。

public class Test{public static void main(String[] args){String a = new String("牛马问题");System.out.println(a);a = new String("什么是牛马");System.out.println(a);}
}	
输出:
牛马问题
什么是牛马

有的童鞋在心中有些思路了,但也有可能还存在疑虑。其实String a不是String 对象,真正的String对象是通过new String()在JVM的堆中创建出来的,String a只是String对象的引用。所以不要再纠结为什么String a可以改变了。

那么我们就讨论一下String对象为什么不能改变

String对象为什么不能改变,顾名思义,就是String对象的值不能改变。我们先来看看String的值是如何存储的,再看看为什么不能改变存储的值。

String 对象的值一般通过value数组变量存储,我们可以看到这个数组被final修饰,那就是说无法改变value数组的地址,从一而终。

  / The value is used for character storage. */private final char value[];

那有同学就问了,我们可以改变value数组里面的值呀?真是个大聪明,你看看String类里面这个value数组变量是不是被private修饰了,那我们只能通过set()方法来改变value数组的元素。可是String类没有提供value数组变量的set()方法!

这下就给你讲明白了为什么String类型是不可变的

什么?它不给你改就是不改了?我非要改!

面试的时候我们只是解释上面的原因其实不是那么尽善尽美,想要更好的去加薪去装逼,我们还需更进一步回答。

别忘了我们的反射机制,在通常情况下,他可以做出一些违反语言设计原则的事情。这也是一个技巧,每当面试官问一些违反语言设计原则的问题,你就可以拿反射来反驳他。

public class Test{public static void main(String[] args){String str = "牛马问题";System.out.println(str);//通过发射获取String内部value数组变量Field field = String.class.getDeclareField("value");//作用就是能够正常的方位私有属性field.setAccessible(true);char[] value = (char[])field.get(str);//把字符串第一个字符改变value[0] = '神';}