- 浏览: 136683 次
- 性别:
- 来自: 上海
-
文章分类
最新评论
-
qq466862016:
不错的文章
JDK动态代理与CGLIB代理的对比 -
jinxiongyi:
你好,jpedal pdf转换图片的 画质,怎么提高。。我转 ...
介绍几款PDF转图片的开源工具 -
qqdwll:
转图片消耗的内存还是不小。 有时间得找找有没有更好的办法, 把 ...
介绍几款PDF转图片的开源工具 -
xiaoyao3857:
Thanks for your work!It's help ...
Keeping Eclipse running clean (转载) -
iceside:
图片讲解非常详细,说清了引用复制是怎么回事
Java 值传递的终极解释
1. 一个类需要实现clone. 一个最佳实践是它需要实现 Cloneable 接口并且提供一个 public clone 方法。
Object 对象的clone 方法是protected。 不重写这个方法, 我们不能够调用一个对象的clone 方法, 除非利用反射。
2. 如果给一个 nonfinal 类重写clone方法。应该通过调用 super.clone获得对象。
因为有个约束, x.clone().getClass() 应该和x.getClass()一致。 所以, 也不要通过构造器去实现clone.
3。当调用clone方法后, 这个类定义的所有属性都回被copy, 并且clone后所有属性的值都会跟那个源对象一样。 如果这个类的所有属性是基本类型或者引用的是不可改变的类型(final) , 就像 String, 那么, 这个clone后获得的对象, 就是我们确切需要的对象。它不需要进一步做任何操作 (当然, 如果某些值是要做唯一性处理的。 也要改)。
但是, 当有fields引用可变的对象时候, 简单的使用上面的实现, 会发生错误。 例如
如果需要把上面的这个类可以cloneable, 而clone方法, 仅仅返回super.clone(). 对于field size会有正确的值, 但field elements 会引用同一个对象。 改变原对象或克隆对象时候, 都会同时影响两个对象。 这时候, 参考下面的原则。
4. clone 方法是作为另外一个构造方法, 你必须保证克隆对象不会影响到源对象。 这时需要对clone方法做一些改变。即我们需要做深度克隆。 例如:
这样的话, 其实要每一层, 每个可变对象都实现良好的克隆。 但是
如果一个类的有些field 是 final的话, 会有些麻烦。 因为克隆中, 试图给一个final field赋值是不可能的(java 基本特性)。 这时候, 可能考虑两种做法。 一是如果可变对象可以在克隆对象和源对象之间安全共享的话, 就不需要做深度克隆 (这里的final, 可变对象不会给大家造成混淆吧?)。另外一种做法是把final 去掉。
从JDK中取个deeply copy 的例子:
HashMap 实现的事shallow copy, HashTable实现的话,某些是deep copy. 例如里面的一个链表实现。
这里简单的介绍下HashTable的实现。
HashTable 里有个Entry 对象数组 Entry[] buckets。
根据上面的分析,我们知道要实现深度克隆, 需要递归调用buckets.clone方法。 如果buckets 数组里的对象仍然还是可变对象 (Entry), 那么还要递归调用Entry.clone方法。 直到基本类型或final类型。 参考下面的源代码片断:
其实, 上面的Entry 的copy方法在链表elements比较大时候, 会有点问题。 因为每个element都会copy一个链表出来。 这回可能发生stack overflow. 用下面的方法, 可以防止这种情况发生。
5. Clone 方法, 不要调用非final 方法。 如果调用的方法被override. 子类调用clone时, 子类的值可能被改变。 所有一般调用final或者private方法。
Object 对象的clone 方法是protected。 不重写这个方法, 我们不能够调用一个对象的clone 方法, 除非利用反射。
2. 如果给一个 nonfinal 类重写clone方法。应该通过调用 super.clone获得对象。
因为有个约束, x.clone().getClass() 应该和x.getClass()一致。 所以, 也不要通过构造器去实现clone.
3。当调用clone方法后, 这个类定义的所有属性都回被copy, 并且clone后所有属性的值都会跟那个源对象一样。 如果这个类的所有属性是基本类型或者引用的是不可改变的类型(final) , 就像 String, 那么, 这个clone后获得的对象, 就是我们确切需要的对象。它不需要进一步做任何操作 (当然, 如果某些值是要做唯一性处理的。 也要改)。
package util; public final class PhoneNumber { private final short areaCode; private final short exchange; private final short extension; public PhoneNumber(int areaCode, int exchange, int extension) { rangeCheck(areaCode, 999, "area code"); rangeCheck(exchange, 999, "exchange"); rangeCheck(extension, 9999, "extension"); this.areaCode = (short) areaCode; this.exchange = (short) exchange; this.extension = (short) extension; } private static void rangeCheck(int arg, int max, String name) { if (arg < 0 || arg > max) throw new IllegalArgumentException(name + ": " + arg); } public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof PhoneNumber)) return false; PhoneNumber pn = (PhoneNumber) o; return pn.extension == extension && pn.exchange == exchange && pn.areaCode == areaCode; } [b]public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new Error("Assertion failure"); // Can't happen } }[/b] }
但是, 当有fields引用可变的对象时候, 简单的使用上面的实现, 会发生错误。 例如
public class Stack { private Object[] elements; private int size = 0; public Stack(int initialCapacity) { this.elements = new Object[initialCapacity]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } // Ensure space for at least one more element. private void ensureCapacity() { if (elements.length == size) { Object oldElements[] = elements; elements = new Object[2 * elements.length + 1]; System.arraycopy(oldElements, 0, elements, 0, size); } } }
如果需要把上面的这个类可以cloneable, 而clone方法, 仅仅返回super.clone(). 对于field size会有正确的值, 但field elements 会引用同一个对象。 改变原对象或克隆对象时候, 都会同时影响两个对象。 这时候, 参考下面的原则。
4. clone 方法是作为另外一个构造方法, 你必须保证克隆对象不会影响到源对象。 这时需要对clone方法做一些改变。即我们需要做深度克隆。 例如:
public Object clone() throws CloneNotSupportedException { Stack result = (Stack) super.clone(); result.elements = (Object[]) elements.clone(); return result; }
这样的话, 其实要每一层, 每个可变对象都实现良好的克隆。 但是
如果一个类的有些field 是 final的话, 会有些麻烦。 因为克隆中, 试图给一个final field赋值是不可能的(java 基本特性)。 这时候, 可能考虑两种做法。 一是如果可变对象可以在克隆对象和源对象之间安全共享的话, 就不需要做深度克隆 (这里的final, 可变对象不会给大家造成混淆吧?)。另外一种做法是把final 去掉。
从JDK中取个deeply copy 的例子:
HashMap 实现的事shallow copy, HashTable实现的话,某些是deep copy. 例如里面的一个链表实现。
这里简单的介绍下HashTable的实现。
HashTable 里有个Entry 对象数组 Entry[] buckets。
根据上面的分析,我们知道要实现深度克隆, 需要递归调用buckets.clone方法。 如果buckets 数组里的对象仍然还是可变对象 (Entry), 那么还要递归调用Entry.clone方法。 直到基本类型或final类型。 参考下面的源代码片断:
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable { /** * The hash table data. */ private transient Entry[] table; /** * The total number of entries in the hash table. */ private transient int count; /** * The table is rehashed when its size exceeds this threshold. (The * value of this field is (int)(capacity * loadFactor).) * * @serial */ private int threshold; /** * The load factor for the hashtable. * * @serial */ private float loadFactor; ...... // Entry 其实是一个链表的简单实现 /** * Hashtable collision list. */ private static class Entry<K,V> implements Map.Entry<K,V> { int hash; K key; V value; Entry<K,V> next; protected Entry(int hash, K key, V value, Entry<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } protected Object clone() { return new Entry<K,V>(hash, key, value, (next==null ? null : (Entry<K,V>) next.clone())); } ..... /** * Creates a shallow copy of this hashtable. All the structure of the * hashtable itself is copied, but the keys and values are not cloned. * This is a relatively expensive operation. * * @return a clone of the hashtable */ public synchronized Object clone() { try { Hashtable<K,V> t = (Hashtable<K,V>) super.clone(); t.table = new Entry[table.length]; for (int i = table.length ; i-- > 0 ; ) { t.table[i] = (table[i] != null) ? (Entry<K,V>) table[i].clone() : null; } t.keySet = null; t.entrySet = null; t.values = null; t.modCount = 0; return t; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(); } }
其实, 上面的Entry 的copy方法在链表elements比较大时候, 会有点问题。 因为每个element都会copy一个链表出来。 这回可能发生stack overflow. 用下面的方法, 可以防止这种情况发生。
// Iteratively copy the linked list headed by this Entry Entry deepCopy() { Entry result = new Entry(key, value, next); for (Entry p = result; p.next != null; p = p.next) p.next = new Entry(p.next.key, p.next.value, p.next.next); return result; }
5. Clone 方法, 不要调用非final 方法。 如果调用的方法被override. 子类调用clone时, 子类的值可能被改变。 所有一般调用final或者private方法。
发表评论
-
介绍几款PDF转图片的开源工具
2011-09-09 00:40 4607最近项目中有个需求需要把PDF转成一张图。经过调查,有 ... -
jadclipse(反编译Eclipse插件)
2011-07-19 19:13 1665Jad Java decompiler plugin for ... -
Java开发时候的内存溢出
2011-07-13 17:33 1200这里以tomcat环境为例, ... -
class loader
2011-07-08 17:23 0Because Class.getResource() eve ... -
Jakarta-Common-BeanUtils使用笔记
2011-07-06 16:55 1403原文转发http://blog.csdn.net/fa ... -
基于MVC模式Struts框架研究
2011-04-13 20:02 1359不做web开发多年了, 可偶尔去面试的时候, 还是 ... -
Java反射与动态代理
2011-04-13 15:08 1021这篇文章是 成富 先生在InfoQ上Java 深度历险系列的一 ... -
Java枚举类型
2011-04-04 19:50 796Tiger中的一个重要新特性是枚举构造,它是一种新的Java枚 ... -
Java 值传递的终极解释
2011-03-21 22:49 1984对于Java的值传递, 你真的了解么? Ja ... -
六种异常处理的陋习
2011-03-20 03:21 839你觉得自己是一个Java专 ... -
数组初始化
2011-03-20 02:40 897数组初始化,你觉得简单吗? a.如果你觉得简单,那请看下面的 ... -
Java 实现 hashCode 方法
2011-03-11 17:07 1219原文 http://www.javapractices.com ... -
Java 中 immutable class 以及怎样实现immutable 类
2011-03-11 16:47 1374原文 http://www.javapractices.com ... -
Java 内部类介绍
2011-02-16 17:14 1012转载: http://zhidao.baidu.com/que ... -
java 通过流, nio 移动文件或者文件夹
2011-01-04 17:54 1896我们用例子说明java怎样通过不同的方式移动文件或文件夹。 ... -
转 深入探讨SOAP、RPC和RMI
2010-12-17 00:34 1083这篇文章是从网上转下来的。 原文应该是写于2001年。 10 ... -
java 6 中的性能优化
2010-12-07 15:30 1451文章转载自: http://www ... -
创建强健,稳定的 JMS 系统
2010-12-07 15:21 991The most reliable way to produc ... -
Java Modifier Summary
2010-11-12 15:10 899<tbody> <tr> ... -
[ZT] 怎样停止一个线程或者任务
2010-11-12 15:08 1013source file http://forward.com. ...
相关推荐
Git 是一个强大的开源分布式版本控制系统,由 Linux 内核开发者 Linus Torvalds 创建,主要用于管理软件开发过程中的源代码版本。Git 的设计目标是速度、数据完整性以及支持分布式非线性工作流程。由于其分布式特性...
Java设计模式是软件开发中一种被广泛采用的方式,它是一组被反复使用、多数人知晓、分类编目、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 根据提供的文件内容,...
在学习Java的过程中,掌握基础知识是非常重要的。以下是一些关键知识点的详细解释: 1. **消息对话框和输入**:Java提供了`java.awt.JOptionPane`类用于创建消息对话框,展示文本信息或获取用户输入。例如,`...
Java编程老鸟潜心写作,奉献高效率的Java学习心得 完全站在没有编程经验读者的角度,手把手教会读者学习Java 配16小时多媒体教学视频,高效、直观 一一击破Java入门可能会遇到的难点和疑惑 抽丝剥茧,层层推进,让...
2. Java的学习 3. 目标 4. 联机文档 5. 章节 6. 练习 7. 多媒体CD-ROM 8. 源代码 9. 编码样式 10. Java版本 11. 课程和培训 12. 错误 13. 封面设计 14. 致谢 第1章 对象入门 1.1 抽象的进步 1.2 对象的接口 1.3 ...
Java编程老鸟潜心写作,奉献高效率的Java学习心得 完全站在没有编程经验读者的角度,手把手教会读者学习Java 配16小时多媒体教学视频,高效、直观 一一击破Java入门可能会遇到的难点和疑惑 抽丝剥茧,层层推进,让...
2. Java的学习 3. 目标 4. 联机文档 5. 章节 6. 练习 7. 多媒体 8. 源代码 9. 编码样式 10. Java版本 11. 课程和培训 12. 错误 13. 封面设计 14. 致谢 第1章 对象入门 1.1 抽象的进步 1.2 对象的接口 1.3 实现方案的...
Java作为一门广泛使用的编程语言,其面试中涉及的知识点涵盖了语法、数据结构、并发、内存管理等多个方面。...通过全面学习和深入理解这些知识点,将有助于在面试中脱颖而出,成功斩获理想的职位。
根据给定的文件信息,我们可以总结出以下关于Java学习的心得和重要知识点: ### Java学习心得及关键概念 #### 1. 对象与类的关系 Java是一种面向对象的语言,对象是类的一个实例,而类则是对象的模板。通过定义类...
Java 程序设计历年真题知识点总结 Java 语言程序设计历年真题是一份非常重要的考试资源,对于 Java 程序设计的学习和考试都具有非常重要的参考价值。下面是对该资源的知识点总结: 一、Java 基础知识 * Java 版本...
【Java面试题总结文档】 Java 是一种广泛使用的面向对象的编程语言,其面试题涵盖了从基础知识到高级概念的方方面面。以下是一些Java面试中的常见知识点: 1. **面向对象的特征**:Java作为面向对象的语言,其核心...
在Java中,克隆功能主要通过实现`Cloneable`接口并重写`Object`类中的`clone()`方法来实现。 1. **实现Cloneable接口**:这是一个标记接口,用于指示该类可以被克隆。 2. **重写clone()方法**:这是一个受保护的...
2. Java的学习 3. 目标 4. 联机文档 5. 章节 6. 练习 7. 多媒体CD-ROM 8. 源代码 9. 编码样式 10. Java版本 11. 课程和培训 12. 错误 13. 封面设计 14. 致谢 第1章 对象入门 1.1 抽象的进步 1.2 对象的接口 1.3 ...
在Java毕业设计周记中,我们通常会遇到一系列与Java编程和软件开发实践相关的知识点。以下是对这些主题的详细说明: 1. **Java基础**:Java是一种广泛使用的面向对象编程语言,具有平台无关性、安全性高和性能优异...
2. Java的学习 3. 目标 4. 联机文档 5. 章节 6. 练习 7. 多媒体CD-ROM 8. 源代码 9. 编码样式 10. Java版本 11. 课程和培训 12. 错误 13. 封面设计 14. 致谢 第1章 对象入门 1.1 抽象的进步 1.2 对象的接口 1.3 ...
"Java 基础知识点" 本文将对 Java 的基础知识点进行总结和解释,涵盖了Java 的基本数据类型、String 相关知识点、Object 方法、基本...这些知识点是 Java 编程的基础,理解这些知识点对于学习 Java 是非常重要的。
开发环境 开发工具:IDEA 2019.3.1 JDK版本:JDK 1.8 Maven版本:3.6.3 插件环境 杰森 招摇2 运行方式 提示:如果是fork的朋友,同步代码...Java设计模式学习与总结 LetCode刷题汇总 交流 如果大家有兴趣,欢迎大家一
本教程将深入探讨Java中的数组、方法以及面向对象编程的基础概念,为初学者提供一个全面的学习指南。 一、数组 数组在Java中是存储同一类型数据的集合,可以看作是一个可变大小的容器。数组的定义和初始化通常包含...
在Java编程语言的基础学习中,我们常常会遇到各种概念和难点。以下是对这些知识点的详细说明,涵盖了CORE JAVA部分的一些核心内容。 1. **静态语句块与非静态语句块、构造执行顺序** 静态语句块在类加载时执行,只...