导航

最近突然被问到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 = new String(test);
 4 String test3 = new String(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/ 

本文是原创,欢迎大家转载;但转载时必须注明文章来源,且在文章开头明显处给明链接。
<欢迎有不同想法或见解的同学一起探讨,共同进步>