Java值传递和引用传递

先明晰一下文中值传递和引用传递的含义(关于对引用的定义的争议请参考评论区)。

值传递:方法调用时,实参把它的值传递给对应的形参(或者说副本),方法执行中形式参数值的改变不影响实际参 数的值。
引用传递:也称为传地址。方法调用时,实参的引用(地址,而不是参数的值)被传递给方法中相对应的形参,在方法执行中,对形参的操作实际上就是对实参的操作,方法执行中形式参数值的改变将会影响实际参数的值。

两个看似没什么区别的代码

代码一

public static void main(String[] args) {
    List<Object> objects = null;
    foo(objects);
}

public static void foo(List<Object> objectsCopy) {
    objectsCopy = new ArrayList<>();
    objectsCopy.add(new Object());
    // Bla bla bla
}

代码二

public static void main(String[] args) {
    List<Object> objects = new ArrayList<>();
    foo(objects);
}

public static void foo(List<Object> objectsCopy) {
    objectsCopy.add(new Object());
    // Bla bla bla
}

真的没有区别吗?

以上是我最近写代码时遇到的,当我使用第一种写法的时候,我发现objects 一直是null,略微诧异了一会,我换了第二种写法,问题解决。

老司机可能一看就知道了,这是一个值传递和引用传递的经典问题。

那么为什么第一个不是引用传递?难道List不是引用类型吗?一图胜过千言万语,先来张图解释一下。

引用本身以及基本数据类型是存放在栈里的,而引用类型所指向的内容存放在堆内。据此,画出了上图所示内容,objects表示引用本身(堆中的地址,后文地址均表示此意),而content表示引用指向的具体内容(后文也均使用该词表示引用指向的具体内容)。
解释:
代码一中,引用objects,其值为null,所以没有指向任何堆内存;
当我们调用foo方法时,引用objects首先会在foo 方法中被拷贝一份副本objectsCopyobjectsCopy同样也不指向任何堆内容(在foo方法中,所有的操作都是通过objects 的副本来操作的);
foo 方法中objectsCopy = new ArrayList<>(); 语句被调用时,content的地址&content(&在C/C++中表示取地址)就会被写到栈objectsCopy,此时objectsCopy 就指向了content;
可以很清楚的看到,接下来所有的操作都会改变content的内容,但是很遗憾,objects不会有任何改变,始终为null。

代码二中,main函数中执行了语句objects = new ArrayList<>();,于是引用objects指向堆中的content
当我们调用foo方法时,引用objects首先会在foo 方法中被拷贝一份副本objectsCopy ,因为objects指向堆中的content,于是objectsCopy指向堆中的content
接下来对objectsCopy 的所有的操作都会改变content的内容,因为objects指向的也是content,所以就改变了objects指向的内容。这就是著名的引用传递。

那么代码一是什么传递?
函数中修改一个存放在栈中的数据,而传递进来的参数是它本身,这是什么传递?或者说函数传了一个引用参数(地址),而现在修改的是引用本身,这是什么传递?
这就是地地道道的、彻头彻尾的goddamn值传递。

仅看表面上传递的是引用类型还是值类型是无法判断这将是值传递还是引用传递,这要取决于你具体的操作是改变引用本身(地址)还是引用指向的内容(content)。

尽管在代码一中,我传递的是一个引用类型,但是我修改的是引用本身,所以它是值传递,它真正操作的部分如上图所示;代码二中我修改的是content,但我传递的是content的引用,所以它才是引用传递。
因此,修改A,传递A的引用,这就是引用传递;修改A,传递A,这就是值传递。传递引用类型不是引用传递。

好绕啊。。。C++或许更好理解一些。

修改引用指向的内容(x和y),传递的是地址(指针)int xint y(地址可以对应理解为Java中的引用),这就是引用传递。

void swap(int x, int y)
{
    int temp = *x;
    x = y;
    *y = temp;
}

修改指针,传递的也是指针本身,这就是值传递。

void foo(int x, int y)
{
    x = new int(2);
    y = new int(5);
}

尽管我自认为在C/C++中就已经将这两种传递理解得很透彻了,但是不经意间这错误还是犯得彻彻底底。为此,我总结出这样的一句话:

如果你想修改引用指向的内容,你需要传递引用;如果你想要修改引用本身的值,那么你需要传递引用的引用,否则那只是穿上了引用外衣的值传递。

最后再补充一点C++中值传递和引用传递。

值传递

void swap(int x, int y)
{
    int temp = x;
    x = y;
    y = temp;
}

地址传递

void swap(int x, int y)
{
    int temp = *x;
    x = y;
    *y = temp;
}

引用传递

void swap(int &x, int &y)
{
    int temp = x;
    x = y;
    y = temp;
}

除值传递外,地址和引用传递都会改变x和y的值。

时间: 2024-10-21 14:54:46

Java值传递和引用传递的相关文章

对象-java传递 ”值传递和引用传递“还是全部 “值传递”

问题描述 java传递 "值传递和引用传递"还是全部 "值传递" 若说值传递.基本类型是copy的值赋给形参,对象是把 堆中对象的 地址 传给 形参. 若说分为值传递和引用传递..引用传递 是因为这个传的地址,导致实参和形参 操作的都是同一块内存. 有点乱,像文字层次上的撕逼.. 若往引用传递方面思考,C++的指针又把我搅乱了.,java里不能直接管理内存,传递的不是 变量本身 的栈地址.而是变量的内容(值) 解决方案 java没有什么值传递引用传递,都是传递对象引

java的值传递和引用传递

java的值传递和引用传递 1. "="是赋值操作. 任何包含=的如+=.-=. /=等等,都内含了赋值操作. 不再是你以前理解的数学含义了,而+ - * /和 = 在java中更不是一个级别,换句话说, = 是一个动作,一个可以改变内存状态的操作,一个可以改变变量的符号,而+ - * /却不会. 这里的赋值操作其实是包含了两个意思: 1.放弃了原有的值或引用: 2.得到了 = 右侧变量的值或引用.Java中对 = 的理解很重要啊!!可惜好多人忽略了,或者理解了却没深思过. 2. 对于

Java中的值传递和引用传递

当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?     答:是值传递.Java 编程语言只有值传递参数.当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本.指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的. Java参数,不管是原始类型还是引用类型,传递的都是副本(有另外一种说法是传值,但是说传副本更好理解吧,传值通常是相对传址而言).    

Java中的值传递和引用传递实例介绍_java

复制代码 代码如下: package Object.reference; public class People {     private String name;     private int age;     public People(){     }     public People(String name, int age) {         super();         this.name = name;         this.age = age;     }    

java中如何区分值传递和引用传递

java中值传递和引用传递一直饱受争议难以区分,下面我通过几个例子来区分一下什么时间是值传递,什么时间是引用传递 1:首先先说值传递:基本类型(int ,float ,long,byte,short ,double, char,boolean)作为参数传递时,是传递值的拷贝,无论你怎么改变这个拷贝,原值是不会改变的 package com.test.list; public class Test1 { public static void main(String[] args) { int i

java及C++中传值传递、引用传递和指针方式的理解_java

java的值传递理解: 代码1: public class Test { /** * @param args */ public static void main(String[] args) { StringBuffer buffer= new StringBuffer("colin"); SChange(buffer); System.out.println( buffer); } public static void SChange (StringBuffer str) { st

浅谈JavaScript 函数参数传递到底是值传递还是引用传递_javascript技巧

在传统的观念里,都认为JavaScript函数传递的是引用传递(也称之为指针传递),也有人认为是值传递和引用传递都具备.那么JS的参数传递到底是怎么回事呢?事实上以下的演示也完全可以用于Java 首先来一个比较简单的,基本类型的传递: function add(num){ num+=10; return num; } num=10; alert(add(num)); aelrt(num); //输出20,10 对于这里的输出20,10,按照JS的官方解释就是在基本类型参数传递的时候,做了一件复制

Android For JNI(三)——C的指针,指针变量,指针常见错误,值传递,引用传递,返回多个值

Android For JNI(三)--C的指针,指针变量,指针常见错误,值传递,引用传递,返回多个值 C中比较难的这一块,大概就是指针了,所以大家还是多翻阅一下资料,当然,如果只是想了解一下,看本篇也就够了,不过我也尽量陈述的很详细 一.指针 要说指针,其实通俗易懂的话来说,他应该是保存内存地址的一个变量,我们来看一下小例子 #include <stdio.h> #include <stdlib.h> main(){ //int 变量 int i ; i = 5; //打印i的值

String类型传递是值传递,char[]类型传递是引用传递的实现_java

如下所示: package com.lstc.test; public class TestDemo3 { String str = new String("hello"); char[] ch = { 'a', 'b' }; public static void main(String[] args) { TestDemo3 t = new TestDemo3(); t.change(t.str, t.ch);//String是封装类,是值传递,char数组是引用传递 System.