如何通过不改变引用的指向来修改内容
案例来源于bilibili,如有侵权,请告知删除!谢谢~
String str = new String("abc")在不改变引用指向的前提下输出“abcd”
public class No1 {
public static void main(String[] args) throws Exception{
String str = new String("abc");
.....
System.out.println(str); //abcd
}
有可能有人会想到通过StringBuilder
中的append()方法以追加的方法实现;
StringBuilder stringBuilder = new StringBuilder(str)
stringBuilder.append("d");
System.out.println(str);//abc
System.out.println(stringBuilder); //abcd
通过代码实现过后,str
输出任为abc
,其实StringBuilder
生成了一个新的stringBuilder
对象,所以append()
方法操作的也是stringBuilder
对象,并不是我们想要的str
;
还有的小伙伴可能会想到利用String
API中的replace
来实现,
String replace = str.replace("abc","abcd");
然而,String
声明的是不可变的对象,每次操作都会生成新的 String
对象,然后将指针指向新的String
对象;
而且调用replace()
方法,底层会生成一个StringBuffer
对象,然后再对StringBuffer
对象进行操作并返回;
其实,我们只要通过String
源码可以看到,它是通过参数赋值给value
属性,value
为一个char[]
数组,
即可通过反射来获取value
字段,将字段值直接修改为abcd
即可:
public class No1 {
public static void main(String[] args) throws Exception{
String str = new String("abc");
//通过反射拿到str里面的value字段
Field value = str.getClass().getDeclaredField("value");
//修改权限为true
value.setAccessible(true);
//将abc直接修改为abcd并转换为字符数组
value.set(str,"abcd".toCharArray());
System.out.println(str);
}
代码解析:
setAccessible()方法不属于Field,它属于AccessibleObject类,
Field通过extends AccessibleObject来获得setAccessible()方法;
getDeclaredField("value"):
java.lang.Class类的getDeclaredField()方法用于获取此类的指定字段。该方法以Field对象的形式返回此类的指定字段。
setAccessible(true):
该方式是用来设置获取权限的。
如果 accessible 标志被设置为true,那么反射对象在使用的时候,不会去检查Java语言权限控制(private之类的);
如果设置为false,反射对象在使用的时候,会检查Java语言权限控制。
需要注意的是,设置为true会引起安全隐患。
String底层源码解析:
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
// 调用String类的构造方法,通过源码可以看到,它是把传进去的值赋值给value属性
public final class String
implements java.io.Serializable, Comparable<java.lang.String>, CharSequenc
/** The value is used for character storage. */
private final char value[];
......
// value属性为一个char数组