java中传值和传址及其引伸深度克隆的思考

原创 2004年06月01日 16:06:00

       大家都知道java中没有指针。难道java真的没有指针吗?句柄是什么?变量地址在哪里?没有地址的话简直不可想象!

      java中内存的分配方式有两种,一种是在堆中分配,一种是在堆栈中分配,所有new出来的对象都是在堆中分配的,函数中参数的传递是在栈中分配的。通常情况下堆的内存可以很大,比如32位操作系统中的虚拟内存都可以被堆所使用(当内存紧张的时候甚至硬盘都可以是堆的存储空间),而堆栈的内存分配是有限的。这和c++中内存分配差不多(c++中还要有另一种方式用于全局变量或者局部静态变量的内存分配,这里就不说了)。java中有几种基本类型如int,float,double,char,byte等,他们不是对象,除此之外一切都是对象,所有的对象都是在堆上分配的。java中对象数组是什么,和c++类似,是句柄数组或者叫指针数组,里面保存的是每个元素的地址。和c++中不同,java没有操作符重载和拷贝构造函数(如果不了解这些也没有关系),因此当创建对象或者对已经创建的对象赋值时(注意是对象,不是基本类型):Object a=new Object 和Object a=b(b是Object的子类型或者同类型)时,进行的是对象地址的传递并复制。这就是所说的句柄的传递和赋值。句柄里存储的就是对象的地址,句柄就是指针,只不过是你无法得到的地址,java就是通过这一点巧妙的将指针隐藏起来。当对象作为参数传递到方法中时,传递的就是对象的地址,而行参中保存的是实参地址的副本(这就是最关键的地方,也是值传递,值传递就是将实参的值的副本作为行参)如:


public class Example{
  int i=0;
}
public class A{
  public int i=0;
  public Example add0(Example e)
  {
     e.i++; 
     return e; 
   }
   
   public void add1(Example e)
   {
      e.i++;
   }

   public void modify0(Example e)
   { 
      Example b=e;//将e行参对象的地址赋给句柄b
      b.i++;//也同时修改了e.i和实参的值
   }
   
   public void modify1(Example e)
   {
     e=new Example();
     e.i++;
   }
   public static void main(String[] args)
  {
     Example ex=new Example();
     A a=new A();
     a=a.add0(ex);//等价于a.add0(ex),无需返回值,因为通过传递的对象地址(句柄),直接修改了ex中i的值
     a.add1(ex);//add0,add1都在其中的方法体中直接修改了ex.i的值,因此add0的返回值有点多余
     a.modify0(ex);//对ex所产生的影响同add1
     a.modify1(ex);//对ex没有产生任何影响(而且这就是等价于什么也没有做).

        这可能会让一部分人搞不清了。为什么呢?因为是对象地址的副本"值传递",在modify1中e=new Example();实际上e仅仅是保存ex对象地址的副本的一个句柄,当对e赋值时仅仅是对堆栈中e的赋值(对ex指针副本的变量e赋值),而并没有改变ex的句柄的指向,当方法调用完毕堆栈弹出,e就将要被垃圾回收,没有任何用处。当然你可以将它作为返回值,这就是另外一回事了。
   }
 }

        这里比较绕,如果你能明白这个原理,那么你就可以写出合理并且高效的程序,并且可以避免一些潜在的逻辑错误,如:对象在方法中被改动了,可能你还不知道!记住c++在这一点上和java有很大的不同,c++默认的是值传递,行参会按照位复制实参(如果用指针或者引用就和java很类似了),在方法中作为参数传递对象,java更象是c++中传递引用,当然还是有区别的,那就是c++中对象的引用不可再赋值为另一个对象,也就是说modify1中的再赋值对引用是不可以的。如果你对c++不了解,那么就当我什么也没有说,和c++的比较只是为了帮助更好的理解(针对熟悉c++而不熟悉java的人)。我本人也对c++了解甚少,平时主要工作侧重于java。因此如果哪位高人发现以上解释有什么错误请不吝赐教。

    引申到克隆技术java中的所有对象都是Object类的子类,Object类定义了protected clone()方法,它的作用和c++中按位复制是一样的,因此同样会带来如果对象中包含另一个对象(注意是对象不是基本数据类型,基本数据类型直接就会被复制)的指针(java中的句柄),clone并没有将被包含的对象clone,而是复制了被包含对象的句柄或者说指针。因此并不能认为复制出来的对象就可以随心所欲的修改,因为它和被clone的对象都包含同一个对象,因此可能会引起潜在的冲突问题。至于深度clone的方法很简单,就是在子类中覆盖父类Object类中clone方法,保证每一个被包含的对象都被按照位被clone。如果包含的数据全部是基本类型数据,那么就什么也不用做了。深度clone还有另一种方法就是利用Serializable,但是对象中被transient关键字修饰的变量是不会被序列化的. 因为clone用到的地方并不多,就不多说了。但是当你遇到的时候,一定要小心。

JS对象简单、深度克隆(赋值与引用的区别)

javascript的一切实例都是对象,只是对象之间稍有不同,分为原始类型和合成类型。原始类型对象指的是字符串(String)、数值(Number)、布尔值(Boolean),合成类型对象指的是数组(...
  • u013291076
  • u013291076
  • 2015年04月17日 11:29
  • 10970

java clone和深度复制和浅复制的个人总结

1.clone() 和“=”的比较 在基本类型变量里的赋值如: int  a = 1;int b = a;a与b有相同的值,改变a或b的值不影响彼此。 在java中,对象间的:java.util.Da...
  • sinat_33327772
  • sinat_33327772
  • 2015年12月12日 12:08
  • 1872

list.addAll()是浅拷贝,如何实现list的深拷贝

由于addAll()实现的是浅拷贝,即将 List A> copy=new ArrayList A>(); List A> src=new ArrayList A>(); src.a...
  • xjbclz
  • xjbclz
  • 2016年10月23日 14:47
  • 2409

java中传值及引伸深度克隆的思考

java中内存的分配方式有两种,一种是在堆中分配,一种是在堆栈中分配,所有new出来的对象都是在堆中分配的,函数中参数的传递是在栈中分配的。通常情况下堆的内存可以很大,比如32位操作系统中的虚拟内存都...
  • zhangweikai966
  • zhangweikai966
  • 2011年07月18日 17:07
  • 356

Java中的传值引用和传址引用

传值引用主要是针对基本数据类型而言。所谓传值引用,就是在进行变量的传递过程中,传递的是变量的实际的值,是一个新的拷贝,一个变量值不会影响另一个变量值得改变。       eg: public cl...
  • MYBOYER
  • MYBOYER
  • 2013年02月19日 16:20
  • 506

Java方法传值和传址分析

前段时间跟同学讨论了java中方法是传值还是传址的问题。 得出一个结论:java中一直是传值。 先看一段代码:public class Main { public static void ...
  • qq_29819411
  • qq_29819411
  • 2016年04月26日 17:10
  • 400

java学习02-传值调用和传址调用

传值调用 传值调用不会改变变量的值,在函数弹栈后,原变量的值不变。 class Demo {       public static void main(String[] args)  ...
  • Leeeoplod
  • Leeeoplod
  • 2017年07月01日 17:54
  • 136

Java中传值与传址、static、继承、重写、super

Ctrl + Alt +S:快速产生get与set方法、构造方法等。 栈:一般是方法调用时进入的区域 方法中永远使用与方法最近的参数。1. 传值与传址 指的是方法调用时的参数传递。 a....
  • u010296640
  • u010296640
  • 2016年10月13日 20:20
  • 141

lua深度克隆的实现(lua基本数据类型传的是值,表类型传的是引用)

local function main() require "src/boot/Bootstrap" BootStrap:boot( ) local value ...
  • themagickeyjianan
  • themagickeyjianan
  • 2016年05月06日 11:19
  • 1198

java深度克隆

  • 2012年05月29日 09:10
  • 686B
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:java中传值和传址及其引伸深度克隆的思考
举报原因:
原因补充:

(最多只允许输入30个字)