Java基礎(chǔ)之繼承

繼承
this關(guān)鍵字的用途

    引用隱式參數(shù);
    調(diào)用該類其他的構(gòu)造器。

super關(guān)鍵字的用途

    調(diào)用超類的方法。
    調(diào)用超類的構(gòu)造器。

多態(tài)

可以通過 is-a規(guī)則,它表明子類的每個對象也是超類的對象。例如,每個
經(jīng)理都是雇員。因此,將Manager類設(shè)計為Employee類的子類是顯而易見的,反之不然,并不是每一名雇員都是經(jīng)理。

is-a 規(guī)則的另一種表述法是置換法則。它表明程序中出現(xiàn)超類對象的任何地方都可以用子類對象置換。
例如:可以將一個子類的對象賦給超類變量

Employee e;
e=new Employee();
e=new Manager();



理解方法調(diào)用

弄清楚如何在對象上應(yīng)用方法調(diào)用非常重要。下面假設(shè)要調(diào)用 x.f(args),隱式參數(shù)x聲明為類C的一個對象。下面是調(diào)用過程的詳細描述:
1. 編譯器查看對象的聲明類型和方法名。

假設(shè)調(diào)用 x.f(args),且隱式參數(shù)x聲明為C類的對象。需要注意的是:有可能存在多個名字為f,但參數(shù)類型不一樣的方法。例如,可能存在
方法 f(int)和方法f(String)。編譯器將會一一列舉所有C類中名為f的方法和其超類中訪問屬性為public且名為f的方法。(超類的私有方法不可訪問)
2. 編譯器將查看調(diào)用方法時提供的參數(shù)類型。

如果在所有名為f的方法中存在一個與提供的參數(shù)類型完全匹配,就選擇這個方法。這個過程稱為**重載解析**。例如,對于調(diào)用 `x.f("HelloWorld")`
來說,編譯器將會挑選 `f(String)`,而不是 `f(int)` 。由于允許類型轉(zhuǎn)換(int 可以轉(zhuǎn)換成double,Manager可以轉(zhuǎn)換成Employee,等等),所以這個過程
可能很復雜。如果編譯器沒有找到與參數(shù)類型匹配的方法,或者發(fā)現(xiàn)經(jīng)過類型轉(zhuǎn)換后有多個方法與之匹配,就會報告一個錯誤。

至此,編譯器已獲得需要調(diào)用的方法名字和參數(shù)類型。

**方法的名字和參數(shù)列表稱為方法的簽名,返回類型不是簽名的一部分。


3. 如果是Private方法、static方法、final方法或者構(gòu)造器,那么編譯器將可以準確地知道應(yīng)該調(diào)用哪個方法,我們將這種調(diào)用方法稱為靜態(tài)綁定。與此對應(yīng)的是,
調(diào)用的方法依賴于隱式參數(shù)的實際類型,并且在運行時實現(xiàn)動態(tài)綁定。在我們列舉的示例中,編譯器采用動態(tài)綁定的方式生成一條調(diào)用 f(String) 的指令。

    當程序運行,并且采用動態(tài)綁定調(diào)用方法時,虛擬機一定調(diào)用與x所引用對象的實際類型最合適的那個類的方法。

    假設(shè)x的實際類型是D,它是C類的子類。如果D類定義了方法 f(String),就直接調(diào)用它;否則,將在D類的超類中尋找 f(String),

    每次調(diào)用方法都要進行搜索,時間開銷相當大。因此,虛擬機預先為每個類創(chuàng)建了一個方法表(method table),其中列出了所有方法的簽名和實際調(diào)用的方法。這樣一來。
    在真正調(diào)用方法的時候,虛擬機僅查找這個表就行了。在前面的例子中。虛擬機搜索D類的方法表,以便尋找與調(diào)用 f(String) 相匹配的方法。這個方法既有可能是 D.f(String),
    也有可能是 X.f(String),這里的X是D的超類。這里需要提醒一點,如果調(diào)用 super.f(param),編譯器將對隱式超類的方法表進行搜索。

    現(xiàn)在,查看一下程序清單5-1中調(diào)用e.getSalary() 的詳細過程。

    動態(tài)綁定有一個非常重要的特性:無需對現(xiàn)存的代碼進行修改,就可以對程序進行擴展。假設(shè)增加一個新類 Executive,并且變量e有可能引用這個類的對象,我們不需要
    對包含調(diào)用 e.getSalary()的代碼進行重新編譯。如果e恰好引用一個Executive類的對象,就會自動地調(diào)用 Executive.getSalary()方法。

阻止繼承:final類和方法

不允許擴展的類稱為final類。

 public final class Executive extends Manager{
    ...
 }

equal方法

Object類中的equals方法用于檢測一個對象是否等于另外一個對象。**在Object類中,這個方法將判斷兩個對象是否具有相同的引用。如果
兩個對象具有相同的引用,它們一定是相等的**。從這點上看,將其作為默認操作也是合乎情理的。

Java 語言規(guī)范要求equals方法具有下面的特性:
1. 自反性:對于任何非空引用x,x.equals(x) 應(yīng)該返回true;
2. 對稱性:對于任何引用x和y,當且僅當y.equals(x)返回true,x.equals(y)
也應(yīng)該返回true。
3. 傳遞性:對于任何引用x,y和z,如果x.equals(y) 返回true,y.equals(z) 返回true,
x.equals(z) 也應(yīng)該返回true。
4. 一致性: 如果x和y引用的對象沒有發(fā)生變化,反復調(diào)用x.equals(y) 應(yīng)該返回同樣的結(jié)果。
5. 對于任意非空引用x,x.equals(null) 應(yīng)該返回false。

編寫一個完美的equals方法的建議:

    顯式參數(shù)命名為 otherObject,稍后需要將它轉(zhuǎn)換成另一個叫做other的變量。

    檢測this與otherObject是否引用同一個對象:

    if(this==otherObject) return true;

    檢測otherObject是否為null,如果為null,返回false。

    if(otherObject==null) return false

    比較this與otherObject是否屬于同一個類。如果euqals的語義在每個子類中有所改變,就使用
    getClass 檢測:

    if(getClass()!=otherObject.getClass()) return false
    將otherObject 轉(zhuǎn)換為相應(yīng)的類類型變量:
    ClassName other=(ClassName)otherObject
    例如: TODO 添加例子:



hashCode 方法

散列碼是由對象導出的一個整型值。散列碼是沒有規(guī)律的。
字符串(String)的散列碼是由內(nèi)容導出的。
字符串緩存(StringBuffer)類中沒有定義hashCode方法,它的散列碼是由Object
類的默認hashCode方法導出的對象存儲地址。
toString方法

設(shè)計子類的model,POJO時必須重寫toString方法。并將子類域的描述添加進去。
toString 方法是一種非常有用的調(diào)試工具。在標準類庫中,許多類都定義了toString方法。
以便用戶能夠獲得一些有關(guān)對象狀態(tài)的必要信息。
對象包裝器與自動裝箱

所有的基本類型都有一個與之對應(yīng)的類。這些類稱為包裝器。這些對象包裝器類擁有很明顯的名字:
Integer,Long,Float,Double,Short,Byte,Character,Void和Boolean(前6個類派生于公共的超類Number)。對象
包裝器類是不可變的,即一旦構(gòu)造了包裝器,就不允許更改包裝器在其中的值。同時,對象包裝器類還是final,因此不能定義它們的子類。

自動裝箱

例如:在集合ArrayList中,下面的調(diào)用 list.add(3) 將自動地變換成 list.add(Integer.valueOf(3))
這種變換被稱為自動裝箱。

自動拆箱

當將一個Integer對象賦給一個int值時,將會自動地拆箱。也就是說,編譯器將下列語句:
int n=list.get(i);
翻譯成
int n=list.get(i).intValue()

大多數(shù)情況下,容易有一種假象,即基本類型與它們的對象包裝器是一樣的。只是它們的相等性不同。大家知道,
== 運算符也可以應(yīng)用于對象包裝器對象。只不過檢測的是對象是否指向同一個存儲區(qū)域,因此,下面的比較通常不會成立:

 Integer a=1000;
 Integer b=1000;
 if(a==b) ...


然而,Java實現(xiàn)卻有可能讓它成立。如果經(jīng)常出現(xiàn)的值包裝到同一個對象中,這種比較就有可能成立。這種不確定的結(jié)果的結(jié)果
并不是我們所希望的。解決這個問題的辦法是在兩個包裝器對象比較時調(diào)用equals 方法。

自動裝箱規(guī)范要求boolean,byte,char<=127,介于-127~127之間的short和int被包裝到固定的對象中。例如,如果前面的例子中將a和b
初始化為100,對他們進行比較的結(jié)果一定成立。

如果在一個條件表達式中混合使用Integer和Double類型,Integer值就會被拆箱,提升為double,再裝箱為Double:

Integer n=1;
Double x=2.0;
System.out.println(true?n:x);  //Prints 1.0


裝箱和拆箱是編譯器認可的。而不是虛擬機。編譯器在生成類的字節(jié)碼時,插入必要的方法調(diào)用。虛擬機只是執(zhí)行這些字節(jié)碼。

字符串轉(zhuǎn)成整型。
int x=Integer.parseInt(s);

Java方法都是值傳遞的。如下所示:

    public static void main(String[] args) {
        int x=3;
        triple(x);
        System.out.println("計算后的x="+x);
    }

    public static void triple(int x) {
        x = 3 * x;
        System.out.println("經(jīng)過運算的x="+x);
    }


輸出結(jié)果是:經(jīng)過運算的x=9,計算后的x=3

例如:

    public static void main(String[] args) {
          Employee outEmployee = new Employee();
          outEmployee.setName("測試");
          call(outEmployee);
          System.out.println("employee:"+outEmployee.getName());
      }

      public static void call(Employee inEmployee) {
          Employee employee = new Employee();
          employee.setName("cba");
          inEmployee.setName("abc");
          inEmployee = employee;
      }



輸出結(jié)果是:employee:abc
分析:在此程序中,這次調(diào)用等于聲明了兩個變量outEmployee和inEmployee,他們指向的是同一個地址,
調(diào)用call方法。只是將outEmployee的地址傳給了inEmployee,而outEmployee本身并沒有傳遞過去(也就是引用沒有傳遞過去)
所以在再次賦值時只是影響了inEmployee。
這樣的傳遞方式只能稱為址傳遞,或者是引用對象傳遞,而不能說是引用傳遞。




作者:碼農(nóng)飛哥
微信公眾號:碼農(nóng)飛哥