java中的传值和传址总结

本文深入探讨Java中的参数传递机制,包括传值与传址的区别,特别关注引用类型和对象参数的行为变化,通过具体实例帮助理解。
class Foo {
private int x;
public Foo(int x) {
this.x = x;
}
public void setX(int x) {
this.x = x;
}
public int getX() {
return x;
}
}

public class Submit {
static Foo fooBar(Foo foo) {
foo = new Foo(100);
return foo;
}

public static void main(String[] args) {
Foo foo = new Foo(300);
System.out.print(foo.getX() + "-");

Foo fooFoo = fooBar(foo);
System.out.print(foo.getX() + "-");
System.out.print(fooFoo.getX() + "-");

foo = fooBar(fooFoo);
System.out.print(foo.getX() + "-");
System.out.print(fooFoo.getX());
}
}


What is the output of the program shown in the exhibit?
A. 300-100-100-100-100
B. 300-300-100-100-100
C. 300-300-300-100-100
D. 300-300-300-300-100

Answer: B


涉及知识点:
1.Java中的参数传递有传值和传址两种;
2.基本类型和String型作为参数时,为传值方式,只把值传入方法,不管在方法中怎么处理这个参数,原值不变;
3.其他引用类型作为参数时,为传址方式,将指向内存中的地址传入方法,方法中此内存地址中的值发生变化时,原值也会改变;
4.当参数是对象时,无论方法体内进行了何种操作,都不会改变实参对象的引用。
5.当参数是对象时,只有在方法内部改变了对象的内容时,才会改变实参对象内容。
6.例外:
(1)如果引用类型的对象通过传址方式将其指向内存中的地址传入方法后,方法中使用new关键字重新给参数赋值时,会在内存中重新开辟空间,参数指向新的内存空间,此时参数和原对象指向的就不是同一个地址了,参数值的变化不会改变原值;
(2)String型是引用类型,但是String型作为参数,是传值方式,可以通过以下两种方式来理解:
<1>String本质上是基本类型的char[],基本类型作为参数时,为传值方式;
<2> 字符串在内存中是存储在堆中的一个常量,String对象指向内存中这个常量的地址,通过传址方式将地址传入方法后,方法中如果通过字符串给参数赋值,则会重新在堆中创建一个字符串常量,并指向这个地址,原值依然指向原来的字符串常量地址,参数值的变化不会改变原值,如果通过new关键字给参数赋值,参见 (1)中的解释。


解析:
1.“Foo foo = new Foo(300);”,此时foo.getX()的值为300;
2.“Foo fooFoo = fooBar(foo);”,因为Foo是引用类型,main方法中的foo通过传址的方式将其指向的地址传给fooBar方法中的foo,此时两个foo指向同一个地址,foo.getX()的值都为300;通过“new Foo(100)”给fooBar方法中的foo赋值后,该foo重新指向了一个新的地址,foo.getX()的值为新地址中的值100,而main方法中的foo仍然指向原来的地址,foo.getX()的值没有改变,仍为 300;fooBar将foo的值返回给fooFoo,因此fooFoo.getX()的值为100;
3.“foo = fooBar(fooFoo);”,同2中的解释,foo.getX()的值变为100,fooFoo.getX()的值没有变化,仍为100;


另外传值和传址的存储也是不一样的 在传址中有两部分(堆和栈)。
### 引用的概念及区别 #### 1. (Call by Value) 在方式中,实际参数的副本被递给形式参数。这意味着任何对形参的操作都不会影响实参本身[^1]。这种方式通常用于简单的数据类型,比如整数或浮点数。 以下是的一个简单示例: ```c void add(int a, int b) { a += b; // 修改的是a的本地副本 } int main() { int x = 5; int y = 3; add(x, y); printf("%d\n", x); // 输出仍然是5,因为x未受影响 return 0; } ``` #### 2. (Call by Reference/Pointer) 是指将变量的实际内存地作为参数递给函数的形式参数。这样,在函数内部可以直接操作原始变量的内容,从而改变其[^4]。这种机制常见于C/C++等支持指针的语言中。 下面展示了一个利用指针实现乘法功能的例子: ```c void multiplyByTwo(int *pX) { (*pX) *= 2; // 改变原位置上的数 } int main(){ int z=7; multiplyByTwo(&z); printf("Result:%d ",z); // 结果应显示为14 return 0; } ``` 对于Java而言,尽管它不直接暴露指针概念,但对于对象来说实际上采用了一种特殊的“按递”,即递的是对象引用的拷贝而非真正的引用本身[^2]。 #### 3. 引用(Call by Reference with Modern Languages Perspective) 某些现代高级语言提供了更直观的方式处理类似需求——通过显式的引用类型来完成所谓的“引用”。例如,在C++中有专门的关键字`&`表示引用;而在Python里一切皆可视为对象,因此本质上也是基于引用计数管理资源释放等问题[^3]。 考虑如下C++代码片段: ```cpp void swapValues(int &val1, int &val2){ int temp=val1; val1=val2; val2=temp; } // 调用swapValues时无需取地符& ``` #### 区别总结表 | 特性 | **** | ** (通过指针)** | **引用** | |--------------|-----------------------------|------------------------------|---------------------------| | 数据共享 | 不共享 | 共享 | 共享 | | 性能开销 | 较低 | 可能较高(取决于上下文) | 高效 | | 安全性 | 更安全 | 存在潜在危险 | 相当安全 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值