[瘋狂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基礎類庫:Date、Calendar類
今天在用Java寫一道去年寒假用C語言刷過的一道特別簡單的水題的時候,用到了Java類庫的Date、Calendar類,不妨寫個總結以便下次複習~ 題目是這樣的: 給定一個日期,輸出這個日期是該年的第幾天 Input 輸入資料有多組,每組佔一行,資料格
夯實Java基礎系列9:深入理解Class類和Object類
目錄 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-連線查詢:SQL92、SQL99
1. 連線查詢: 1) 即查詢的時候同時需要多張表(特別是存在外來鍵關係的),此時需要多張表之間的值進行連線; 2) 目前SQL標準提出過兩種連線查詢,第一種是較早的SQL92標準,第二種是目前使用廣泛的較新的SQL99標準; &n