String详解
-
注意区分对象和对象的引用
首先来看一下我在jdk中找到的String源代码,这里只截取开头的小小一部分
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
从这里可以看出,String类是被final所修饰的,因此String类对象不可变,也不可继承。这里要注意一个误区,字符串对象不可变,但字符串变量所指的值是可变的,即引用地址可变。String变量存储的是对String对象的引用,String对象里存储的才是字符串的值【注意区分对象和对象的引用】。看下面的例子
String str = "abc"; //str是个对象引用
System.out.println(str); //输出abc
str = "abcde"; //不会出错
System.out.println(str); //输出abcde
当给str第二次赋值的时候,对象"abc"并没有被销毁,仍存放在常量池中(String自带),只是让str指向了"abcde"的内存地址,而字符串对象"abcde"是新生成的,即str第二次赋值时并不是在原内存地址上修改数据,而是重新指向一个新对象,新地址。记住,对String对象的任何改变都不影响到原对象,相关的任何改变的操作都会生成新的对象。
-
String对象是否真的不可变?!
答案并不是的,那么该如何做呢?那就是使用反射(反射不太懂的可以点击链接学习——反射基础详解)
从上面的源码可知String的成员变量是private final的,也就是初始化之后不可改变。那么在这几个成员中,value比较特殊,因为它是一个引用变量,而不是真正的对象。value是final修饰的,即final不能再指向其他数组对象,那么我想改变value指向的数组时,比如将数组中的某个位置上的字符变为下划线"_"。因为不能直接通过这个引用去修改数组,那么为了访问到至value这个引用,我们可以使用反射来访问私有成员,反射出String对象中的value属性,进而改变value数组的内容。
public static void testReflection() throws Exception {
// 创建字符串"Hello World" 并赋给引用s
String s = "Hello World";
System.out.println("s = " + s); // 打印出Hello World
// 获取String类中的value字段————Field类获取成员变量
Field valueFieldOfString = String.class.getDeclaredField("value");
valueFieldOf