最近突然被问到String为什么被设计为不可变,当时有点懵,这个问题一直像bug一样存在,竟然没有发现,没有思考到,在此总结一下。
1.String的不可变
String类被final修饰,是不可继承和修改的。当一个String变量被第二次赋值时,不是在原有内存地址上修改数据,而是在内存中重新开辟一块内存地址,并指向新地址。
String类为什么要被设计为是final的?
1.不可变性支持线程安全。
2.不可变性支持字符串常量池,提升性能。
3.String字符串作为最常用数据类型之一,不可变防止了随意修改,保证了数据的安全性。
正常情况下Java的String字符串是final且不可变的。不过可以通过特殊手段修改它的内容。
String类的主力成员字段value是个char[ ]数组,而且是用final修饰的。final修饰的字段创建以后就不可改变。因为虽然value是不可变,也只是value这个引用地址不可变。挡不住Array数组是可变的事实。Array的数据结构看下图:
也就是说Array变量只是stack上的一个引用,数组的本体结构在heap堆。String类里的value用final修饰,只是说stack里的这个叫value的引用地址不可变。没有说堆里array本身数据不可变。
代码测试:
1 String test = "immutable String";2 String test1 =test;3 String test2 = newString(test);4 String test3 = newString(test.toCharArray());5 Field values = String.class.getDeclaredField("value");6 values.setAccessible(true);7 char[] chars = (char[])values.get(test);8 chars[0] = 'u';9 chars[1] = 'n';10 System.out.println("test==test1:" + (test ==test1));11 System.out.println("test==test2:" + (test ==test2));12 System.out.println("test1==test2:" + (test1 ==test2));13 System.out.println("test:" + test + "test1:" + test1 + "test2:" + test2 + "test3:" + test3);
由String的不可变性引申到其他基本数据类型: Byte,Short,Integer,Long,Double,Float,Character,Boolean 八种基本数据的包装类,仔细查看发现也是final修饰的,再仔细查看一下enum枚举类型,发现用javac编译后再用javap反编译也是被编译为final修饰的类,并且其枚举值全部定义为static final 修饰的成员变量。
由此发现,Java设计者在设计Java基本数据类型时,把基本数据类型全部设计为不可变的,这样既方便了开发人员,又保证了数据的安全性。
总结:Java中String是不可变的,但是可以通过反射修改其内容。
备注:
作者:Shengming Zeng
博客:http://www.cnblogs.com/zengming/
本文是原创,欢迎大家转载;但转载时必须注明文章来源,且在文章开头明显处给明链接。