普通的对象赋值例如:对象A=对象B,是指用不同的句柄指向同一个内存空间。
我们有时候需要进行深层复制,即指向不同的内存空间,可以实现接口:Cloneable
例一:
import java.util.*;
class MyObject implements Cloneable {
int i; 354
MyObject(int ii) { i = ii; }
public Object clone() {
Object o = null;
try {
o = super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("MyObject can't clone");
}
return o;
}
public String toString() {
return Integer.toString(i);
}
}
public class LocalCopy {
static MyObject g(MyObject v) {
v.i++;
return v;
}
static MyObject f(MyObject v) {
v = (MyObject)v.clone();
v.i++;
return v;
}
public static void main(String[] args) {
MyObject a = new MyObject(11);
MyObject b = g(a);
if(a == b)
System.out.println("a == b");
else
System.out.println("a != b");
System.out.println("a = " + a);
System.out.println("b = " + b);
MyObject c = new MyObject(47);
MyObject d = f(c);
if(c == d)
System.out.println("c == d");
else
System.out.println("c != d");
System.out.println("c = " + c);
System.out.println("d = " + d);
}
}
输出:
a == b
a = 12
b = 12
c != d
c = 47
d = 48
上述程序很简单,对象内只有普通数据成员。
例二:
public class Snake extends Object implements Cloneable {
private Snake next;
private char c;
Snake(int i, char x) {
c = x;
if(--i > 0)
next = new Snake(i, (char)(x + 1));
}
void increment() {
c++;
if(next != null)
next.increment();
}
public String toString() {
String s = ":" + c;
if(next != null)
s += next.toString();
return s;
}
public Snake clone() {
Snake o = null;
try {
o = (Snake)super.clone();
} catch (CloneNotSupportedException e) {}
return o;
}
public static void main(String[] args) {
Snake s = new Snake(5, 'a');
System.out.println("s = " + s);
Snake s2 = s.clone();
System.out.println("s2 = " + s2);
s.increment();
System.out.println(
"after s.increment, s2 = " + s2);
}
}
一条Snake(蛇)由数段构成,每一段的类型都是Snake。所以,这是一个一段段链接起来的列表。所有段都是以循环方式创建的,每做好一段,都会使第一个构建器参数的值递减,直至最终为零。而为给每段赋予一个独一无二的标记,第二个参数(一个Char)的值在每次循环构建器调用时都会递增。 increment()方法的作用是循环递增每个标记,使我们能看到发生的变化;而 toString 则循环打印出每个标记。输出如下:
s = :a:b:c:d:e
s2 = :a:b:c:d:e
after s.increment, s2 = :a:c:d:e:f
这意味着只有第一段才是由Object.clone()复制的,所以此时进行的是一种“浅层复制”。若希望复制整条蛇——即进行“深层复制”——必须在被覆盖的clone()里采取附加的操作。
通常可在从一个能克隆的类里调用 super.clone(),以确保所有基础类行动(包括Object.clone())能够进行。随着是为对象内每个句柄都明确调用一个 clone();否则那些句柄会别名变成原始对象的句柄。构建器的调用也大致相同——首先构造基础类,然后是下一个衍生的构建器……以此类推,直到位于最深层的衍生构建器。区别在于 clone()并不是个构建器,所以没有办法实现自动克隆。为了克隆,必须由自己明确进行。
所以并不是深层复制:可以修改代码如下:
package com.myweb.Controller.test;
public class Snake extends Object implements Cloneable {
private Snake next;
private char c;
Snake(int i, char x) {
c = x;
if(--i > 0)
next = new Snake(i, (char)(x + 1));
}
void increment() {
c++;
if(next != null)
next.increment();
}
public String toString() {
String s = ":" + c;
if(next != null)
s += next.toString();
return s;
}
public Snake clone() {
Snake o = null;
try {
o = (Snake)super.clone();
if(o.next!=null){
o.next=o.next.clone();
}
} catch (CloneNotSupportedException e) {}
return o;
}
public static void main(String[] args) {
Snake s = new Snake(5, 'a');
System.out.println("s = " + s);
Snake s2 = s.clone();
System.out.println("s2 = " + s2);
s.increment();
System.out.println(
"after s.increment, s2 = " + s2);
}
}
这边是深层复制了,注意克隆方法的内部实现有所不同。
最后再介绍一下关于vector的复制,其实差不多:
class Int2 implements Cloneable {
private int i;
public Int2(int ii) { i = ii; }
public void increment() { i++; }
public String toString() {
return Integer.toString(i);
}
public Object clone() {
Object o = null;
try {
o = super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("Int2 can't clone");
}
return o;
}
}
class Int3 extends Int2 {
private int j;
public Int3(int i) { super(i);j=i; }
public void increment(){
j++;
}
public String toString(){
return Integer.toString(j);
}
}
public class AddingClone {
public static void main(String[] args) {
Int2 x = new Int2(10);
Int2 x2 = (Int2)x.clone();
x2.increment();
System.out.println(
"x = " + x + ", x2 = " + x2);
Int3 x3 = new Int3(7);
Int3 x4 = (Int3)x3.clone();
System.out.println(x3);
x3.increment();
System.out.println(x3);
System.out.println(x4);
Vector v = new Vector();
for(int i = 0; i < 10; i++ )
v.addElement(new Int2(i));
System.out.println("v: " + v);
Vector v2 = (Vector)v.clone();
for(int i = 0; i < v.size(); i++)
v2.setElementAt(
((Int2)v2.elementAt(i)).clone(), i);
for(Enumeration e = v.elements(); e.hasMoreElements(); )
((Int2)e.nextElement()).increment();
System.out.println("v: " + v);
System.out.println("v2: " + v2);
}
}
必须对对象内的对象进行一一克隆,有点递归的意思,才是深层复制。从上述代码还可以看出, Int3继承了Int2,Int2实现了public clone,Int3不用重复实现,输出如下:
x = 10, x2 = 11
7
8
7
v: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
v: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
v2: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
可能有错误,欢迎指出。顺便继续看看序列化实现深层复制。
相关知识
【JAVA】Java基础—面向对象编程:类与对象
JAVA编程艺术
Java对象生命周期管理:从创建到垃圾回收的完整解析
javascript dom 编程艺术pdf javascript dom编程艺术pdf下载网盘
JavaScript DOM 编程艺术 (第二版)学习之1
使用java编写一只玫瑰花
借花献佛!朋友干了5年整的Java面试官,给我分享了一份面试官最爱问的Java面试题
Java通用型支付+电商平台双系统实战
菜根花小宝贝
Java 并没有死!那么,Java这朵“永生花”是怎么永葆青春的呢?
网址: java编程思想阅读笔记之对象克隆 https://m.huajiangbk.com/newsview652475.html
上一篇: 本周绘本推荐——《花婆婆》,清华 |
下一篇: NOIP2017初赛阅读程序写结 |