Java的null和值传递,理解透彻至少能打牢Java基础的1/10
public class A {
/***********第一种情况 ,实参不为null的情况下,猜猜b.j=???****************/
int i = 1;
B b = new B();
A(){
setB(b);
System.out.println(b.j);
}
public static void main(String[] args) {
A a = new A();
}
public void setB(B b) {
b=new B();
b.j=10;
}
}
class B{
int j=1;
}
总结:理解 null 和值传递的本质,需明确实参与形参的独立性,值传递仅复制值,引用类型传递的是地址值,修改形参指向的新对象不影响实参原指向的对象(除非形参直接修改实参对象的属性)
第一种情况 ,实参不为null的情况下,猜猜b.j=???
Java 的值传递本质是 “将实参的值复制给形参”
main方法开始执行:
先简单分析一下内存(超简易版)
1.执行A a = new A();
此时内存
此时假设b = new B()的b的地址值是 0x456;
2.执行构造函数里的setB(B b)时
把实参b的地址值 0x456 拷贝给方法参数里的形参b
此时内存
开始执行
setB(B b) 里面的b=new B();此时b指向了一个新地址 假设是0x789,此时内存
下一步 b.j=10;
内存图
3.setB(B b) 执行完毕,为这个方法分配的栈空间消失,此时内存
堆空间的那块j=10,没有引用指向这块内存,后续会被垃圾回收器收走,
执行
System.out.println(b.j);
最终结果 为1
********************************************************************************************************************
public class A {
/***********第二种情况 ,实参为null的情况下,猜猜b.j=???****************/
B b ;
A(){
setB(b);
System.out.println(b.j);
}
public static void main(String[] args) {
A a = new A();
}
public void setB(B b) {
b=new B();
b.j=10;
}
}
class B{
int j=1;
}
-
第一步:创建
A
实例A
对象在堆中创建,成员变量b
初始化为null
(默认值),此时栈中a
变量指向这个A
对象。- 实参:
A
对象的成员变量b
(值为null
,相当于一张没写地址的纸条)。
-
第二步:调用
setB(b)
- 进入
setB
方法时,Java 会将实参b
的值(null
)拷贝给形参b
。 - 形参:
setB
方法内的局部变量b
(值也是null
,相当于用实参纸条复印了一张新纸条,内容都是null
)。 - 此时只有两张纸条:实参
b
(属于A
对象)和形参b
(方法内的局部变量),“拷贝” 是赋值动作,不是第三张纸条。
- 进入
-
第三步:执行
b = new B();
- 形参
b
这张纸条的内容被修改为0x789
(指向新B
对象),但实参b
依然是null
(纸条内容没动)。 - 此时还是只有两张纸条,只是形参纸条的内容变了。
- 形参
为什么不存在 “第三个变量”?
- “拷贝” 是动作,不是变量:Java 的值传递本质是 “将实参的值复制给形参”,这个过程中没有产生新的变量,只是形参变量被赋予了实参的值。
- 类比理解:
- 实参
b
是 “原始纸条”,形参b
是 “复印的纸条”。 - “拷贝” 相当于 “用复印机把原始纸条的内容印到复印纸条上”,这个动作完成后,依然只有两张纸条(原始和复印),没有第三张。
- ************************************************************************************
- 实参
关键误区:“拷贝”≠“新变量”
很多人误以为 “值传递” 会产生一个 “拷贝变量”,但实际上:
- 实参和形参是两个独立的变量,它们的关系是 “形参被实参的值初始化”。
- 就像你有一个苹果(实参),把它的样子画到纸上(形参),纸上的苹果是独立的,不是第三个苹果。