Java经典问题:传值与传引用?

来源:互联网 发布:类似帝国时代的网络 编辑:程序博客网 时间:2024/06/04 23:16

第一部分:


Java到底是传值还是传引用?相信很少有人能完全回答正确。通常的说法是:对于基本数据类型(整型、浮点型、字符型、布尔型等),传值;对于引用类型(对象、数组),传引用。基本类型传值,所有人都不会对此有疑义;问题出在引用类型上。


为引入正题,不妨先看看下面的例子,你能正确给出程序的运行结果嘛?

[java] view plaincopyprint?
  1.      
  2.   public   class  Swap   {    
  3.    
  4.      public  Swap()   {}     
  5.         
  6.      public   static   void  main(String[] args)   {    
  7.         Changer c  =   new  Changer();    
  8.             
  9.         String stra  =   " Mighty " ;    
  10.         String strb  =   " Mouse " ;    
  11.         c.swap(stra, strb);    
  12.         System.out.println(stra  +   "   "   +  strb);    
  13.             
  14.         String[] strArr  =   new  String[ 2 ] ;    
  15.         strArr[ 0 ]  =  stra;    
  16.         strArr[ 1 ]  =  strb;    
  17.         c.swap(strArr);    
  18.         System.out.println(strArr[ 0 ]  +    "   "   +  strArr[ 1 ]);             
  19.     }     
  20.         
  21.      static   class  Changer   {          
  22.          public   < T >   void  swap(T a, T b)   {    
  23.             T temp  =  a;    
  24.             a  =  b;    
  25.             b  =  temp;    
  26.         }     
  27.             
  28.          public   < T >   void  swap(T[] t)   {    
  29.              if  (t.length  <   2 )   {    
  30.                 System.out.println( " error! " );    
  31.                  return ;    
  32.             }     
  33.                 
  34.             T temp  =  t[ 0 ];    
  35.             t[ 0 ]  =  t[ 1 ];    
  36.             t[ 1 ]  =  temp;    
  37.         }     
  38.     }     
  39. }     

上面程序的正确运行结果为:


Mighty Mouse

Mouse Mighty


你答对了嘛?


下面我们来分析一下:为什么会出现上面的运行结果?


为分析这个问题,我们必须对程序中的数据在内存中的布局有一定了解。上面main程序中和String相关的变量共有3个,其布局可以用下图所示:堆区中存放具体对象



当调用swap(stra, strb)函数时,传递的是引用类型stra、strb的拷贝值,因此函数中任何对参数的改变都不会影响到stra和strb的值;而调用swap(strArr)时,传递的是strArr的拷贝值,程序中对参数的任何改变仍然不会影响到strArr的值,然而swap(T[] t)中改变的并不是strArr的值,而是strArr[0]和strArr[1]的值,也就是引用类型strArr所指向的对象的值,因而strArr[0]和strArr[1]的值发生了变化。

从上面的分析,我们可以得出结论:对于引用类型,其实参数传递时仍然是按值传递的;当然,按引用传递也不是完全没有道理,只是参考对象不是引用类型本身,而是引用类型所指向的对象。


第二部分:如果上面的并没有让我们明白其中的道理,请往下看!


问题: 如果Java是用引用来传递的话,为什么交换函数(swap)不起作用呢?

回答: 你的问题引出了Java新手的常犯的错误。事实上,一些老手也很难搞清楚这些概念。

Java确实使用对象的引用来做计算的,所有的对象变量都是引用。但是,Java在向方法传递参数时传的不是引用,是值。


以 badSwap() 函数为例:

[java] view plaincopyprint?
  1. public void badSwap(int var1, int var2)  
  2. {  
  3.     int temp = var1;  
  4.     var1 = var2;  
  5.     var2 = temp;  
  6. }  

当badSwap方法返回时,被当作参数传入的变量仍然保持了原来的值不变。如果我们把传入的int型变量改为Object型也是一样的,因为Java通过传值来传递引用的。现在,我们来看下是哪个地方搞的鬼:


[java] view plaincopyprint?
  1. package com.zz.jquery;  
  2.   
  3. import java.awt.Point;  
  4.   
  5. public class Test {  
  6.       
  7.       
  8.        
  9.     public static void main(String [] args)  
  10.     {  
  11.         Point pnt1 = new Point(0,0);  
  12.         Point pnt2 = new Point(0,0);  
  13.         System.out.println("X: " + pnt1.x + " Y: " +pnt1.y);  
  14.         System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);  
  15.         System.out.println(" ");  
  16.         new Test().tricky(pnt1,pnt2);  
  17.         System.out.println("X: " + pnt1.x + " Y:" + pnt1.y);  
  18.         System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);  
  19.     }  
  20.   
  21.     public void tricky(Point arg1, Point arg2)  
  22.     {  
  23.         arg1.x = 100;  
  24.         arg1.y = 100;  
  25.         Point temp = arg1;  
  26.         arg1 = arg2;  
  27.         arg2 = temp;  
  28.     }  
  29.   
  30. }  

执行这个函数,将得到以下输出:
———————————————————-
X: 0 Y: 0
X: 0 Y: 0

X: 100 Y: 100
X: 0 Y: 0
————————


即使是通过值传递,tricky函数依然成功地改变了pnt1的值。但是pnt1和pnt2的置换失败了。这正是最令人困惑的地方。在main()函数当中,pnt1和pnt2仅仅是对象的引用。当你向tricky()函数传递pnt1和pnt2参数时,Java仅仅向传递任何其他参数一样,通过传值来传递引用。这就意味着:传向函数的引用实际上是原始引用的副本。下面的图一展现了当Java传递对象给函数之后,两个引用指向了同一对象

图一: 当被传递给函数之后,一个对象至少存在两个引用

Java复制并传递了“引用”的值,而不是对象。因此,方法中对对象的计算是会起作用的,因为引用指向了原来的对象。但是因为方法中对象的引用是“副本”,所以对象交换就没起作用。如图2所示,交换动作只对方法中的引用副本起作用了,不影响方法外的引用。所以不好意思,方法被调用后,改变不了方法外的对象的引用。如果要对方法外的对象引用做交换,我们应该交换原始的引用,而不是它的副本。

图二: 只有传入函数的引用交换了,原始引用则没有

这里再附上一个demo:

[java] view plaincopyprint?
  1. package com.zz.jquery;  
  2.   
  3.   
  4. public class TestObject {  
  5.   
  6.        
  7.         public static void main(String [] args)  
  8.         {  
  9.             Person p1 = new Person();  
  10.             Person p2 = new Person();  
  11.             p1.setAge(10);  
  12.             p2.setAge(11);  
  13.              
  14.             System.out.println("before-p1.age:"+ p1.getAge());  
  15.             System.out.println("before-p2.age:"+ p2.getAge());  
  16.             System.out.println(" ");  
  17.             new TestObject().trickyObject(p1,p2);  
  18.             System.out.println("after-p1.age:"+ p1.getAge());  
  19.             System.out.println("after-p2.age:"+ p2.getAge());  
  20.         }  
  21.   
  22.         public void trickyObject(Person p1, Person p2)  
  23.         {  
  24.             p1.setAge(12);  
  25.             p2.setAge(13);  
  26.              
  27.             Person temp = p1;  
  28.             p1 = p2;  
  29.             p2 = temp;  
  30.              
  31.             System.out.println("trickyObject-p1.age:" + p1.getAge());  
  32.             System.out.println("trickyObject-p2.age:" + p2.getAge());  
  33.             System.out.println(" ");  
  34.         }  
  35. }  

输出结果:

before-p1.age:10
before-p2.age:11
 
trickyObject-p1.age:13
trickyObject-p2.age:12
 
after-p1.age:12
after-p2.age:13




0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 去上海没有流量怎么办 去外地没流量怎么办 外地流量不够用怎么办 手机在外地流量怎么办 联通网卡在外地怎么办 套餐流量不够用怎么办 省内流量去省外怎么办 在外省流量不够怎么办 榨汁机开关坏了怎么办 健伍dvd没遥控器怎么办 佳能 显示屏关不了怎么办 iqos充电闪红灯怎么办 航班取消了乘客怎么办 摩托罗拉电话静音了怎么办 对讲机话筒坏了怎么办 摩托罗拉xt1570费电怎么办 主板没有rgb接口怎么办 对讲机频段没了怎么办 怀孕查出宫颈囊怎么办 办养殖场没地怎么办 宝宝睡觉衣服湿透怎么办 开衫衣服往下滑怎么办 迷你世界没有牛怎么办 火龙果树烂了怎么办 误喝鸵鸟墨水怎么办 被鸵鸟啄伤怎么办 被鸵鸟啄住怎么办 鸟翅膀受伤了怎么办 北京卖房户口怎么办 深户挂人才市场集体户小孩怎么办? 郑州房子限购怎么办 外地怎么送东西怎么办 晚上登华山下雨怎么办 北京摇不到号想买车怎么办 北京买车摇不到号怎么办 朋友代购贵了怎么办 赢时通把车开走怎么办 杭州房子租不起怎么办 质量效应2吵架怎么办 学生有两个学籍怎么办 孩子出现双学籍怎么办