Java参数传递中的传值和传址的区别

本文通过实例对比Java中的传值与传址,并详细解释了基本类型、String类型及引用类型的参数传递机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

昨天一个人跑到昆山市区的新华书店,看到了一本叫做《你必须知道的261个java语言问题》,讲的都是一些很基础,但大家几乎都没明白的问题。至少对我来说是这样的。感觉很不错,不过并没有下手,因为新华书店的书都是不打折的,所以准备回来再往上订购。看到其中一个题目就是参数传递中的传值和传址的区别,讲的很清楚。引因为上次面试的时候面试官就问道了我这道题,再会想到自己当时的回答,惭愧啊!
不多说了,早上起来试着自己写了一段代码,以证实书上所讲的内容

public class CallValueOrAddress{
private String name;
private int age;

public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}

public void setAge(int age){
this.age=age;
}
public int getAge(){
return age;
}

public static void main(String agrs[]){
String str_name="wangxianxing";
String new_name="nihao";
int int_age=21;
int new_age=22;
System.out.println("传值引用");
System.out.println("姓名:"+str_name+"年龄:"+int_age);
new_name=str_name;
new_name=new String("nihaoma?");
new_age=int_age;
new_age=22;
System.out.println("姓名:"+str_name+"年龄:"+int_age);

CallValueOrAddress cvoa=new CallValueOrAddress();
cvoa.setName("wangxianxing");
cvoa.setAge(21);
System.out.println("传址引用");
System.out.println("姓名:"+cvoa.getName()+"年龄:"+cvoa.getAge());
CallValueOrAddress new_cvoa=cvoa;
new_cvoa.setName("kingxianstar");
new_cvoa.setAge(22);
System.out.println("姓名:"+cvoa.getName()+"年龄:"+cvoa.getAge());
System.out.println("例外情况一");
new_cvoa=new CallValueOrAddress();
new_cvoa.setName("kingxianstar+kingxianstar");
new_cvoa.setAge(32);
System.out.println("姓名:"+cvoa.getName()+"年龄:"+cvoa.getAge());
}


}



运行结果如下:
传值引用
姓名:wangxianxing年龄:21
姓名:wangxianxing年龄:21
传址引用
姓名:wangxianxing年龄:21
姓名:kingxianstar年龄:22
例外情况一
姓名:kingxianstar年龄:22

感觉还不是很全面,所以在网上又搜索了一下,发现了这篇(http://mgc.name/article.asp?id=842)


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.例外:
(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;


另外传值和传址的存储也是不一样的 在传址中有两部分(堆和栈)。
调用调用是程序设计语言中函数或过程调用时递参数的两种基本机制。 ### 调用(Call by Value) 在这种调用方式下,实际参数的具体会复制给形参。这意味着,在被调用的过程中对形参所做的任何修改都会影响到实参本身。它通常用于需要保护原始数据变的情况下,确保外部环境的数据安全性与稳定性。 **特点:** - 参数传递的是变量的一份拷贝; - 函数内部的操作会改变原变量的状态; - 更加安全可靠,因为存在副作用; ```python # Python 示例 (Python 中所有可变类型都是按递) def modify(x): x = 10 a = 5 modify(a) print(a) # 输出仍为5,受函数内操作的影响 ``` ### 调用(Call by Reference 或 Call by Address) 而地址则是将实际参数内存位置的信息提供给了形式参数,使得两者共享相同的存储空间。因此如果在过程中改变了形参的内容,则相应的结果同样会在实参上体现出来。这种方式可以节省时间空间开销,并允许返回多个计算结果等优点。 **注意:** 某些高级语言如 C++、C#, Java 支持直接引用类型的递, 其他像 Python 则是以对象引用的方式间接实现了类似的效果. **特点:** - 直接作用于原有数据; - 可能会影响原来的变量内容; - 提高效率并简化某些算法; ```cpp // C++ 示例 void swap(int &x , int &y){ int temp=x; x=y; y=temp; } int main(){ int a=4,b=6; cout << "交换前:" << endl; cout<<"a="<<a<<", b="<<b<<endl; swap(a,b); cout<<"\n交换后:"<<endl; cout <<"a=" <<a<< ", b="<<b ; } /* 运行以上代码将会打印出: 交换前: a=4, b=6 交换后: a=6, b=4 */ ``` 这两种调用模式各有优劣,开发者应当根据具体情况选择合适的策略来编写高效可靠的软件系统。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值