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

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

面试必问: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] = '神';}
}
输出:
牛马问题
神马问题

既然理解了String,那就再简述几句StringBuffer跟StringBuilder吧。

String对象的不可变性,导致每次修改值都要创建新的对象,性能肯定就很慢,没有的对象还要GC来清除,性能就更低了。不可变 线程安全 性能低

StringBuffer对象可以改变值,性能就提升了不少啦。但是也要面临着线程同步的问题,所以加了synchronized进行同步,耗费了一点点性能。可变 线程安全 性能较高

StringBuilder对象也可以改变值,但是这家伙为了追求性能,连同步都不要了,所以是线程不安全的。可变 线程不安全 性能最高

补充一点JVM知识

我的“牛马问题”String对象创建了但是没有用,只会占着内存,以后我可不敢随便创建String对象了。我觉得你是多虑了,String a引用了"什么是牛马"String对象后,“牛马问题”String对象没人引用,JVM不一会儿就把它当垃圾收走了。

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

当你看完文章后,又不留下点什么,那么这篇知识不一会儿就被你的大脑当垃圾清除掉的哟~