String和基本包装类作为参数在传递问题
public class ReferenceDemo {
public static void StringReference(){
String a = new String("abc");
String b = a;
changeString(b);
System.out.println("a: "+a);
System.out.println("b: "+ b);
}
public static void changeString(String str){
// str = "23232"; 同样的结果
str = new String("abcd");
}
public static void IntegerReference(){
Integer c = new Integer("1");
Integer d = c;
changeInteger(d);
System.out.println("c: "+c);
System.out.println("d: "+ d);
}
public static void changeInteger(Integer d){
d = new Integer("12");
}
public static void main(String[] args) {
StringReference();
IntegerReference();
}
}
结果:
a: abc
b: abc
c: 1
d: 1
可以发现b没有变成abcd
,而且d也没有变成12
, 明明是引用类型,什么变化,网上说String类型和基本包装类作为参数传递时会变成值传递,还有一种说法是String和基本包装类构造函数里的value是final类型的,我们知道,被final修饰的变量一旦初始化后就不能被修改。
//Integer.java源码
public final class Integer extends Number implements Comparable<Integer> {
private final int value;
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
}
//String源码
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
public String() {
this.value = new char[0];
}
}
自己实现一个实体类PlainString
:
public class PlainString {
private String value;
public PlainString(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString() {
return this.value;
}
}
测试:
public class ReferenceDemo {
public static void plainString(){
PlainString ps = new PlainString("aaa");
PlainString pls = ps;
changePlainString(pls);
System.out.println("ps: "+ps);
System.out.println("pls: "+pls);
}
public static void changePlainString(PlainString ps){
// ps = new PlainString("bbb"); //注释1
ps.setValue("bbb"); //注释2
}
public static void main(String[] args) {
plainString();
}
}
结果:
//打开注释1
ps: aaa
pls: aaa
//打开注释2
ps: bbb
pls: bbb
如果将PlainString
中变成value用final修饰,那么就不能有setter方法了
public class PlainString {
private final String value;
public PlainString(String value) {
this.value = value;
}
@Override
public String toString() {
return this.value;
}
}
结果:
ps: aaa
pls: aaa
猜测: 刚开始初始对象时在堆上分配内存地址A,在非setter方法赋值的change*
方法,实际上是在堆上分配了一块不同于初始化对象的内在地址B,当出change*
方法体后,GC会回收B的内存地址,相当于没有改变;但使用setter方法的change*
方法,实际上是在同一块内存地址A上修改了value,因此会对指向该内存地址的句柄产生影响