1. 程式人生 > >java之clone方法的使用

java之clone方法的使用

首先看一下jdk中對clone方法的解釋:



大概意思是說:返回一個要克隆物件的副本,克隆的型別依賴被克隆物件,換句話說:克隆後的物件型別與被克隆物件的型別相同。

一、簡單用法

只需要在需要clone的物件上實現(implements)Cloneable介面,然後再在類中加上clone方法,在方法中只需要呼叫super.clone(),根據自己的需要實現即可。

public class Student implements Cloneable {
	private String name;
	private int age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	@Override
	protected Student clone() throws CloneNotSupportedException {
		return (Student)super.clone();
	}
	
	public static void main(String[] args) {
		Student stu = new Student();
		stu.setAge(1);
		stu.setName("aa");
		System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName());
		try {
			Student sC = stu.clone();
			System.out.println(sC + " sC.age: " + sC.getAge() + " sC.name: " + sC.getName());
			sC.setAge(12);
			sC.setName("bb");
			System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName());
			System.out.println(sC + " sC.age: " + sC.getAge() + " sC.name: " + sC.getName());
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

輸出結果:

[email protected] age: 1 name: aa
[email protected] sC.age: 1 sC.name: aa
[email protected] age: 1 name: aa
[email protected] sC.age: 12 sC.name: bb

分析結果:1、根據輸出結果中前邊的類名,可以得出被克隆物件的與原來的物件是同一種類型。2、根據記憶體地址(hashcode)知道,被克隆物件的與原來的物件是存在於記憶體中的不同的兩個物件。所以後邊有一個賦值,對原來物件沒有任何影響。

二、“影子”克隆與深度克隆

首先看一個例子:

class Bag{//學生的書包
	private int width;
	private String logo;
	public int getWidth() {
		return width;
	}
	public void setWidth(int width) {
		this.width = width;
	}
	public String getLogo() {
		return logo;
	}
	public void setLogo(String logo) {
		this.logo = logo;
	}
}

public class Student2 implements Cloneable {
	private String name;
	private int age;
	private Bag bag;
	
	public Bag getBag() {
		return bag;
	}
	public void setBag(Bag bag) {
		this.bag = bag;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	@Override
	protected Student2 clone() throws CloneNotSupportedException {
		return (Student2)super.clone();
	}
	
	public static void main(String[] args) {
		Student2 stu = new Student2();
		stu.setAge(1);
		stu.setName("aa");
		Bag b = new Bag();
		b.setWidth(10);
		b.setLogo("Nike");
		stu.setBag(b);
		printStudent(stu);
		try {
			Student2 sC = stu.clone();
			printStudent(sC);
			sC.setAge(12);
			sC.setName("bb");
			sC.getBag().setWidth(100);//改變書包的屬性
			sC.getBag().setLogo("JNike");
			printStudent(stu);
			printStudent(sC);
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 輸出
	 * @param stu
	 */
	private static void printStudent(Student2 stu) {
		System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName() + 
				" bag: " + stu.getBag() + "(" + stu.getBag().getLogo() + " width: " + 
				stu.getBag().getWidth() + ")");
	}
}
輸出結果:

[email protected] age: 1 name: aa bag: [email protected](Nike width: 10)
[email protected] age: 1 name: aa bag: [email protected](Nike width: 10)
[email protected] age: 1 name: aa bag: [email protected](JNike width: 100)
[email protected] age: 12 name: bb bag: [email protected](JNike width: 100)

分析:發現是不是跟預期的不太一樣,通過第二個同學改變書包,但是第一個同學的書包也被改變了。並且通過記憶體地址可知,他們是同一物件(書包)。原因:呼叫Object類中clone()方法產生的效果是:先在記憶體中開闢一塊和原始物件一樣的空間,然後原樣拷貝原始物件中的內 容。對基本資料型別,這樣的操作是沒有問題的,但對非基本型別變數,我們知道它們儲存的僅僅是物件的引用,這也導致clone後的非基本型別變數和原始對 象中相應的變數指向的是同一個物件。 這就是所謂的“影子”克隆。

解決方案:深度克隆,既是對裡邊的引用也要克隆。以下是實現:

class Bag implements Cloneable{//學生的書包
	private int width;//寬
	private String logo;//品牌
	public int getWidth() {
		return width;
	}
	public void setWidth(int width) {
		this.width = width;
	}
	public String getLogo() {
		return logo;
	}
	public void setLogo(String logo) {
		this.logo = logo;
	}
	
	@Override
	protected Bag clone() throws CloneNotSupportedException {
		return (Bag)super.clone();
	}
}

public class Student3 implements Cloneable {
	private String name;
	private int age;
	private Bag bag;
	
	public Bag getBag() {
		return bag;
	}
	public void setBag(Bag bag) {
		this.bag = bag;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	@Override
	protected Student3 clone() throws CloneNotSupportedException {
		Student3 stu = (Student3)super.clone();
		stu.bag = bag.clone();
		return stu;
	}
	
	public static void main(String[] args) {
		Student3 stu = new Student3();
		stu.setAge(1);
		stu.setName("aa");
		Bag b = new Bag();
		b.setWidth(10);
		b.setLogo("Nike");
		stu.setBag(b);
		printStudent(stu);
		try {
			Student3 sC = stu.clone();
			printStudent(sC);
			sC.setAge(12);
			sC.setName("bb");
			sC.getBag().setWidth(100);//改變書包的屬性
			sC.getBag().setLogo("JNike");
			printStudent(stu);
			printStudent(sC);
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 輸出
	 * @param stu
	 */
	private static void printStudent(Student3 stu) {
		System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName() + 
				" bag: " + stu.getBag() + "(" + stu.getBag().getLogo() + " width: " + 
				stu.getBag().getWidth() + ")");
	}
}

輸出:

[email protected] age: 1 name: aa bag: [email protected](Nike width: 10)
[email protected] age: 1 name: aa bag: [email protected](Nike width: 10)
[email protected] age: 1 name: aa bag: [email protected](Nike width: 10)
[email protected] age: 12 name: bb bag: [email protected](JNike width: 100)

與我們期望的相同了。

三、是不是萬事大吉了?

注意:要知道不是所有的類都能實現深度clone的。例如,StringBuffer,看一下 JDK API中關於StringBuffer的說明,StringBuffer沒有過載clone()方法,更為嚴重的是StringBuffer還是一個 final類,這也是說我們也不能用繼承的辦法間接實現StringBuffer的clone。如果一個類中包含有StringBuffer型別物件或和 StringBuffer相似類的物件,我們有兩種選擇:要麼只能實現影子clone,要麼自己重新生成物件: new StringBuffer(oldValue.toString()); 進行賦值。

你是不是想問上邊例子中的String呢,難道實現了clone?(查詢String.java原始碼,發現並沒有)通過以下的例子為你解答疑惑:

class StrCloneDemo implements Cloneable {
	public String str;
	public StringBuffer strBuff;

	public Object clone() throws CloneNotSupportedException {
		return (StrCloneDemo) super.clone();
	}
}

public class StrCloneDemoTest {
	public static void main(String[] a) {
		StrCloneDemo scd1 = new StrCloneDemo();
		scd1.str = new String("abcdefghijk");
		scd1.strBuff = new StringBuffer("rstuvwxyz");
		System.out.println("before clone,scd1.str = " + scd1.str);
		System.out.println("before clone,scd1.strBuff = " + scd1.strBuff);

		StrCloneDemo scd2 = null;
		try {
			scd2 = (StrCloneDemo) scd1.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		scd2.str = scd2.str.substring(0, 3);
		scd2.strBuff = scd2.strBuff.append("RSTUVWXYZ");
		
		System.out.println("******************************************");
		System.out.println("after clone,scd1.str = " + scd1.str);
		System.out.println("after clone,scd1.strBuff = " + scd1.strBuff);
		
		System.out.println("******************************************");
		System.out.println("after clone,scd2.str = " + scd2.str);
		System.out.println("after clone,scd2.strBuff = " + scd2.strBuff);
	}
}
輸出:

before clone,scd1.str = abcdefghijk
before clone,scd1.strBuff = rstuvwxyz
******************************************
after clone,scd1.str = abcdefghijk
after clone,scd1.strBuff = rstuvwxyzRSTUVWXYZ
******************************************
after clone,scd2.str = abc
after clone,scd2.strBuff = rstuvwxyzRSTUVWXYZ

分析:String型別的變數好象已經實現了深度clone,因為對scd2.str的改動並沒有影響到scd1.str!實質上,在clone的時候scd1.str與scd2.str仍然是引用,而且都指向了同一個 String物件。但在執行c2.str = c2.str.substring(0,5)的時候,生成了一個新的String型別,然後又賦回給scd2.str。這是因為String被 Sun公司的工程師寫成了一個不可更改的類(immutable class),在所有String類中的函式都不能更改自身的值。類似的,String類中的其它方法也是如此,都是生成一個新的物件返回。當然StringBuffer還是原來的物件。




相關推薦

javaclone方法的使用

首先看一下jdk中對clone方法的解釋: 大概意思是說:返回一個要克隆物件的副本,克隆的型別依賴被克隆物件,換句話說:克隆後的物件型別與被克隆物件的型別相同。 一、簡單用法 只需要在需要clone的物件上實現(implements)Cloneable介面,然後再在類中加

Java Object物件clone方法

原文:http://blog.csdn.net/bigconvience/article/details/25025561 克隆的目的:快速建立一個已有物件的副本。 克隆的步驟: 建立一個物件將原有物件的資料匯入到新建立的資料中 1. Object的clone()原始

JAVA反射方法的應用】

JAVA之反射的應用 import java.util.HashMap; import java.util.Map; public class Child extends Parent { public int add(int a,int b){ return a+b; }

Javaclone()方法的使用

Java程式設計思想 物件克隆是指建立已有物件的一個拷貝,如果想要修改一個物件,但同時不想改變呼叫者的物件,那麼克隆會是很好的解決方式。 在Java中,實現物件的克隆只需要覆蓋Object提供的clone()方法,並將方法訪問級別改為public,同時要注意物件所屬類必須實

Java靜態方法中的內部類

靜態方法中不能直接建立內部類,為什麼呢?在外部呼叫靜態方法時,可以使用"類名.方法名"的方式,也可以使用"物件名.方法名"的方式。而例項方法只有後面這種方式。也就是說,呼叫靜態方法可以無需建立物件。 靜態方法在訪問本類的成員時,只允許訪問靜態成員(即靜態成員變數和靜態方法

JavaClone方法

一.  Clone的原理:    將現有的物件克隆一份,包括給現有物件屬性所賦的值,形成一個新的物件,相當於在記憶體中將現有物件拷貝一份,並重新分配記憶體空間,拷貝後的物件和現有的物件相當於物理隔離了,操作克隆物件不會影響原來物件。 二.  Clone

javaclone方法的理解(深拷貝、淺拷貝)

前言: java中的clone一直是一個老生常談的問題,另外關於克隆網上也有很多的寫過這方面的問題。 我在這裡記錄一下我遇到的問題和使用clone的方法。 知識點一:什麼是淺拷貝? 我們這裡說的淺拷貝是指我們拷貝出來的物件內部的引用型別

Javaclone方法以及深複製和淺複製

Java中處理基本資料型別(如:int , char , double等),都是採用按值傳遞的方式執行,除此之外的其他型別都是按照引用傳遞(傳遞的是一個物件的引用)的方式執行。物件在函式呼叫時和使用“=”賦值時都是引用傳遞。 Java中clone方法的作用是為了在現實程式

Javasleep()方法與yield()方法的區別

1.執行緒睡眠:sleep()方法 sleep() 使當前執行緒(即呼叫該方法的執行緒)暫停執行一段時間,讓其他執行緒有機會繼續執行(不理會其他執行緒的優先順序),並進入阻塞狀態,但它並不釋放物件鎖。噹噹前執行緒呼叫sleep()方法進入阻塞狀態後,在

JAVAclone方法詳解

為了理解java的clone,有必要先了解一些東西。java的型別,java的型別分為兩大類,一類為primitive,如int,另一類為引用型別,如String,Object等等。java引用型別的儲存,java的引用型別都是儲存在堆上的。  Java程式碼 public

JavatoString方法

如何實現 方法 info ack 接收 總結 java nds 被調用   在Java中所有的類都有一個共同的父類Object(默認繼承,不用加extends關鍵字),toString方法就是Object類的一個方法,用來返回該對象的字符串表示形式(個人總結:需要把該類轉換

java構造方法建立JFrame

package test; import java.awt.*; import javax.swing.*; public class demo extends JFrame{ privat

javaclone 方法的作用

java中的資料型別分為:基礎資料型別(int、char、double等),非基礎型別(,map.class等)。 在處理基本資料型別時候,採用按值傳遞的方式(傳遞的是輸入引數的複製),其他型別的處理按引用傳遞(傳遞的是物件的一個引用),另外物件呼叫時是引用傳遞,在使用“=

Object物件clone方法

克隆的目的:快速建立一個已有物件的副本。克隆的步驟:建立一個物件將原有物件的資料匯入到新建立的資料中1. Object的clone()原始碼簡介 /** * Creates and returns a copy of this {@code Object}. The default *

JavaSE入門學習23:Java面向對象構造方法

ons 抽象類 什麽 ont 機會 語法 好的 error return 學了JavaSE面向對象這一部分,也該對構造方法做一個總結了。 一構造方法 在多數情況下,初始化一個對象的終於步驟是去調用這個對象的構造方法。構造

java設計模式模板方法模式

java 設計模式宋丹丹姐的一個小品,說把大象放入冰箱分為幾步驟,三步、第一步:把冰箱門打開,第二步:把大象裝進去,第三步:把冰箱門關上。就是這樣的一個小品,可以說是其實簡化的一個模板方法。把雞蛋裝進冰箱分為幾步,同樣也是這個道理。模板方法模式概念:把一些共同的行為抽象出來,作為父類公共使用,一些具體的步驟

Java線程,常用方法,線程同步,死鎖

時間 imp log 沖突 根據 oms adl 無法 誰的 1, 線程的概念 進程與線程 進程:每個進程都有獨立的代碼和數據空間(進程上下文),進程間的切換會有較大的開銷,一個進程包含1--n個線程。(進程是資源分配的最小單位) 線程:同一類線程共享代碼和數據空間,每個線

JAVA與模式》模板方法模式

cal php pri jsm vsm uft tmp throw tox 模板方法模式是類的行為模式。準備一個抽象類,將部分邏輯以具體方法以及具體構造函數的形式實現,然後聲明一些抽象方法來迫使子類實現剩余的邏輯。不同的子類可以以不同的方式實現這些抽象方法,從而對剩余的邏輯

JAVA與模式》工廠方法模式

ktv bwt dex zoho ase lba dmg bps rpc 在閻宏博士的《JAVA與模式》一書中開頭是這樣描述工廠方法模式的:   工廠方法模式是類的創建模式,又叫做虛擬構造子(Virtual Constructor)模式或者多態性工廠(Polymorphic

Java使用Integer類中的parseInt()方法將字符串轉換為基本數據類型

javaimport java.util.*; public class SystemInfo { public static void main(String[] args) { Properties sp = System.getProperties();