摘要:而并不是父類對象的引用,而只是給編譯器的一個提示性質的標志。或者自定義的提示在編譯的時候使用當前子類的父類定義的構造器去初始化當前對象。所以,總結起來,的用法歸為兩種一是可以調用父類構造器,二是可以調用父類方法。
Core Java Volume 1 Key Points chap5 super關鍵字和this開篇
Java是一門不那么簡單也不那么復雜的語言,Java里面有很多問題和特性是容易被使用者忽視的,這些問題也許會難住新手,同時也許會是老手不小心跌入的無故之坑,只有精于對基礎的提煉才能最大程度地解決類似的疑問。所以,在讀Cay.Horstmann的《Java核心技術》的過程中,我記錄下這些所謂的易忽略的問題,這些問題將會持續更新在我的這個Segment Fault的博客下,也算是激勵自己重新挖掘這些基礎問題的內涵。這個博客將以原書中的章節為分割,大概會是每章一篇,持續更新,每篇的內容也不會一次全部寫完,視我個人對問題的理解和我的閱讀進度而定。
super這個關鍵字和this其實是完全不同的,因為this實際上是一個真實存在的引用,是一個缺省的傳入方法的隱式參數,引用當前的對象,所以可以完全當做一個正常的引用來使用。而super并不是父類對象的引用,而只是給javac編譯器的一個提示性質的標志。當使用如下的super時:
super.someFunction(); 提示javac在編譯這個someFunction()方法的時候去使用該類的父類定義的someFunction()方法,這一般是在當前的子類也定義了同樣名字的someFunction()方法的時候去使用的。
super();或者自定義的super(type Par); 提示javac在編譯的時候使用當前子類的父類定義的構造器去初始化當前對象。
所以,總結起來,super的用法歸為兩種:一是可以調用父類構造器,二是可以調用父類方法。
子類對象的初始化過程:子類執行子類的初始化方法,初始化方法里會先執行缺省的super()方法(寫不寫這個super方法都會執行),也就是說會先初始化出一個父類對象,然后再執行其他的初始化部分,最終之前初始化出的父類對象將會成為子類對象,這也是多態性的起始,因為本質上來講,子類對象是有父類對象的結構的。
繼承存在情形下的對象初始化在繼承存在的時候,初始化的過程就變得異常復雜,但是卻仍然遵循著初始化的原則:先加載類和屬于類的static屬性,然后創建類的對象,因為有繼承的問題,所以在初始化子類對象的時候要先初始化其父類的對象。
這個例子極佳地說明了這個復雜的過程,可以使用單步調試給出初始化的全過程:
class FatherClass { private static FatherClass f = new FatherClass(); static { b = 10; System.out.println("father static block"); } { System.out.println("father object block"); } static int b = 5; public FatherClass() { System.out.println("father constructor..."); System.out.println( "b = " + b); } } public class ChildClass extends FatherClass { static int a = 5; static { a = 10; System.out.println("child static block"); } { System.out.println("chid object block"); } private static ChildClass t1 = new ChildClass(); public ChildClass() { System.out.println("child constructor..."); System.out.println("a = " + a); } public static void main(String[] args) { ChildClass test = new ChildClass(); } }
這是運行的初始化結果:
先加載類,所以順序執行static代碼塊和static屬性聲明,按照先父類再子類的順序加載類,
father object block //加載父類執行f的初始化時激發了父類對象的初始化,對象的初始化塊執行 father constructor... //對象的構造器 b = 0 //未經初始化的屬性的缺省值 father static block //f的初始化完成,執行父類的靜態初始化快,父類加載完畢 child static block //子類初始化開始,執行子類的靜態初始化塊 father object block //t1的初始化激發了子類對象的初始化,并進而激發了父類對象的初始化,執行父類對象初始化塊 father constructor... //執行父類的構造器 b = 5 //父類的域屬性已經被初始化,父類對象初始化完成 chid object block //子類對象開始初始化,對象初始化塊執行 child constructor... //子類對象的構造器執行 a = 10 //子類對象的域屬性已被初始化,完成子類的加載 father object block //執行父類對象的初始化 father constructor... b = 5 chid object block //執行子類對象的初始化 child constructor... a = 10多態是怎么實現的
如果使用最簡單的辦法去說明什么是多態,那么這樣寫無疑是有力的:
FatherClass child = new ChildClass(); child.funcOverride();
這個的意思就是說棧中創建的父類對象可以引用堆中的子類對象,那么這個過程為什么可以實現呢?從兩個方面可以說明:
從對象初始化的角度來說,和上一節我們說到的一樣,子類對象的初始化意味父類先加載,子類再加載,父類對象初始化,子類對象初始化,所以子類對象實際上就是在父類對象的基礎上生成的,因此父類對象當然可以引用子類對象了;
從方法覆蓋的角度來說,父類對象的方法子類對象都有,因此對父類方法的使用實際上就會成為對子類方法的使用,這個過程不能反過來,也就是說子類對象的方法并不是父類對象都有,因此不能使用子類對象去引用父類對象。
jvm為了實現多態情況下方法執行的快速判斷,會為每個類維護一個虛方法表(和C++實現多態的虛擬函數有關),這個方法表表述了該類型對象在執行某方法時應該執行的方法具體是哪個,理論上在child去執行funcOverride()方法的時候會先去看ChildClass是否擁有覆蓋的funcOverride()方法,如果有則執行這個,如果沒有覆蓋則執行父類的同名方法,而實際上為避免做這些可能要進行多次的判斷,jvm為類準備的虛方法表固化這種方法對應關系,進而可以快速定位要執行的方法。
舉個例子:
class FatherClass{ public void func1(){ System.out.println("func from FC"); } public void func2(){ System.out.println("func from FC"); } } class ChildClass extends FatherClass{ @Override public void func1() { System.out.println("func from CC"); } }
那么,jvm為類維護的虛方法表類似(不要去考慮和這個問題無關的Object自帶方法):
VirtualMethodTableOfFatherClass: FatherClass.func1() -- FatherClass.func1() FatherClass.func2() -- FatherClass.func2() VirtualMethodTableOfChildClass: ChildClass.func1() -- ChildClass.func1() ChildClass.func2() -- FatherClass.func2()一個完善的equals方法怎么寫呢?
每個類都源自Object類,所以也就不得不接受Object父類帶來的基本方法,比如這個麻煩的額equals方法,在Object類中的equal方法非常簡單,就是一句話:
public boolean equals(Object obj) { return (this == obj); }
這句話用于判斷當前這個棧中變量引用的堆中對象是否和參數變量引用的堆中對象和同一個對象,所以這其實是個很抽象的“相等”,因為這就像廢話一樣。
所以一個完善的equals方法應該恰當地指出兩個對象相等的內涵,即時兩個變量并不是指向同一塊內存,如果他們的某些被指定的屬性是一樣的,那么就可以算作是“相等”。所以,我們有以下這樣對對象相等的考慮:
1.是否指向同一塊堆中內存空間,也就是是否引用同一對象,如果是,這肯定相等;
2.是否是空引用的比較,如果不是,則才可能相等;
3.是否具有同樣的類型,如果是,則才可能相等;
4.是否具有繼承鏈上的一系列類型,如果是,則可能相等;
5.是否某些屬性是相等的,如果是,則才可能相等。
基于上面的考慮,我們可以有這樣的例子來寫出一個相對完善的equals方法:
(特別注意的是,equals的參數是Object類型的,只有這樣才能覆蓋Object的equals方法)
class Employee{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object obj) { if(this == obj) return true; if(obj == null) return false; if(this.getClass() != obj.getClass()) return false; Employee tempEmployee = (Employee)obj; return tempEmployee.getName().equals(name); } } public class TestClass { public static void main(String[] args) { Employee employee1 = new Employee(); employee1.setName("boss"); Employee employee2 = new Employee(); employee2.setName("boss"); System.out.println(employee1.equals(employee2)); System.out.println(employee2.equals(employee1)); } }ArrayList之類的集合是值拷貝的和還引是用拷貝的
我們都知道ArrayList這樣的數據集合就是Java快速處理批量數據的容器,那么對這個容器里的每個數據元素而言,是把每個元素的值拷貝到容器中呢還是只是把它們的引用拷貝到容器中去呢?從效率和資源占用的角度來說,ArrayList選擇了引用拷貝,只是把“構成”這個集合的數據元素的引用放到了容器中,這個例子說明了這個問題:
并不是每次“添加”一個數據元素到容器中,而是“引用”一個元素到容器中,如果每次都拿一個數據元素做引用,最后所有的容器元素索引都指向一個引用的地址。
class Employee { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } public class TestClass { public static void main(String[] args) { Employee employee = new Employee(); ArrayListlist = new ArrayList (); for (int i = 0; i < 5; i++) { employee.setName("name" + i); list.add(employee); } for (Employee each : list) { System.out.println(each.getName()); } } }
未完成。。。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65335.html
摘要:所以,在讀的核心技術的過程中,我記錄下這些所謂的易忽略的問題,這些問題將會持續更新在我的這個的博客下,也算是激勵自己重新挖掘這些基礎問題的內涵。類路徑只是讓能夠通過配置好的全路徑名找到所需的外部類。 開篇Java是一門不那么簡單也不那么復雜的語言,Java里面有很多問題和特性是容易被使用者忽視的,這些問題也許會難住新手,同時也許會是老手不小心跌入的無故之坑,只有精于對基礎的提煉才能最大...
摘要:內部類就是這樣一個情況,內部類的出現雖然在運行時會被拆分為獨立的臨時類,但是在代碼層面加深了對代碼的理解難度,所以很難說其優弊殊勝。 Core Java Volume 1 Key Points chap6 接口和抽象類的概念 接口和抽象類是Java繼承鏈的基礎,其區別也較為明顯,在Java語言的設計中,允許接口的多實現,但不允許抽象類的多繼承,這樣做符合簡潔明了的面向對象設計思路:也就...
摘要:前言大家好,這里是從零開始學之數據類型,本文首發于公眾號,歡迎前往大家關注。輸出布爾類型中的布爾類型用表示,它的值有和。若需要可空引用時,布爾類型的值會被裝箱。此時程序會拋出異常最后從零開始學之數據類型到這里就結束了。 前言 大家好,這里是「從零開始學 Kotlin 之『2 』數據類型」,本文首發于公眾號「Binguner」,歡迎前往大家關注。我會每周分享一些關于 Android 和...
摘要:在使用字面量表示法的時候,并不會調用對象的構造函數種常用方法同一樣可通過和字面量兩種方法來創建。直接調用基本包裝類型的構造函數,返回實例都屬于這個構造函數是會根據參數返回相應的基本包裝類型的實例。 第五章、引用類型 一共七種引用類型: Object: 可通過new和字面量兩種方法來創建。在使用字面量表示法的時候,并不會調用對象的構造函數 Array: 17種常用方法; 同object...
閱讀 1668·2023-04-26 00:30
閱讀 3145·2021-11-25 09:43
閱讀 2868·2021-11-22 14:56
閱讀 3183·2021-11-04 16:15
閱讀 1137·2021-09-07 09:58
閱讀 2013·2019-08-29 13:14
閱讀 3101·2019-08-29 12:55
閱讀 982·2019-08-29 10:57