1. 程式人生 > >[瘋狂Java]基礎類庫:Object、深拷貝、Objects工具類

[瘋狂Java]基礎類庫:Object、深拷貝、Objects工具類

1. Object類簡介:

    1) 是Java所有型別的基類,如果一個自定義的類沒有extends顯示指定其父類則它預設繼承Object類;

    2) 常用方法(通常需要根據需求覆蓋,Object本身對它們的定義極其簡單):

         i. 相等判斷:

public boolean equals(Object obj) {
    return (this == obj);
}
!!可以看到就是簡單的判斷是否地址相同;

         ii. 雜湊值:public native int hashCode();  // 預設用地址值計算,和地址一一對應

         iii. 返回執行時類:public final native Class<?> getClass();

         iv. 返回字串表示:

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
!!可以看到原始的字串資訊就是"類名@雜湊值的16進製表示";

    3) 上面這些方法除了getClass不能覆蓋之外其餘都可以覆蓋!!

    4) 其它幾個常用方法(之前用過):

         i. finalize:回收之前呼叫;

         ii. wait、notify、notifyAll:用於執行緒通訊;

2. 自我克隆簡介:clone

    1) 是Object中提供的一種最基礎的工具,可以實現物件的自我克隆,和C++的拷貝建構函式是一個道理;

    2) 原型:protected native Object Object.clone() throws CloneNotSupportedException;

    3) 該方法的目的是克隆物件的整個記憶體空間(克隆出的物件和原物件在記憶體中是相互隔離的),和C++一樣同樣存在淺拷貝和深拷貝的問題;

    4) 如果一個類想要實現克隆的功能就必須實現該方法,因為它在Object中是protected,即對外界是隱藏的,只能在子類中使用;

    5) 如何實現克隆呢?需要以下的步驟完成:

         i. 實現類要implements一個Cloneable介面,該介面是一個標記介面,裡面沒東西;

!!標記介面是給人看的,而不是給編譯器看的,僅僅就是用來提醒類庫的使用者,該類已經實現了自我克隆的功能,可以放心使用了!僅此而已!只是增強可讀性而已;

         ii. 自己重寫clone方法,一般clone是要對外開放的,因此重新的時候要把protected改成public!

         iii. 在clone的實現中不用像C++那樣非常麻煩地將每個成員都複製給克隆物件一個,只需要呼叫Object基類的clone方法即可,即:

@Override
public Object clone() throws CloneNotSupportedException {
	// TODO Auto-generated method stub
	return super.clone();
}
!就可以實現克隆了!

    6) 為什麼??為什麼Object的clone方法那麼神奇?都不需要自己手寫派生部分的克隆嗎?這裡我們要講解以下Object的clone的原理,其實它是這樣實現的:

protected native Object clone() throws CloneNotSupportedException {
	getClass一下得到當前的執行時類!  // 即使你是子類也是可以辨別出來的!
	由於在程式語言中型別決定了物件記憶體空間的大小,因此就決定了要拷貝的空間的大小len了;
	然後在獲得this的地址;
	然後將this開始的len大小的記憶體空間賦值到一個新的len大小的記憶體空間中;
	最後返回新記憶體空間的指標(引用)即可;
}
!!也就是說它根據型別確定空間的大小,然後就賦值這麼大的記憶體空間即可(即物件的純資料區),因此還是非常智慧的;

!!由於它是根據記憶體空間總大小一次性複製,因此其效率特別高(一次性複製類似於C語言的memcpy);

    7) 但是Object的這種clone就有一個非常大的隱患,那就是淺複製:如果物件中還包含其它物件,那麼拷貝的時候僅僅拷貝的就是一個指標值,而這兩個物件的該指標指向的還是同一段記憶體,其指向的區域並沒有隔離,因此需要自己解決深複製的問題;

3. 深拷貝:

    1) 深拷貝必須由開發者自行實現,而實現的方法就是“遞迴”克隆;

    2) 遞迴克隆的要領:總的來說有兩點

         i. 物件成員必須也實現了Cloneable介面(這裡的實現必須是實現clone方法);

         ii. 在外層物件的clone方法中顯示呼叫該物件成員的clone方法;

    3) 模板示例:

class MemObj implements Cloneable {
	// 資料定義...

	@Override
	public Object clone() throws CloneNotSupportedException {
		// 具體實現,保證該類本身就是深拷貝
	}
	
}

class Obj implements Cloneable {
	// 基本型別資料定義
	MemObj memObj; // 引用型別資料定義

	@Override
	public Object clone() throws CloneNotSupportedException {
		Obj obj = null;
		
		try {
			obj = (Obj)super.clone(); // 先暫時獲取一個淺拷貝的物件
		}
		catch (CloneNotSupportedException e) { // 拷貝過程中可能出現異常
			e.printStackTrace();
		}
		
		// 如果沒有引用型別成員該句就不用,其它的所有程式碼都是模板程式碼
		obj.memObj = (MemObj)memObj.clone(); // 再利用成員物件已經實現好的深拷貝clone單獨拷貝該成員
		
		return obj; // 最終返回
	}
	
	
}
!!可以看到上面的定義是一種遞迴定義,一個物件要能深拷貝就必須保證其成員也能深拷貝;

    4) Java類庫中有些類(引用型別的)可以像int等基本型別一樣可以直接用Object的clone進行深拷貝!!

         i. String就是一個典型的例子,如果包含它的類想要實現深拷貝就直接super.clone就行了,無需遞迴顯式呼叫String成員物件的clone!

         ii. 這種類在底層其實已被Java轉化成了基本資料型別;

         iii. 那應該怎樣辨別Java類庫中哪些類可以自動深拷貝(像String一樣)哪些由跟普通使用者自定義類一樣需要遞迴深拷貝呢?

         iv. 方法很簡單:只要該類的clone方法是可見的,那就必須顯式遞迴深拷貝,如果clone方法是不可見的那就可以自動深拷貝(super.clone就行了);

         v. 示例:

class A implements Cloneable {
	public String name; // clone可見,因此可以自動深拷貝
	public int[] arr = {1, 2, 3, 4, 5}; // 陣列型別的clone不可見,必須手動遞迴深拷貝
	
	public A (String name) {
		this.name = name;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		A a = (A)super.clone();
		a.arr = arr.clone(); // arr必須手動呼叫clone深拷貝!!
		return a;
	}
	
}

class B implements Cloneable { // 再巢狀一層
	public A a;
	public B(A a) {
		this.a = a;
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		B b = (B)super.clone();
		b.a = (A)a.clone();
		return b;
	}
	
}

public class Test {
	
	public static void main(String[] args) throws FileNotFoundException, IOException, CloneNotSupportedException {
		B b1 = new B(new A("lala"));
		B b2 = (B)b1.clone();
		b1.a.name = "xxx";
		b1.a.arr[0] = 999;
		System.out.println(b2.a.name); // lala
		System.out.println(Arrays.toString(b2.a.arr)); // 1, 2, 3, 4, 5 第一個並不是999,說明記憶體隔離
		System.out.println(b1.a == b2.a); // false,引用成員a的記憶體也是隔離的
	}
}


4. Objects工具類:

    1) 裡面包含了和Object一樣的方法,只不過都是靜態的工具方法,並且引數是一個Object物件;

    2) 其目的就是防止空指標異常,設想你要呼叫一個物件的toString方法,如果該物件引用還是一個null那豈不是會丟擲空指標異常嗎?

    3) Objects就是為了解決當物件呼叫Object基礎方法時發生的空指標異常問題!!

    4) Object類中對應的幾個:如果引數就是null的,那麼將返回null

         i. static String toString(Object o);  // 內部呼叫o的toString方法,只不過空指標安全

         ii. static int hashCode(Object o);  // 內部呼叫o的hashCode方法,只不過空指標時返回0

    5) 其它幾個常用的工具方法:

         i. 非空賦值:static <T> T requireNonNull(T obj);  // 要求obj不為空,否則丟擲異常,如果不為空就返回obj本身

!!!類似於C++的assert先判斷一個東西不為空,只有不為空的時候才能拿它賦值:

Obj obj = Objects.requireNonNull(otherObj);

// 相當於
assert(otherObj);
obj = otherObj;
         ii. 標準非空判斷:static boolean isNull(Object obj);  // 比obj == null看上去更加正規!

!!其實其實現就是:

public static boolean isNull(Object obj) {
    return obj == null;
}



相關推薦

[瘋狂Java]基礎Object拷貝Objects工具

1. Object類簡介:     1) 是Java所有型別的基類,如果一個自定義的類沒有extends顯示指定其父類則它預設繼承Object類;     2) 常用方法(通常需要根據需求覆蓋,Object本身對它們的定義極其簡單):          i. 相等判斷:

java基礎梳理三基本資料型別轉換運算子

1、基本資料型別轉換 byte i = 2;int j = 3;byte result = i + j;×①賦值號右側兩個int型別的變數相加,得到的還是一個int型別的結果,把int型別的結果賦值給byte型別的變數,產生精度丟失,提示出錯 ②賦值號右側int型別的變數和byte型別的變數相加

筆記十複製建構函式拷貝拷貝

複製建構函式 定義: 只有單個形參,而且該形參是對本類型別物件的引用(常用const修飾),這樣的建構函式成為複製建構函式。複製建構函式可用於: 1、根據另一個同類型的物件顯示或隱式初始化一個物件 2、複製一個物件,將它作為實參傳遞給一

java中clone方法的理解(拷貝拷貝

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

Object的clone()方法拷貝拷貝

         一個物件直接使用=,比如Object o1=new Object(); Object o2=o1;那麼問題是o1改變,o2也會改變。 這時候,需要不隨之前的物件改變而改變,使用clone。 需要注意clone是protect的,所以子類繼承Object

javascript---物件和函式的引用拷貝拷貝遞迴

1、javascript 對象和函式的引用 <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>javascript 物

拷貝拷貝淺賦值賦值

一、淺拷貝     物件初始化物件的時候調動拷貝建構函式,只是拷貝指標指向的拷貝構造稱為淺拷貝。       當要析勾的時候物件被一一析勾的時候,第二個析勾的物件就找不到需要釋放的空間,程式報錯。

Java基礎DateCalendar

今天在用Java寫一道去年寒假用C語言刷過的一道特別簡單的水題的時候,用到了Java類庫的Date、Calendar類,不妨寫個總結以便下次複習~ 題目是這樣的: 給定一個日期,輸出這個日期是該年的第幾天 Input 輸入資料有多組,每組佔一行,資料格

夯實Java基礎系列9深入理解ClassObject

目錄 Java中Class類及用法 Class類原理 如何獲得一個Class類物件 使用Class類的物件來生成目標類的例項 Object類 類構造器public Object(); registerNatives()方法; Clone()方法實現淺拷貝 getClass()方法 equals()方法

Java基礎_3.5簡單Java

inf 簡單 字符串 stat 被調用 name屬性 職位 void 類的定義 簡單Java類 簡單Java類是一種在實際開發之中使用最多的類的定義形式,在簡單Java類中包含有類、對象、構造方法、private封裝等核心概念的使用,而對於簡單Java類首先給出如下的基本開

java基礎 第十二章(異常處理工具集合)

重寫 trac com int 出現異常 sta 順序存儲 空指針異常 處理 一、異常處理 1.兩種異常: (1)程序員自身問題(運行時異常) (2)外界問題(可控異常) 2.兩種異常的詳細說明 (1)運行時異常

java基礎之十三Abstract和方法

.get 引用 ava ESS 實現 print student 通過 bst 這篇介紹Java中抽象類和抽象方法,用關鍵字abstract表示抽象,是一個可以修飾類和方法的關鍵字。如果類名前面用abstract修飾,這個類就是抽象類。如果方法名稱前面有abstract修

Java基礎 實驗二和物件

1.實驗目的 掌握類的宣告、物件的建立、方法的定義和呼叫、建構函式的使用。 2.實驗內容 1)定義一個表示學生資訊的類Student,要求如下: ① 類Student的成員變數:       sNO 表示學號;      

Java基礎複習第七天——面向物件思想物件封裝構造方法JavaBean

目錄 一 面向物件思想 1.概述 2.面向物件的三大特徵          3.類和物件 4.類和物件的關係 5.類的定義 6.成員變數和區域性變數 7.物件的使用格式 8.物件記憶體圖 二.封裝

Java基礎複習第六天——方法的定義呼叫形參實參方法過載ArrayList(集合)

一.方法 定義格式: //定義方法:求兩個整數之和 //返回值型別 int //引數:未知量 2個 都是int public static int getSum(int num1,int num2) { //方法體 int sum = num1 + num2;

Java基礎知識回顧之Object

簡介 類Object是類層次結構的根類。每個類都使用Object作為超類。所有物件(包括陣列)都實現這個類的所有方法。我們接觸到的元素有:物件、陣列、介面等,這麼多的元素為了方便統一,就可以使用 Object。 任何一個類在定義的時候如果沒有明確的繼承一個父類的話,那麼他就是Ob

Java基礎學習之--理解Object

看Java API的Object類, 一共11個方法。按使用的頻度排名: toString() 這個方法最常用在打日誌,定位程式碼問題。 equals()和hashCode(), 這兩個方法的使用經典例子是HashMap的原始碼 public V put(K key, V val

java基礎梳理二基本資料型別變數

1、基本資料型別  分為四大類: 佔用位元組數①整數型別byte:位元組型別 1short:短整型 2int:整型 4long:長整型 8②浮點數型別float:單精度                

java基礎-反射(1)基本周邊資訊獲取

前言:堅持夢想,過程或是艱辛的,回憶是幸福的。與其最後豪言如果當時我怎樣怎樣,倒不如堅持腳下。 相關文章: 今天開始給大家講講有關反射的知識,在應用程式開發時,如果純做上層,搭搭框架啥的,那用到反射的機會不多,但如果你想做出來一個公共類或者公共模組給其它人用的時候,那用到反射的可能性就大大增加了。況且

[瘋狂Java]SQL-連線查詢SQL92SQL99

1. 連線查詢:     1) 即查詢的時候同時需要多張表(特別是存在外來鍵關係的),此時需要多張表之間的值進行連線;     2) 目前SQL標準提出過兩種連線查詢,第一種是較早的SQL92標準,第二種是目前使用廣泛的較新的SQL99標準; &n