1. 程式人生 > >Java基礎(4)物件克隆(複製)

Java基礎(4)物件克隆(複製)

以下介紹兩種不同的克隆方法,淺克隆(ShallowClone)深克隆(DeepClone)

在Java語言中,資料型別分為值型別(基本資料型別)和引用型別,值型別包括int、double、byte、boolean、char等簡單資料型別,引用型別包括類、介面、陣列等複雜型別。淺克隆和深克隆的主要區別在於是否支援引用型別的成員變數的複製,下面將對兩者進行詳細介紹。

一、淺克隆

在Java語言中,通過覆蓋Object類的clone()方法可以實現淺克隆

1. 被複制的類需要實現Clonenable介面(不實現的話在呼叫clone方法會丟擲CloneNotSupportedException異常), 該介面為標記介面(不含任何方法)

2. 覆蓋clone()方法,訪問修飾符設為public方法中呼叫super.clone()方法得到需要的複製物件。(native為本地方法)

3. API裡其中一個實現了clone方法的類:java.util.Date

4. 在淺克隆中,如果原型物件的成員變數是值型別,將複製一份給克隆物件;如果原型物件的成員變數是引用型別,則將引用物件的地址複製一份給克隆物件,也就是說原型物件和克隆物件的成員變數指向相同的記憶體地址。

簡單來說,在淺克隆中,當物件被複制時只複製它本身和其中包含的值型別的成員變數,而引用型別的成員物件並沒有複製。


二、深克隆

在Java語言中,如果需要實現深克隆,可以通過覆蓋Object類的clone()方法實現(這裡引用型別的成員變數也要實現clone()方法),也

可以通過序列化(Serialization)等方式來實現。(如果引用型別裡面還包含很多引用型別,或者內層引用型別的類裡面又包含引用型別,使用clone方法就會很麻煩。這時我們可以用序列化的方式來實現物件的深克隆。

1. 序列化就是將物件寫到流的過程,寫到流中的物件是原有物件的一個拷貝,而原物件仍然存在於記憶體中。通過序列化實現的拷貝不僅可以複製物件本身,而且可以複製其引用的成員物件,因此通過序列化將物件寫到一個流中,再從流裡將其讀出來,可以實現深克隆。需要注意的是能夠實現序列化的物件其類必須實現Serializable介面,否則無法實現序列化操作。

2. 在深克隆中,無論原型物件的成員變數是值型別還是引用型別,都將複製一份給克隆物件,深克隆將原型物件的所有引用物件也複製一份給克隆物件。

簡單來說,在深克隆中,除了物件本身被複制外,物件所包含的所有成員變數也將複製。


三、例子

開發中遇到這樣的情況,需要將上傳上來的Excel檔案,先儲存原始檔,再進行提取和處理,此時,需要將上傳來的流檔案物件進行克隆,儲存。

首先,在後臺用於接收前臺提交的Excel檔案的物件是CommonsMultipartFile,CommonsMultipartFile實現了Serializable介面。

克隆檔案物件:

	private CommonsMultipartFile[] cloneMultiFiles(CommonsMultipartFile[] multiFiles){
		CommonsMultipartFile[] copyMutiFiles = null;
		ByteArrayOutputStream baos = null;
		ObjectOutputStream oos = null;
		ByteArrayInputStream bais = null;
		ObjectInputStream ois = null;
		try {
			baos = new ByteArrayOutputStream();
			oos = new ObjectOutputStream(baos);
			oos.writeObject(multiFiles);
			bais = new ByteArrayInputStream(baos.toByteArray());
			ois = new ObjectInputStream(bais);
			copyMutiFiles = (CommonsMultipartFile[]) ois.readObject();
		} catch (IOException | ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			if(baos!=null){
				try {
					baos.flush();
					baos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(oos!=null){
				try {
					oos.flush();
					oos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(bais!=null){
				try {
					bais.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(ois!=null){
				try {
					ois.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

		}
		return copyMutiFiles;
	}

儲存原始檔:

	private void saveOriginFiles(CommonsMultipartFile[] multiFiles, String resultFolder) throws IOException {
		String originFileFolder = resultFolder+"_origin";
		for(int i=0;i<multiFiles.length;i++){
			File originFile = new File(originFileFolder+File.separator+multiFiles[i].getOriginalFilename());			
			if(!originFile.getParentFile().exists()){
				originFile.getParentFile().mkdirs();
			}
			if(originFile.exists()){
				//先刪除已存在的檔案
				originFile.delete();
			}
			originFile.createNewFile();
			multiFiles[i].transferTo(originFile);			
		}
		//進行資料夾壓縮並刪除原始檔
		String zipFileName=originFileFolder.concat(".zip");
		ZipCompressorUtil zc = new ZipCompressorUtil(zipFileName);
		zc.compress(originFileFolder);
		boolean flag=FileUtil.forceDeleteFile(originFileFolder);
		log.info("原檔案儲存成功: ["+originFileFolder+"], "+flag);
	}