经常看到说Sting是不可变的,可是平时String类型的对象的值进行可以变
比如
String s = "Hello World";
System.out.println(StrUtil.format("值={},hashCode={}", s, s.hashCode()));
s = "Hello World Again";
System.out.println(StrUtil.format("值={},hashCode={}", s, s.hashCode()));
运行的结果为
值=Hello World,hashCode=-862545276
值=Hello World Again,hashCode=-2066678108
其实,这里有一个思维误区,就是不可变不是说对象引用s不可变,而是指它的值Hello World以后在内存中就存在了,且不可改变这个字面值,其他Strin对象都可以引用它。
因此s只是引用了这个常量Hello World,对象引用s是依然可以引用其他值,若要s也不可变,即不可以再引用其他值,则需要在其前面加上final
关键字。
final
对于基本类型就是值不能变;
对于引用类型则是变量的地址值不可变改变,至于引用对象里面的值,则没有了约束。
其实String类里维持了一个不可变数组private final char value[];
每次赋值了之后,那这个value就是指向内存中字面量值Hello World,且数组引用value由于final修饰,也就不可能再次改变引用了。
那么问题来了:
1、为什么说String是不可变的?
因为:String值创建了之后,该值就一直存在内存中了,且这个值不会被改变。
2、为什么要设计为不可变
因为,第一点可以共享,只需要建立一次,它就放入了常量池可供其他引用变量来引用。第二点是,为了安全,集合的相关操作中,经常使用String类型的值作为key,用的就是它的hashCode,我们不希望hashCode经常变来变去,没有唯一性,否则集合操作就存在风险。
3、一定不可变吗?
不一定,可以通过反射的方式,改变字面量的值。
4、可变StringBuilder、StringBuffer
因为里面维护了char[] value;
,可以随时改变引用。StringBuffer加锁安全。
举例
String s = "Hello World";
String s2 = "Hello World";
System.out.println("-------第一处-----");
System.out.println(StrUtil.format("值={},hashCode={}", s, s.hashCode()));
System.out.println(StrUtil.format("值={},hashCode={}", s2, s2.hashCode()));
//值=Hello World,hashCode=-862545276
//值=Hello World,hashCode=-862545276
//反射s
Field valueFieldOfString = String.class.getDeclaredField("value");
valueFieldOfString.setAccessible(true);
char[] value = (char[]) valueFieldOfString.get(s);
value[5] = '_';
System.out.println("-------第二处-----");
System.out.println(StrUtil.format("值={},hashCode={}", s, s.hashCode()));
System.out.println(StrUtil.format("值={},hashCode={}", s2, s2.hashCode()));
//值=Hello_World,hashCode=-862545276
//值=Hello_World,hashCode=-862545276
//再次换一个
s = "Hello World2";
System.out.println("-------第三处-----");
System.out.println(StrUtil.format("值={},hashCode={}", s, s.hashCode()));
//值=Hello World2,hashCode=-969099730
//【重点】Hello World
s = "Hello World";
System.out.println("-------第四处-----");
System.out.println(StrUtil.format("值={},hashCode={}", s, s.hashCode()));
//值=Hello_World,hashCode=-862545276
//Hello_World
s = "Hello_World";
System.out.println("-------第五处-----");
System.out.println(StrUtil.format("值={},hashCode={}", s, s.hashCode()));
//值=Hello_World,hashCode=941091237
重点可以看到第4处,我们让其回到了最开始的Hello World,此时s依然只是该值的引用,但是结果却是反射之后的Hello_World,是因为String里面的数组的值通过反射变了。
这样就是说明,说不可变是指String里面的不可变数组对应的字面值建立之后就不会变了,就存在内存中。第二,也就是说不一定完全就不变了,可以使用反射来改变其值。
【完】
正在去BAT的路上修行