Java 函数参数是对象的时候不是按值传递也不是按引用传递吧?好像是二者的结合?

申祷无 2020-08-28 02:43:40
或者说,参数是对象的时候,传递的是引用的一个副本?这个说法好像也不太好。好像看成是传递的是指针变量更好一些?只不过不需要解引用。但好像 Java 的函数参数传递不是标准的按值传递也不是标准的按引用传递。
《Java 编程思想》说传递的是引用,《Java 核心技术》说传递的是值。网上好像也有各种说法。
如果是按值传递,下面的代码是不应该导致对象的值交换的:
class TestClass {
private int m;
public TestClass(int member) {
m = member;
}
public String toString() {
return "" + m;
}
public void setMember(int member) {
m = member;
}
public int getMember() {
return m;
}
}

public class SwapTest {
static void swap(TestClass x, TestClass y) {
TestClass tmp = new TestClass(x.getMember());
x.setMember(y.getMember());
y.setMember(tmp.getMember());
}
public static void main(String[] args) {
TestClass a = new TestClass(7);
TestClass b = new TestClass(8);

swap(a, b);

System.out.println(a);
System.out.println(b);
}
}
如果是按值传递,对 x 和 y 进行操作是不应该影响 a 和 b 的。
如果是按引用传递,就像《Java 核心技术》(第十版)119 页说的那样,也是不合理的。所以结论好像只能是二者都不是。
...全文
7428 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
鸡窝里的毛 2020-08-29
  • 打赏
  • 举报
回复
究竟什么值?什么是引用?对象的地址是值么,还是对象本身是值呢?定义的变量是引用么?还是对象是引用呢?你在通过变量对一个对象进行操作的时候,你是改变了引用呢?还是改变了值呢?
申祷无 2020-08-29
  • 打赏
  • 举报
回复
引用 6 楼 qybao 的回复:
没错,按值传递就是将整个实参复制,就是将实参的内存信息拷贝到形参的内存。但是你不要忘了,如果实参是指针,那实参的内存信息实际只是保存个地址而已,所以复制到形参,也是复制个地址而已(并不是复制地址所指向的对象本身)。

所以java里就是纯粹的值传递。java的对象存在于堆里,你不可能把整个对象的堆内存信息复制到方法栈里(java规约就不允许对象存在于栈里),而且实参也只是个指针,只是保存对象的地址,并不是保存对象本身,所以这个值传递从头到尾都不存在对象的复制,只是简单的地址复制。(java不像c++,在栈里还能存放对象,即使c++的栈里可以存放对象,函数调用也是值传递,把整个对象复制一份传递到函数)

最简单的理解就是用赋值语句来理解
int a = 5; //a相当于实参
int b = a; //b相当于形参,b的内存地址不同于a的内存地址,但是b的内存信息和a的内存信息相同,这就是值传递

在c++里,一个类如果有拷贝构造函数,你用以下的赋值的方法
A a(5);//定义一个A类型变量a
A b = a; //把a赋值给b,你会发现这里会调用拷贝构造函数,把a拷贝给b;
同样的,把a传递给一个函数
fun(a);//你会发现这里也会调用拷贝构造函数,把a拷贝给形参
说明实参传递到形参,就是个纯粹的赋值语句,就是 形参 = 实参 这样的赋值。按汇编指令来说,就是mov 实参 到 形参,也就是把实参的内存信息放到形参的内存
你说的值指的是实参而不是和实参相关联的对象?我一直以为值指的是跟实参相关联的那个对象。但是我印象中 C++ 的值指的是跟指针或者引用相关联的对象。不过也可能是我当时就理解错了。那假如你说的 Java 的方式是存粹的按值传递的话,那么直接传递整个对象的叫什么?你觉得这二者(Java 的方式和传递整个对象)是完全相同的吗?
qybao 2020-08-29
  • 打赏
  • 举报
回复
引用 5 楼 申祷无 的回复:
是我理解有错吗?我的理解是按值传递传递参数的时候会将实参整个复制,我印象中 C++ 好像就是这样的。不过我 C++ 相关的东西好像基本都忘光了。你的意思是只要实参的“地址”复制了一份就是按值传递?我这么问吧,你觉得是否存在所谓的“纯粹的”按值传递?所谓的“纯粹的”按值传递就是我前面说的将实参整个复制。而传递地址和 JAVA 的参数传递都不是“纯粹的”的按值传递?


没错,按值传递就是将整个实参复制,就是将实参的内存信息拷贝到形参的内存。但是你不要忘了,如果实参是指针,那实参的内存信息实际只是保存个地址而已,所以复制到形参,也是复制个地址而已(并不是复制地址所指向的对象本身)。

所以java里就是纯粹的值传递。java的对象存在于堆里,你不可能把整个对象的堆内存信息复制到方法栈里(java规约就不允许对象存在于栈里),而且实参也只是个指针,只是保存对象的地址,并不是保存对象本身,所以这个值传递从头到尾都不存在对象的复制,只是简单的地址复制。(java不像c++,在栈里还能存放对象,即使c++的栈里可以存放对象,函数调用也是值传递,把整个对象复制一份传递到函数)

最简单的理解就是用赋值语句来理解
int a = 5; //a相当于实参
int b = a; //b相当于形参,b的内存地址不同于a的内存地址,但是b的内存信息和a的内存信息相同,这就是值传递

在c++里,一个类如果有拷贝构造函数,你用以下的赋值的方法
A a(5);//定义一个A类型变量a
A b = a; //把a赋值给b,你会发现这里会调用拷贝构造函数,把a拷贝给b;
同样的,把a传递给一个函数
fun(a);//你会发现这里也会调用拷贝构造函数,把a拷贝给形参
说明实参传递到形参,就是个纯粹的赋值语句,就是 形参 = 实参 这样的赋值。按汇编指令来说,就是mov 实参 到 形参,也就是把实参的内存信息放到形参的内存







申祷无 2020-08-29
  • 打赏
  • 举报
回复
引用 4 楼 qybao 的回复:
严格来说java只有值传递,所谓引用传递只是为了强调这个值是个地址
对于c++来说,指针(地址)传递也是值传递,只有参数为引用&类型才是引用传递
对于形参来说,它是实参的拷贝(这是值传递的特征,形参实参地址不同但值相同),形参在被调用的方法栈里,实参在调用方的方法栈里,各自有自己的内存地址,只是地址里的值(内容)相同,所以是值传递。
而c++的引用传递是,形参虽然有自己的地址(和实参地址不同,因为在不同的方法栈),但是形参本身的地址不能获取,一获取会被自动寻址到实参的地址,也就是从技术上实现了形参和实参是一体共用同一个地址,这才是所谓的引用传递(形参和实参不仅值相同,地址也相同)

是我理解有错吗?我的理解是按值传递传递参数的时候会将实参整个复制,我印象中 C++ 好像就是这样的。不过我 C++ 相关的东西好像基本都忘光了。你的意思是只要实参的“地址”复制了一份就是按值传递?我这么问吧,你觉得是否存在所谓的“纯粹的”按值传递?所谓的“纯粹的”按值传递就是我前面说的将实参整个复制。而传递地址和 JAVA 的参数传递都不是“纯粹的”的按值传递?
qybao 2020-08-29
  • 打赏
  • 举报
回复
java里不能直接传递整个对象,因为java规范约定对象在堆里,不能复制到栈里来(当然现在的一些编译优化也能在栈里保存对象,但是并不适用于参数传递) 直接传整个对象也分情况,如果这个对象是原实参对象的拷贝,那就是值传递(c++的指针传递属于这一种);如果是这个对象是实参本身那就是引用传递(c++的引用&传递属于这一种,因为c++的栈里可以存对象,引用&本身就是该对象的别名)
qybao 2020-08-28
  • 打赏
  • 举报
回复
严格来说java只有值传递,所谓引用传递只是为了强调这个值是个地址 对于c++来说,指针(地址)传递也是值传递,只有参数为引用&类型才是引用传递 对于形参来说,它是实参的拷贝(这是值传递的特征,形参实参地址不同但值相同),形参在被调用的方法栈里,实参在调用方的方法栈里,各自有自己的内存地址,只是地址里的值(内容)相同,所以是值传递。 而c++的引用传递是,形参虽然有自己的地址(和实参地址不同,因为在不同的方法栈),但是形参本身的地址不能获取,一获取会被自动寻址到实参的地址,也就是从技术上实现了形参和实参是一体共用同一个地址,这才是所谓的引用传递(形参和实参不仅值相同,地址也相同)
shy078 2020-08-28
  • 打赏
  • 举报
回复
class TestClass {
  private int m;
  public TestClass(int member) {
    m = member;
  }
  public String toString() {
    return "" + m;
  }
  public void setMember(int member) {
    m = member;
  }
  public int getMember() {
    return m;
  }
}
 
public class SwapTest {
  static void swap(TestClass x, TestClass y) {
    TestClass tmp = x;
     x = y;
     y =tmp;
  }
  public static void main(String[] args) {
    TestClass a = new TestClass(7);
    TestClass b = new TestClass(8);
     
    swap(a, b);
     
    System.out.println(a);
    System.out.println(b);
  }
这个是正确演示值传递的事例。 你写的交换方式,是因为x,a指向同一块地址,当指向内容被更改,那就意味着a指向的内容也被更改。所以大部分人认为java只有值传递,没有引用传递。
清欢无白茶 2020-08-28
  • 打赏
  • 举报
回复
引用 1 楼 清欢无白茶 的回复:
我想有点很明白 java参数的传递是值传递;但是作为基本数据类型的时候 传递的是值得本身;当参数是引用数据类型的时候传递的是地址;我们所谓的对象也是一个地址,指向的是堆内的一个空间。
纠正个错误,就是我说的对象只是个引用,语句表述有点问题,正确的是对象名只是一个引用
清欢无白茶 2020-08-28
  • 打赏
  • 举报
回复
我想有点很明白 java参数的传递是值传递;但是作为基本数据类型的时候 传递的是值得本身;当参数是引用数据类型的时候传递的是地址;我们所谓的对象也是一个地址,指向的是堆内的一个空间。

62,628

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧