字符串的不可变性核心原因是因为String内部的Char [ ] value属性是final修饰的,是不可变的。
有人会误认为String的不可变性是因为String是被final修饰的,这里误会了final修饰类时的作用,final修饰类时表示该类不可以被继承,而final修饰属性的时候,表示的是该属性不可以被更改。
因此字符串的不可改变其实应该理解为不可以直接改变或者不可以操作字符串去改变自己的初始的字符存储数组,字符串本身是不可修改的,他的改动是建立在新new出一个string对象,然后生成新的Char[ ] value数组来完成的。
一、下面先介绍String构建对象的几种形式。
如果字符串是直接new出来的,对象会创建在队中,而属性value指向常量池。
特别的,当 String s1 = "sss"时,对象会创建在常量池中。(常量池不会维护相同的字符数组)
下面是一道经典面试题:
String s1 = "sss";
String s2 = "sss";
String s3 = new String("sss");
String s4 = new String("sss");
上图同样可以看出,如果是第一次new String( ),会构建两个对象,一个在堆中,一个在方法区的常量池中。
上面一题看懂了,再来一道综合的面试题。
String s1 = "hello";
String s2 = "world";
String s3 = "hello" + "world";
String s4 = s1 + "world";
String s5 = s1 + s2;
String s6 = (s1 + s2 ).intern( );
结论:
1.常量与常量的拼接的结果在常量池中,这个拼接可以在程序运行前的编译阶段就可以完成。
2.只要有右边有变量,结果就会在堆中new一个对象。
3.如果拼接的结果是调用intern()方法,则返回值就是常量池中的结果。
二、下面再讲讲程序不可变性的体现。
只要尝试改变值,都会产生新对象。如 + ,连接操作可能不涉及变量,所以产生的对象即可能在堆中,也可能在常量池。而trim(),replace()等方法,需要靠对象调用,一定会设计变量,所以会在堆中先new对象,然后再去方法区中变动。