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

大家都知道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用到的地方并不多,就不多说了。但是当你遇到的时候,一定要小心。

http://www.51cto.com/specbook/449/2869.htm

java中传值及引伸深度克隆的思考(说白了Java只能传递对象指针)的更多相关文章

  1. Java中传值与传引用

    不管Java参数类型是什么,一律传递参数的副本. <Thinking In Java>:“When you're passing primitives into a method,you ...

  2. Java的赋值、浅克隆和深度克隆的区别

    赋值 直接  = ,克隆 clone 假如说你想复制一个简单变量.很简单: int a= 5; int b= a; b = 6; 这样 a == 5, b == 6 不仅仅是int类型,其它七种原始数 ...

  3. java中传值方式的个人理解

    前言 这几天在整理java基础知识方面的内容,对于值传递还不是特别理解,于是查阅了一些资料和网上相关博客,自己进行了归纳总结,最后将其整理成了一篇博客. 值传递 值传递是指在调用函数时将实际参数复制一 ...

  4. 原型模式 —— Java的赋值、浅克隆和深度克隆的区别

    赋值 直接  = ,克隆 clone 假如说你想复制一个简单变量.很简单: int a= 5; int b= a; b = 6; 这样 a == 5, b == 6 不仅仅是int类型,其它七种原始数 ...

  5. Java中调用c/c++语言出现Exception in thread "main" java.lang.UnsatisfiedLinkError: Test.testPrint(Ljava/lang/String;)V...错误

    错误: Exception in thread "main" java.lang.UnsatisfiedLinkError: Test.testPrint(Ljava/lang/S ...

  6. java中的URLEncoder.encode对应JS中用decodeURIComponent,js和java编码,解码

    用get请求传中文,经常搞到乱码,这几天搞搞这个东西,总结一下,以方便以后处理这类的问题. Java代码中的URLEncoder.encode方法和JS的encodeURIComponent功能差不多 ...

  7. Java 中的 3 个双引号是什么语法?Java 15 刷新你的认知!

    Java 中的 3 个双引号 """ 是什么语法? 这是 Java 15 新出的,刷新你的认知! 一.前言 在 Java 15 的推出的时候,Text Blocks 正式 ...

  8. 关于 Java 中 finally 语句块的深度辨析

    应该有很多人对java的finally 理解的不是很透彻吧,这里有篇文章讲的非常深入.猛击下面链接.面试时经常问到这问题 http://www.ibm.com/developerworks/cn/ja ...

  9. Java中如果把构造方法也私有化,如何创建对象?Java的单例设计模式——饿汉式和懒汉式区别

    Java的单例模式——饿汉式 package com.swift; //Java单例设计模式——恶汉式 public class SinglePerson { private String name= ...

随机推荐

  1. PC游戏编程(入门篇)(前言写的很不错)

    PC游戏编程(入门篇) 第一章 基石 1. 1 BOSS登场--GAF简介 第二章 2D图形程式初体验 2.l 饮水思源--第一个"游戏"程式 2.2 知其所以然一一2D图形学基础 ...

  2. DLL与EXE之间的通讯调用 以及 回调函数的线程执行空间

    dll 与 exe 之间的通讯方式有很多种, 本文采用回调函数的方法实现, 本文也将研究多线程,多模块的情况下,回调函数所在的线程, 啥也不说了,先附上代码: 下面的是dll模块的的, dll的工程文 ...

  3. JavaEE Tutorials (8) - Java持久化API介绍

    8.1实体96 8.1.1实体类的需求97 8.1.2实体类中的持久化字段和属性97 8.1.3实体的主键101 8.1.4实体关系中的多重性103 8.1.5实体关系中的方向103 8.1.6实体中 ...

  4. 宣布发布 Windows Azure ExpressRoute,宣告与 Level 3 建立全新的合作伙伴关系并推出关于其他 Azure 服务令人振奋的更新

     在我们与世界各地的客户和合作伙伴交谈时,总会听到他们说,希望找到一个提供商帮助他们最大限度地发挥内部部署投资的作用并且能够利用云的灵活性.这是我们构建混合云策略和云操作系统愿景的基本原则.本着我 ...

  5. POJ-1010 Stamps

    [题目描述] 题目大意是:邮票发行商会发行不同面值.不同种类的邮票给集邮爱好者,集邮爱好者有总目标面额,通过不同的邮票组合(总数在4张以内)达到该面值,卖给集邮爱好者.另外,发行商发行的邮票面值最多2 ...

  6. [编程语言][java][java se]java泛型中? T K V E含义(学习)

    ? 表示不确定的java类型,类型是未知的. T  表示java类型. K V 分别代表java键值中的Key Value. E 代表Element,特性是枚举. 1.意思     jdk中的K,V, ...

  7. 启动及重新启动nginx,重启nginx后丢失nginx.pid问题解决

    停止操作 停止操作是通过向nginx进程发送信号(什么是信号请参阅linux文 章)来进行的 步骤1:查询nginx主进程号 ps -ef | grep nginx 在进程列表里 面找master进程 ...

  8. hdu1867之KMP

    A + B for you again Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Othe ...

  9. 用Aspose.Cells控件读取Excel

    Aspose是一个很强大的控件,可以用来操作word,excel,ppt等文件,用这个控件来导入.导出数据非常方便.其中Aspose.Cells就是用来操作Excel的,功能有很多.我所用的是最基本的 ...

  10. ansible笔记

    ansible 资料 ansible 配置 ansible inventory配置文件 ansible模块 http://www.cnblogs.com/iois/p/6216936.html ans ...