Java筆記整理——接口、繼承與多態

一、類的繼承

在Java中使用extents關鍵字來標識兩個類的繼承關係。在子類中,可以聯通初始化父類構造方法來完成子類初始化操作,既可以在子類的構造方法中使用super()語句調用父類的構造方法,也可以在子類中使用super關鍵字調用父類的成員方法等,但是子類沒有權限調用父類中被修飾爲private的方法,只可以調用父類中修飾爲public或protected的成員方法。
在繼承中還有一種特殊的重寫方式,子類與父類的成員方法返回值、方法名稱、參數類型及個數完全相同,唯一不同的是方法實現內容,這種特殊重寫方式被稱爲重構。
注意:當重寫父類方法時,修改方法的修飾權限只能從小的範圍到大的範圍改變。
若子類重寫父類的方法,需要修改返回值,這種重寫方式需要遵循一個原則,即重寫的返回值類型必須是父類中同一方法返回值類型的子類。
子類調用構造方法的順序,首先是頂級父類,然後是上級父類,。。。,最後是子類。也就是說實例化子類對象時首先要實例化父類對象,然後再實例化子類對象,所以在子類構造方法訪問父類的構造方法之前,父類已經完成實例化操作。
說明:在實例化子類對象時,父類無參構造方法將被自動調用,但有參構造方法並不能被自動調用,只能依賴於super關鍵字顯式地調用父類的構造方法。
技巧:如果使用finalize()方法對對象進行清理,需要確保子類的finalize()方法的最後一個動作是調用父類的finalize()方法,以保證當垃圾回收對象佔用內存時,對象的所有部分都能被正常終止。

二、Object類

在Java中,所有的類都直接或間接繼承了java.lang.Object類。Object類是所有類的父類,是Java類層中的最高層類。
注意:Object類中除了getClass()、notify()、notifyAll()、wait()等方法不能被重寫,因爲這些方法被定義爲final類型,其他的都可以被重寫。
1、getClass()方法
getClass()方法是Object類定義的方法,它會返回對象執行時的Class實例,然後使用此實例調用getName()方法可以去的類的名稱。語法如下:
getClass().getName();
2、toString()方法
toString()方法的功能是將一個對象返回爲字符串形式。在實際應用中通常從寫toString()方法,爲對象提供一個特定的輸出模式。當這個類轉換爲字符串或與字符串連接時,將自動調用重寫的toString()方法。
示例如下:
import java.lang.Object;
public class ObjectInstance {
 public String toString() {
  return "在" + getClass().getName() + "類中重寫toString()方法";
 }

 public static void main(String[] args) {
  System.out.println(new ObjectInstance());
 }
}
運行結果:
在ObjectInstance類中重寫toString()方法
3、equals()方法
“==”運算符和equals()方法對比,“==”運算符比較的是兩個對象的引用是否相等,而equals()方法比較的是兩個對象的實際內容。
示例如下:
class V {
 
}
public class OverWriteEquals {
 public static void main(String[] args) {
  String str1 = "123";
  String str2 = "123";
  System.out.println(str1.equals(str2));
  V v1 = new V();
  V v2 = new V();
  System.out.println(v1.equals(v2));
 }
}
運行結果:
true
false
從上面的例子的運行結果中可以看出,在自定義的類中使用equals()方法進行比較時,將返回false,因爲equals()方法的默認實現是使用“==”運算符比較兩個對象的引用地址,而不是比較對象的內容,所以要想真正做到比較兩個對象的內容,需要在自定義類中重寫equals()方法。

三、對象類型的轉換

1、向上轉型
一個子類對象可以看作是父類的對象。
示例如下:
class Quadrangle {                                    // 四邊形類
 public static void draw(Quadrangle q){ // 四邊形類中的方法
  // SomeSentence
 }
}
public class Parallelogram extends Quadrangle { // 平行四邊形類,繼承了四邊形類
 public static void main(String[] args) {
  Parallelogram p = new Parallelogram();              // 實例化平行四邊形類對象引用
  draw(p);                                                                  // 調用父方法
 }
}
在父類中存在draw()方法,參數是Quadrangle類型,但是子類調用時可以傳入Parallelogram類型。這就是說平行四邊形也是一種類型的四邊形,所以可以將平行四邊形類的對象看作是一個四邊形類的對象,就相當於"Quadrangle obj = new Parallelogram();",就是把子類對象賦值給父類類型的變量,這種技術被稱爲“向上轉型”。
若在父類對象的draw()方法中根據不同的圖形對象設置不同的處理,就可以做到在父類對象中定義一個方法完成各個子類的功能,這樣可以使同一份代碼毫無差別地運用到不同類型至上,這就是多臺機制的基本思想。
2、向下轉型
示例如下:
class Quadrangle {
 public static void draw(Quadrangle q){
  // SomeSentence
 }
}
public class Parallelogram extends Quadrangle {
 public static void main(String[] args) {
  draw(new Parallelogram());
  // 將平行四邊形對象看作是四邊形對象,稱爲向上轉型操作
  Quadrangle q = new Parallelogram();
  // Parallelogram p = q;
  // 將父類對象賦予子類對象,是錯誤的
  // 將父類對象賦予子類對象,並強制轉換爲子類對象,是正確的
  Parallelogram p = (Parallelogram)q;
 }
}
上例可以看出不能將父類直接賦予子類,會產生編譯錯誤。但是可以將父類對象強制轉換爲某個子類對象,這種方式稱爲顯式類型轉換。當在程序中使用向下轉型技術時,必須使用顯示類型轉換。

四、使用instanceof操作符判斷對象類型

當執行向下轉型操作時,若父類對象不是子類對象的實例,就會發生ClassCastException異常,所以在執行向下轉型之前,一定要先判斷父類對象是否是子類對象的實例。這個判斷通常使用instanceof操作符來完成。可以使用instanceof操作符判斷是否一個類實現了某個接口,也可以用它來判斷一個實例對象是否屬於一個類。instanceof語法如下:
myobject instanceof ExampleClass
示例如下:
class Quadrangle {
 public static void draw(Quadrangle q){
  // SomeSentence
 }
}
class Square extends Quadrangle {
 // SomeSentence
}
class Anything {
 // SomeSentence
}
public class Parallelogram extends Quadrangle {
 public static void main(String[] args) {
  Quadrangle q = new Quadrangle();
  // 判斷父類對象是否爲Parallelogram的一個實例
  if(q instanceof Parallelogram) {
   Parallelogram p = (Parallelogram)q;
  }
  // 判斷父類對象是否爲Parallelogram的一個實例
  if(q instanceof Square) {
   Square s = (Square)q;
  }
  // 由於q對象不爲Anything類的對象,所以下面的語句是錯誤的
  //System.out.println(q instanceof Anything)
 }
}

五、方法的重載

示例如下:
public class OverLoadTest {
 public static int add(int a, int b) {
  return a + b;
 }
 // 定義與第一個方法相同名稱不同參數的方法
 public static double add(double a, double b) {
  return a + b;
 }
 // 定義與第一個方法相同名稱不同參數的方法
 public static int add(int a) {
  return a;
 }
 public static int add(int a, double b) {
  return 1;
 }
 public static int add(double b, int a) {
  return 1;
 }

 public static void main(String[] args) {
  System.out.println("調用add(int,int)方法:" + add(1, 2));
  System.out.println("調用add(double ,double)方法:" + add(2.1, 3));
  System.out.println("調用add(int)方法:" + add(1));
 }
}
運行結果:
調用add(int,int)方法:3
調用add(double ,double)方法:1
調用add(int)方法:1
注意:雖然在方法重載中可以使兩個方法的返回值不同,但只有返回值不同並不足以區分兩個方法的重載,還需要通過參數的個數以及參數的類型來設置。
在談到參數個數可以確定兩個方法是否具有重載關係時,會想到定義不定長參數方法。
示例如下:
public static int add(int...a) { // 定義不定長參數方法
    int s = 0;
    for(int i = 0; i < a.length; i++) {
    s += a[i];                            // 做參數累計操作
    return s;                             // 將結果返回
    }
}
不定長方法的語法如下:
返回值  方法名(參數數據類型...參數名稱)
在參數列表中使用“...“形式定義不定長參數,其實這個不定長參數a就是一個數組,編譯器會將(int...a)這種形式看作是(int[]a),所以在add()方法體做累加操作時使用了for循環。

六、多態
利用多態可以使程序具有良好的擴展性,並可以對所有類對象進行通用的處理。
示例如下:
public class Quadrangle {
 private Quadrangle[] qtest = new Quadrangle[6];
 private int nextIndex = 0;
 public void draw(Quadrangle q) {
  if(nextIndex < qtest.length) {
   qtest[nextIndex] = q;
   System.out.println(nextIndex);
   nextIndex++;
  }
 }

 public static void main(String[] args) {
  Quadrangle q = new Quadrangle();
  q.draw(new Square());
  q.draw(new Paralle());
 }
}
class Square extends Quadrangle {
 Square() {
  System.out.println("正方形");
 }
}
class Paralle extends Quadrangle {
 Paralle() {
  System.out.println("平行四邊形");
 }
}
運行結果:
正方形
0
平行四邊形
1
從本實例的運行結果中可以看出,以不同類對象爲參數調用draw()方法可以處理不同的圖形問題。使用多態節省了開發和維護事件,因爲程序員無須在所有的子類中定義執行相同功能的方法,避免了大量重複代碼的開發,同時,只要實例化一個集成父類的子類對象即可調用相應的方法,這裏只要維護父類中的這個方法即可。

七、抽象類與接口

1、在解決實際問題時,一般將父類定義爲抽象類,需要使用這個父類進行繼承與多態處理。抽象類的語法如下:
public abstract class Test {
    abstract void testAbstract(); // 定義抽象方法
}
使用abstract關鍵字定義的類稱爲抽象類,而使用這個關鍵字定義的方法稱爲抽象方法,抽象方法沒有方法體,這個方法本身沒有任何意義,除非它被重寫,而承載這個抽象方法的抽象類必須被集成,實際上抽象類除了被繼承之外沒有任何意義。反過來講,如果聲明一個抽象的方法,就必須將承載這個抽象方法的類定義爲抽象類,不可能在非抽象類中獲取抽象方法。換句話說,只要類中有一個抽象方法,此類就被標記爲抽象類。
2、接口
1)簡介
接口是抽象類的延伸,可以將它看作是純粹的抽象類,接口中的所有方法都沒有方法體。可以將一個方法封裝到一個接口中,使需要這個方法的類實現這個接口,也同時繼承抽象類,這就是接口存在的必要性。接口的存在避免了子類繼承抽象父類時,必須每次都實現抽象父類中的抽象方法的問題。可以減少代碼的冗餘。
接口使用interface關鍵字進行定義,語法如下:
public interface drawTest {
    void draw(); // 接口內的方法,省略abstract關鍵字
}
一個類實現一個接口可以使用implements關鍵字,如下示:
public class Parallelogram extends Quadrangle implements drawTest {
    ... //
}
注意:在接口中定義的方法必須定義爲public或abstract形式,其他修飾權限不能被Java編譯器認可,即使不將該方法聲明爲public形式,也是public。
說明:在接口中定義的任何字段都自動是static和final的。
示例如下:
interface drawTest {     // 定義接口
 public void draw();  // 定義方法
}
class ParallelogramgleUseInterface extends QuadrangleUseInterface
implements drawTest {
 public void draw() {
  System.out.println("平行四邊形.draw()");
 }
 void doAnything() { // 覆蓋父類方法
  // SomeSentence
 }
}
class SquareUseInterface extends Quadrangle
implements drawTest {
 public void draw() {
  System.out.println("正方形.draw()");
 }
 void doAnything() {
  // SomeSentence
 }
}
class AnythingUseInterface extends Quadrangle {
 void doAnything() {
 
 }
}
public class QuadrangleUseInterface { // 定義四邊形類
 void doAnything() {
  // SomeSentence
 }

 public static void main(String[] args) {
  drawTest[] d = {new SquareUseInterface(),
    new ParallelogramgleUseInterface()
  };
  for(int i = 0; i < d.length; i++) {
   d[i].draw();
  }
 }
}
運行結果:
正方形.draw()
平行四邊形.draw()
2)接口與繼承
Java中不允許有多重繼承,但使用接口就可以實現多重繼承,因爲一個類可以同時實現多個接口,這樣可以將所有需要繼承的接口放置在implements關鍵字後並使用都好隔開,但這可能會在一個類中產生龐大的代碼量,因爲繼承一個接口時需要實現接口中所有的方法。
多重繼承的語法如下:
class 類名 implements 接口1, 接口2, …,接口n
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章