今天朋友问我String的内容是真的不可变吗?我肯定告诉他是的?因为在我的主观意识里String就是一个不可变的对象。于是他给我发了这段程序:
p
今天朋友问我String的内容是真的不可变吗?我肯定告诉他是的?因为在我的主观意识里String就是一个不可变的对象。于是他给我发了这段程序:
public class StringTest {
public static void main(String[] args) throws Exception {
String a = chenssy;
System.out.println(a = + a);
Field a_ = String.class.getDeclaredField(value);
a_.setAccessible(true);
char[] value=(char[])a_.get(a);
value[4]='_'; //修改a所指向的值
System.out.println(a = + a);
}
}
看到这个简单的程序,我笑了,你这不是从底层来修改String的值么?从这里来理解String的值肯定是可以改变的啦(我们应该始终相信String的不可变性)!接着他再给我一段程序:
public class StringTest {
public static void main(String[] args) throws Exception {
String a = chenssy;
String b = chenssy;
String c = new String(chenssy);
System.out.println(--------------修改前值-------------------);
System.out.println(a = + a);
System.out.println(b = + b);
System.out.println(c = + c);
//修改String的值
Field a_ = String.class.getDeclaredField(value);
a_.setAccessible(true);
char[] value=(char[])a_.get(a);
value[4]='_'; //修改a所指向的值
System.out.println(--------------修改后值-------------------);
System.out.println(a = + a);
System.out.println(b = + b);
System.out.println(chenssy);
System.out.println(c = + c);
}
}
乍看这程序是异常的简单,无非就是赋值、改值、输出嘛!可能你现在就会毫不犹豫的说太简单了结果就是……但是!!你的毫不犹豫会害死你,而且你的结果很可能错误。那么运行结果是什么呢?
--------------修改前值-------------------
a = chenssy
b = chenssy
c = chenssy
--------------修改后值-------------------
a = chen_sy
b = chen_sy
chen_sy
c = chen_ssy
修改前值很容易理解,但是修改后值呢?是不是有点儿不理解呢?你可能会问:为什么System.out.println(chenssy);的结果会是chen_ssy,System.out.println(c = + c);也是chen_ssy呢?
要明白这个其实也比较简单,掌握一个知识点:字符串常量池。
我们知道字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串我们使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串(这点对理解上面至关重要)。
public static void main(String[] args) throws Exception {
String a = chenssy;
System.out.println(a = + a);
Field a_ = String.class.getDeclaredField(value);
a_.setAccessible(true);
char[] value=(char[])a_.get(a);
value[4]='_'; //修改a所指向的值
System.out.println(a = + a);
}
}
看到这个简单的程序,我笑了,你这不是从底层来修改String的值么?从这里来理解String的值肯定是可以改变的啦(我们应该始终相信String的不可变性)!接着他再给我一段程序:
public class StringTest {
public static void main(String[] args) throws Exception {
String a = chenssy;
String b = chenssy;
String c = new String(chenssy);
System.out.println(--------------修改前值-------------------);
System.out.println(a = + a);
System.out.println(b = + b);
System.out.println(c = + c);
//修改String的值
Field a_ = String.class.getDeclaredField(value);
a_.setAccessible(true);
char[] value=(char[])a_.get(a);
value[4]='_'; //修改a所指向的值
System.out.println(--------------修改后值-------------------);
System.out.println(a = + a);
System.out.println(b = + b);
System.out.println(chenssy);
System.out.println(c = + c);
}
}
乍看这程序是异常的简单,无非就是赋值、改值、输出嘛!可能你现在就会毫不犹豫的说太简单了结果就是……但是!!你的毫不犹豫会害死你,而且你的结果很可能错误。那么运行结果是什么呢?
--------------修改前值-------------------
a = chenssy
b = chenssy
c = chenssy
--------------修改后值-------------------
a = chen_sy
b = chen_sy
chen_sy
c = chen_ssy
修改前值很容易理解,但是修改后值呢?是不是有点儿不理解呢?你可能会问:为什么System.out.println(chenssy);的结果会是chen_ssy,System.out.println(c = + c);也是chen_ssy呢?
要明白这个其实也比较简单,掌握一个知识点:字符串常量池。
我们知道字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串我们使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串(这点对理解上面至关重要)。