再一次 - Java的引用传递与值传递

如果函数内引用没有被改变,函数内部的对象被修改将导致外部的对象变化。

如果函数内部引用发生了改变—引用被切断:(B=A,或者引用了 新new的对象),则函数内部的对象修改不会导致外部对象的变化。

可以简单的记为,在初始化时,“=”语句左边的是引用,右边new出来的是对象。
在后面的左右都是引用的“=”语句时,左右的引用同时指向了右边引用所指向的对象。

传值意味着传的是副本,不会改变被传递对象本身。传引用既是传内存地址(也可认为是内存地址的副本),如果传递的对象发生了改变,那么被传递的对象也将改变(所有某些场景需要深拷贝)


1、对象是按引用传递的,原始数据类型是按值传递的

2、如果把引用副本也当做是一种“值”(java中一个对象s是什么,同样也是一个指针),对于jvm来说,这个引用(内存地址)也仅仅是一个int型的整数,所以可以认为:Java 应用程序有且仅有的一种参数传递机制,即按值传递。

但是,传值和传引用本来就是两个不同的内容,没必要把两者弄在一起,弄在一起反而更不易理解。

对于一切对象型变量,Java都是传引用的副本。其实传引用副本的实质就是复制指向地址的指针,只不过Java不像C++中有显著的*和&符号。(这里Java和C++不同,在C++中,当参数是引用类型时,传递的是真实引用而不是引用副本)



也就是说,参数传递,如果参数是对象的话,那么此时,传递的是引用。所以如果函数内部(形参)让该参数对象发生变化,外部对象(被传递的参数也会发生变化)。


以代码下来证明3,4在参数的传递中,哪一个是正确的?

3、按值传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的一个副本

4、按引用传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的内存地址,而不是值的副本

    首先我们来看看第一点:对象是按引用传递的确实,这一点我想大家没有任何疑问,例如: 


    class Test01 
    { 
    public static void main(String[] args) 
    { 
    StringBuffer s= new StringBuffer("good"); 
    StringBuffer s2=s; 
    s2.append(" afternoon."); 
    System.out.println(s); 
    } 
    } 

    对象s和s2指向的是内存中的同一个地址因此指向的也是同一个对象。 
    如何解释“对象是按引用传递的”的呢? 

    这里的意思是进行对象赋值操作是传递的是对象的引用,因此对象是按引用传递的,有问题吗? 
    程序运行的输出是: 
    good afternoon. 

    这说明s2和s是同一个对象。所以对象的赋值,是传递的引用,他们的已经绑为一体。 

    所以反过来,s.append, 最终打印s2,结果依然是一样的:当引用传递(对象赋值)的时候,无论哪一个对象内容反正了改变,另外一个对象也会发生变化,除非某个对象重新建立一个引用(s2 = new StringBuffer())。

    这里有一点要澄清的是,这里的传对象其实也是传值,因为对象就是一个指针,这个赋值是指针之间的赋值,因此在java中就将它说成了传引用。(引用是什么?不就是地址吗?地址是什么,不过就是一个整数值) 

    再看看下面的例子: 


    class Test02 
    { 
    public static void main(String[] args) 
    { 
    int i=5; 
    int i2=i; 
    i2=6; 
    System.out.println(i); 
    } 
    } 

    程序的结果是什么?5!!! 
    这说明什么,原始数据类型是按值传递的,这个按值传递也是指的是进行赋值时的行为。 


    下一个问题:如果把对象的引用看作内存地址的值,那么Java 应用程序有且仅有的一种参数传递机制,即按值传递.(基本变量传递肯定是值传递)
    

    下面的例子加入了在函数中进行引用赋值的操作,进行之前,先回顾一下Java中,对象与引用的重要概念。


如下表达式:
A a1 = new A();
它代表A是类,a1是引用,a1不是对象,new A()才是对象,a1引用指向new A()这个对象。

在JAVA里,“=”不能被看成是一个赋值语句,它不是在把一个对象赋给另外一个对象,它的执行过程实质上是将右边对象的地址传给了左边的引用,使得左边的引用指向了右边的对象。JAVA表面上看起来没有指针,但它的引用其实质就是一个指针,引用里面存放的并不是对象,而是该对象的地址,使得该引用指向了对象。在JAVA里,“=”语句不应该被翻译成赋值语句,因为它所执行的确实不是一个赋值的过程,而是一个传地址的过程,被译成赋值语句会造成很多误解,译得不准确。

所以当看到"A=B"操作时,要真正理解它为 A→B:A的引用地址变成了B的引用地址。或者说,让A和B都指向同一地址。切忌简单想成A等于B,或者把B的值赋给A。

这样,当函数体中进行引用变换时: A=B, 要考虑:右边的B对应的外部B引用会变,左边的A对应的外部A引用反而不会变:即函数外部B会变(因为B没有被赋予新的引用,所以他对应的值在函数内被改变,而造成了外部B引用也跟着改变[因为对象传递是引用传递]),而外部A引用的打印值不变,是因为在函数内部,A被赋予了新的引用【B】,所以外部A与函数内部A的引用关系被切断,他们指向了不同的对象。

再如:
A a2;
它代表A是类,a2是引用,a2不是对象,a2所指向的对象为空null;

再如:

a2 = a1;
它代表,a2是引用,a1也是引用,a1所指向的对象的地址传给了a2(传址),使得a2和a1指向了同一对象。



    class Test03 
    { 
    public static void main(String[] args) 
    { 
    StringBuffer s= new StringBuffer("good"); 
    StringBuffer s2=new StringBuffer("bad"); 
    test(s,s2); 
    System.out.println(s);//9 
    System.out.println(s2);//10 
    } 

    static void test(StringBuffer s,StringBuffer s2) { 
    System.out.println(s);//1 
    System.out.println(s2);//2 
    s2=s;//3 改变s2的引用,使s2指向引用对象s,即s2和s都指向同一对象

    s=new StringBuffer("new");//4 

/*
* note: 本来s被new 了新的,引用被切断也不应该被改变外部的s,但是因为s2=s(且s2在函数中没有被赋予新的引用),那么s2的变化
* 才导致外部s的变化。

* important: 如果3,4调换顺序,则s的引用被改变成new,且s2指向s相同的新引用new,那么相对于外部引用来说,函数内部的s和s2,他们的引用都发生了变化
* 所以外部(main)的打印结果,保持不变。
*/
    System.out.println(s);//5 s被赋予了新的引用,所以打印新值: new
    System.out.println(s2);//6 对象的赋值是引用传递,但关联引用(s2-->s)不会实时变化,所以s2的引用地址还是老的s,这个跟s2=s;s.append("new"), 然后导致s2也                            //会被append "new"不一样。
    s.append("hahs");//7 此时函数内s, 是newhahs1, 对于外部s来说,这个append不会起作用,因为第4步里,s被重新设置了引用.
    s2.append("hahs2");//8 因为s2=s的关系,此时s2.append相当于s.append。且s2指向的是老的s引用地址,则此时,外部s,仍然是good,所以函数执行完毕后s的打

                       //是good+ s2.append的值【并没有受到new StringBuffer的影响】

      /* 8 等价于s.append("hahs2"), 因为在第3步中这个引用对象被赋予了新的引用:“s”, 等同于切断外部s2和内部s2的 相同引用传递。则引用赋值s2=s的左边s2

* 是被切断的引用,那么外部的s2(原来那个引用对象s2)就和此时的s2毫无关联,他们指向了不同的对象。s也被new了新的引用,所以外部s本该不变,但

        * 第8行,因为s2与s已经连为一体(引用赋值),所以s2变化将绑定在老s(外部s)上,因为他们是同一地址。则s2的append,实际上对于外部的s来说,是

        * 相当于进行了s.append("hahs2") 的操作

* 所以不论函数里面的s,s2如何变化,都不会影响外部的那个引用对象s2(原来的s2),故调用方法前后的s2没有发生变化。
       */

    } 
    } 

    程序的输出是: 
    good 
    bad 
    new 
    good 
    goodhahs2
    bad

   Note:test方法传递的值,所以9,10中,s2 原来的值不变。
   Need to think: s为什么变成了goodhah?

    对于基本類型和字符串(字符串特殊,因为其是immutable,所以传递字符串的时候,对象作为参数在函数内部发生变化,外部这个对象,是不会变的)是传值,但是对于对象而言,传的是引用,而引用指向的是同一個對象!所以函数外部的s和s2的地址一致(绑定在了一起,操作同一块内存空间),而s2被append了,所以s发生了变化。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值