java值传递
java值传递是指地址值的传递,先看一个例子
public class Demo {
String str = "aa";
char[] ch = {'a','b'};
public void test(String str, char[] ch){
str = "bb";
ch[0] = 'c';
}
public static void main(String[] args) {
Demo demo = new Demo();
demo.test(demo.str, demo.ch);
System.out.println(demo.str); //aa
System.out.println(demo.ch); //c,b
}
}
根据上图分析执行过程:
- 创建Demo对象,初始化成员变量,str 赋值引用地址:0x22,ch 赋值引用地址:0x11。
- 调用test方法,test(0x22, 0x11),形参str = 0x22,ch = 0x11,需要注意的是,test方法的形参str和ch,与成员属性str和ch,完全是两码事。两者是独立的,只是名字相同而已。
- 执行test方法,形参str = ‘bb’,即str = 0x55, 这时成员属性str存储的引用地址仍然是0x22。然而形参ch存储的引用地址与成员属性ch存储的引用地址相同,均为0x11,因此形参ch[0] = ‘c’,即ch[0] = 0x66,会引起堆内存中真正的 ch[] 产生变化,0x11的值由0x33变为0x66。
- 最终结果可见,成员属性str和ch的引用地址并未改变,但是ch指向堆内存的ch[]已发生了变化。
String的理解
String str1 = "abc"
String str2 = new String("abc")
System.out.println(str1 == str2); //false
String str1= “abc”; 在编译期,JVM会去常量池来查找是否存在“abc”,如果不存在,就在常量池中开辟一个空间来存储“abc”;如果存在,就不用新开辟空间。然后在栈内存中开辟一个名字为str1的空间,来存储“abc”在常量池中的地址值。
String str2 = new String(“abc”);在编译阶段JVM先去常量池中查找是否存在“abc”,如果过不存在,则在常量池中开辟一个空间存储“abc”。在运行时期,通过String类的构造器在堆内存中new了一个空间,然后将String池中的“abc”复制一份存放到该堆空间中,在栈中开辟名字为str2的空间,存放堆中new出来的这个String对象的地址值。
也就是说,前者在初始化的时候可能创建了一个对象,也可能一个对象也没有创建;后者因为new关键字,至少在内存中创建了一个对象,也有可能是两个对象。
以下例子原因可参考 https://blog.csdn.net/qq_41376740/article/details/80338158
String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hel" + "lo";
String s4 = "Hel" + new String("lo");
String s5 = new String("Hello");
String s6 = s5.intern();
String s7 = "H";
String s8 = "ello";
String s9 = s7 + s8;
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // true
System.out.println(s1 == s4); // false
System.out.println(s1 == s9); // false
System.out.println(s4 == s5); // false
System.out.println(s1 == s6); // true