一、String是一个不可变类
我们都知道String是一个不可变类,因为它的源码内部维护着一个final修饰的char数组,final修饰的变量不可以被改变,修饰的方法不可以被重写,修饰的类不可以被继承:(简要源码)
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[];
}
也就是说:String对象一旦创建,就不可改变。拼接、截取或者重新赋值都是在重新建对象。
做一下代码演示:
/** * @Author 程Sir * @Version 2020年3月26日上午11:20:51 * @description 描述:字符串不可以被改变 */ public class Demo { public static void main(String[] args) throws IOException { String str = "不变的字符串"; System.out.println("1. " + str +" : " + str.hashCode()); str = str.substring(2); System.out.println("3. " + str +" : " + str.hashCode()); str = str+1; System.out.println("2. " + str +" : " + str.hashCode()); } }
输出的结果是:
1. 不变的字符串 : 83361354
3. 的字符串 : 927327327
2. 的字符串1 : -1317623886
通过演示表明:hashcode值每次都是不一样的。说明操作的不是同一对象,再次过程中有新的对象被创建。
二、应用Java中的反射机制
我们学习了反射机制,了解到:反射是在运行期能够对类的属性、方法进行操作。即:运行期绑定。那么我们是不是可以在String运行期对它的值进行一个完美操作了,答案是肯定的,完全可以的,看以下代码:
/** * @Author 程Sir * @Version 2020年3月26日上午11:20:51 * @description 描述:字符串不可以被改变 */ public class Demo { public static void main(String[] args) throws IOException { String str = "不变的字符串"; System.out.println("1. " + str +" : " + str.hashCode()); try { //反射通过属性拿到值,Sting类的属性char数组的属性名为:value Field field = str.getClass().getDeclaredField("value"); //设置该属性为可操作 field.setAccessible(true); //改变该属性的值:改变char数组 field.set(str, new char[]{'程','s','i','r'}); System.out.println("2. " + str+" : " + str.hashCode()); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
输出的结果是:
1. 不变的字符串 : 83361354
2. 程sir : 83361354
从演示结果中,我们发现,字符串str的hashcode值两次相等,并没有发生改变,说明是同一对象,但前后的值(内容)完全是发生了改变。即达到了我们的目的:String类的值是可以通过Java的反射机制进行改变的。
三、总结
最后,我们做一个总结:
String类是用final关键字修饰的,那值就是不可以改变,值不可以改变,导向到结果是这个值的引用地址在内存中是不可被改变。再String类的本质是内部维护这一个char类型的数组,明确这点后,我们既可以通过Java反射机制的特性,在运行期对其内部的char数组内容进行操作,从而达到改变String类内容的目的。