java传参怎么理解_如何理解Java的值传递

结论

为了加深印象,先把结论放在文章开头。

++Java中只有值传递++。

形参与实参

在理解Java的值传递

实参Argument

实际参数,主调用函数传递给调用函数的参数

形参Parameter

形式参数,并非实际存在的变量,只在函数定义的函数内部使用。在调用函数时,实参将会给形参赋值,从而实现主调函数向调用函数传输数据的目的。

所谓的传递,就是实参给形参赋值的过程。这是我们理解值传递和引用传递的基础。

基本类型与引用类型

基本类型的变量保存值,即值就是变量自身。

而引用类型的变量的值表示对象的引用地址,而非对象自身。

Java内存空间分为 heap 和 stack。对于基本类型的局部变量而言,空间直接在 stack 中分配(例如int a,虚拟机会为a在 stack 中分配一个四字节的内存空间,用来存放a的值),而对于引用类型的局部变量而言,虚拟机会现在 stack 中分配一个四字节的空间,用来存放指向 heap 的地址,同时在 heap中分配一块地址用来存放对象。

按值传递和引用传递

按值传递

所谓值传递,就是在实参为形参赋值的时候,将值赋给形参。

而引用传递,是是在实参为形参赋值的时候,直接将引用(即地址)赋给形参。

所谓的 Java 按值传递,就是说明实参为形参赋值时,只存在值副本的拷贝。

测试代码

一个简单的测试代码:

public class ValOrRef {

public static void main(String[] args){

System.out.println("SCENE 1:--------------------");

scene1();

System.out.println("SCENE 2:--------------------");

scene2();

System.out.println("SCENE 3:--------------------");

scene3();

}

protected static void scene1(){

int argument = 1;

incrVal(argument);

System.out.println("After method invoke, argument: " + argument);

}

protected static void scene2(){

Foo argument = new Foo("original");

changeStr(argument);

System.out.println("After method invoke, argument: " + argument);

}

protected static void scene3(){

Foo argument = new Foo("original");

createNewInstance(argument);

System.out.println("After method invoke, argument: " + argument);

}

private static void incrVal(int parameter){

parameter = parameter + 1;

System.out.println("parameter: " + parameter);

}

private static void changeStr(Foo parameter){

parameter.setStr("changed");

System.out.println("parameter: " + parameter);

}

private static void createNewInstance(Foo parameter){

parameter = new Foo("Brand New");

System.out.println("parameter: " + parameter);

}

static class Foo{

private String str;

public Foo(String str){

this.str = str;

}

public String getStr() {

return str;

}

public void setStr(String str) {

this.str = str;

}

@Override

public String toString() {

return "Foo{" +

"str='" + str + '\'' +

'}';

}

}

}

先看输出的测试结果:

SCENE 1:--------------------

parameter: 2

After method invoke, argument: 1

SCENE 2:--------------------

parameter: Foo{str='changed'}

After method invoke, argument: Foo{str='changed'}

SCENE 3:--------------------

parameter: Foo{str='Brand New'}

After method invoke, argument: Foo{str='original'}

Scene1分析

对于Scene1而言,首先主调函数scene1()方法在 自己的函数调用栈上分配了一个四字节的空间,用来存放 argument的值(值为1)。然后在调用incrVal(int val)时,会在incrVal()自己的函数调用栈上,为parameter同样分配一个四字节的空间,同时拷贝一份argument的值(值为1)赋给parameter。

此时argument和parameter除了值相等之外,没有任何联系。因此在incrVal()内部修改了parameter的值对argument没有任何影响。

Scene2分析

同样对于Scene2而言,主调函数在自己的函数调用栈上分配了一个四字节空间,用来存放argument的引用,其值是指向 heap 的一个地址,虚拟机可以通过地址值从 heap 中找到指定的对象。主调函数在调用changeStr()时,虚拟机为changeStr()同样分配了四字节空间存放paramter的值,并将argument的值(指向 heap的地址)赋值给parameter。

和Scene1相同,argument 和 parameter 除了值相等外,没有其他关联。

当changeStr()内部调用paramter.setStr()时,虚拟机会根据paramter的值找到 heap 上对应的Foo对象,然后修改了Foo对象上str所指的值。

由于paramter和argument的值指向了相同的对象,因此argument的对象也发生了改变。

但赋值的过程依旧是按值传递。

Scene3分析

对于Scene3,argument保存在主调函数的函数调用栈上,然后在调用createNewInstance时,把值(表示 heap 上的某个空间地址)赋值给了parameter。而在函数内部,parameter的值又被修改成了新创建对象的地址。但是外部argument的值仍然是指向之前的对象。因此argument并未发生改变。

总结

最后在总结一次,Java 只存在按值传递。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值