String、StringBuilder做为实参在方法中修改字符串值的问题
场景介绍:需要在一个方法中同时修改两个字符串的值,因为不想传回一个数组,所以就尝试下直接修改字符串对象(地址引用)
- 下面介绍下几种修改方式:
public static void changeString(String originStr){
originStr=new String("str1 change");
}
main(){
String str1="I am str1";
changeString(str1);
System.out.println(str1);//输出I am str1
}
public static void changeString(String originStr){
originStr="str2 change";
}
main(){
String str2="I am str2";
changeString(str2);
System.out.println(str2);//输出I am str2
}
上面两种方式是直接传一个String对象,第一个是直接让形参指向新的对象,结果是方法内改变的值不是影响到实参;第二个是让形参指向一个字符串常量,结果同第一种是一样的。
因为在Java中,参数传递分为两种。一个是基本类型的值引用,这个就不多说了;还有个就是对象引用,而对象引用实际上是把存储在JVM栈区的对象引用的数据拷贝了一份。所以当你在方法内部将该对象拷贝的引用重新指向新的地址对实参是没有影响的。
这是引用拷贝时的示意图
接着拷贝后的originStr指向新的对象
可见str1的引用都没有改变,所以这样是不能改变String对象的。
接下来说所第二种为什么也不行,也就是在方法内部不重新为对象引用指向新的对象地址,而是直接给字符串赋值。先来看看String内部是怎么得构造的。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
......
}
String类是一个被final类,也就是它是不能被继承和更改的。里面用一个char数组维护他的字符,所以我们使用的字符串都是里面的char数组转化而来,而这个char[] value也是被final修饰过的。所以String对象具有不能被修改的属性。如下图:
即使你在方法内部使用的将一个String对象拷贝引用originStr指向一个字符串常量"new str1",他也是拷贝引用去指向字符串常量池中某一块地址,所以依旧还是不能影响到实参的字符串对象值。
- 最后是使用了StringBuilder作为参数,在方法内部修改StringBuilder引用指向内存的数据,实现在方法中直接修改字符串的值。
public static void changeString(StringBuilder originStr){
originStr.replace(0,originStr.length(),"str1 change");
}
main(){
StringBuilder sb1=new StringBuilder("I am sb1");
changeString(sb1);
System.out.println(sb1);//输出str1 change
}