Java基礎 --08 接口、多態

接口
接口,是Java語言中一種引用類型,是方法的集合,如果說類的內部封裝了成員變量、構造方法和成員方法,那麼 接口的內部主要就是封裝了方法,包含抽象方法(JDK 7及以前),默認方法和靜態方法(JDK 8),私有方法 (JDK 9)。

  定義格式
  public interface 接口名稱 {
   // 抽象方法 
   / 默認方法
   // 靜態方法 
   // 私有方法 
   	}

抽象方法:使用 abstract 關鍵字修飾,可以省略,沒有方法體。該方法供子類實現使用。
默認方法:使用 default 修飾,不可省略,供子類調用或者子類重寫。
靜態方法:使用 static 修飾,供接口直接調用。
私有方法:使用 private 修飾,供接口中的默認方法或者靜態方法調用。
實現
類與接口的關係爲實現關係,即類實現接口,該類可以稱爲接口的實現類,也可以稱爲接口的子類。實現的動作類 似繼承,格式相仿,只是關鍵字不同,實現使用 implements 關鍵字。
非抽象子類實現接口

  1. 必須重寫接口中所有抽象方法。
  2. 繼承了接口的默認方法,即可以直接調用,也可以重寫。
 實現格式
 class 類名 implements 接口名 { 
   // 重寫接口中抽象方法【必須】
    // 重寫接口中默認方法【可選】
     }

抽象方法的使用
必須全部實現,如下

定義接口:
public interface LiveAble {
 // 定義抽象方法 
public abstract void eat();
public abstract void sleep();
   }
   
定義實現類
public class Animal implements LiveAble {
@Override 
public void eat() { 
System.out.println("喫東西"); 
}
@Override
 public void sleep() { 
 System.out.println("晚上睡");
      } 
 }
定義測試類
public class InterfaceDemo {
 public static void main(String[] args) {
  // 創建子類對象 
  Animal a = new Animal(); 
  // 調用實現後的方法 
  a.eat(); 
  a.sleep(); 
      } 
  }
  輸出結果: 
  喫東西 
  晚上睡 

默認方法的使用
可以繼承,可以重寫,二選一,但是隻能通過實現類的對象調用
繼承默認方法:

定義接口

public interface LiveAble {
public default void fly(){
System.out.println("天上飛"); 
	} 
}

定義實現類

public class Animal implements LiveAble {
 // 繼承,什麼都不用寫,直接調用
  }

定義測試類

public class InterfaceDemo {
 public static void main(String[] args) {
  // 創建子類對象 
  Animal a = new Animal(); 
  // 調用默認方法 
  a.fly();
  	 } 
   }
   輸出結果: 天上飛

重寫默認方法:
定義接口

public interface LiveAble { 
public default void fly(){ 
System.out.println("天上飛");
 } }

定義實現類

public class Animal implements LiveAble {
 @Override 
 public void fly() { 
 System.out.println("自由自在的飛"); 
 } }

定義測試類:

public class InterfaceDemo {
 public static void main(String[] args) {
  // 創建子類對象
   Animal a = new Animal();
    // 調用重寫方法
     a.fly();
     		 }
       }
      輸出結果: 自由自在的飛

靜態方法的使用
靜態與.class 文件相關,只能使用接口名調用,不可以通過實現類的類名或者實現類的對象調用,代碼如下:
定義接口:

public interface LiveAble {
 public static void run(){
  System.out.println("跑起來~~~");
   		}
    }

定義實現類:

public class Animal implements LiveAble { 
// 無法重寫靜態方法
 }

定義測試類:

public class InterfaceDemo { 
public static void main(String[] args) { 
// Animal.run(); // 【錯誤】無法繼承方法,也無法調用 LiveAble.run(); 
		}
 }
 輸出結果: 跑起來~~~

私有方法的使用
私有方法:只有默認方法可以調用
私有靜態方法:默認方法和靜態方法可以調用
如果一個接口中有多個默認方法,並且方法中有重複的內容,那麼可以抽取出來,封裝到私有方法中,供默認方法 去調用。從設計的角度講,私有的方法是對默認方法和靜態方法的輔助。
定義接口:

public interface LiveAble {
 default void func(){ func1(); 
 func2(); 
 }
 private void func1(){
  System.out.println("跑起來~~~"); 
  }
  private void func2(){ 
  System.out.println("跑起來~~~"); 
  	} 
  }

#接口的多實現
在繼承體系中,一個類只能繼承一個父類。而對於接口而言,一個類是可以實現多個接口的,這叫做接 口的多實現。並且,一個類能繼承一個父類,同時實現多個接口。
實現格式:

class 類名 [extends 父類名] implements 接口名1,接口名2,接口名3... { 
// 重寫接口中抽象方法【必須】 
// 重寫接口中默認方法【不重名時可選】
 }

抽象方法:
接口中,有多個抽象方法時,實現類必須重寫所有抽象方法。如果抽象方法有重名的,只需要重寫一次。代碼如 下:
定義多個接口:

interface A { 
public abstract void showA(); 
public abstract void show();
 }
 interface B { 
 public abstract void showB();
  public abstract void show();
   }

定義實現類:

public class C implements A,B{
@Override 
public void showA() { 
System.out.println("showA"); 
}
@Override
 public void showB() { 
 System.out.println("showB"); 
 }
 @Override public void show() { System.out.println("show"); 
 	} 
 }

默認方法
接口中,有多個默認方法時,實現類都可繼承使用。如果默認方法有重名的,必須重寫一次。代碼如下:
定義多個接口

interface A { 
public default void methodA(){}
 public default void method(){} 
 }
 interface B { 
 public default void methodB(){} 
 public default void method(){}
  }

定義實現類

public class C implements A,B{ 
@Override 
public void method() { 
System.out.println("method");
 	} 
 }

靜態方法
接口中,存在同名的靜態方法並不會衝突,原因是隻能通過各自接口名訪問靜態方法。
優先級的問題
當一個類,既繼承一個父類,又實現若干個接口時,父類中的成員方法與接口中的默認方法重名,子類就近選擇執行父類的成員方法。代碼如下:
定義接口:

interface A {
 public default void methodA(){ 
 System.out.println("AAAAAAAAAAAA");
     } 
  }

定義父類:

class D {
 public void methodA(){ 
 System.out.println("DDDDDDDDDDDD"); 
 } 
 }

定義子類:

class C extends D implements A { 
// 未重寫methodA方法
 }

定義測試類:

public class Test { 
public static void main(String[] args) { 
C c = new C(); 
c.methodA(); 
	}
 }
 輸出結果: DDDDDDDDDDDD

接口的多繼承
一個接口能繼承另一個或者多個接口,這和類之間的繼承比較相似。接口的繼承使用 extends 關鍵字,子接口繼 承父接口的方法。如果父接口中的默認方法有重名的,那麼子接口需要重寫一次。代碼如下:
定義父接口:

interface A { 
public default void method(){ 
System.out.println("AAAAAAAAAAAAAAAAAAA");
	 } 
 }
 interface B {
  public default void method(){
   System.out.println("BBBBBBBBBBBBBBBBBBB");
   		 }
     }

定義子接口:

interface D extends A,B{ 
@Override 
public default void method() { 
System.out.println("DDDDDDDDDDDDDD");
 	} 
 }

接口中,無法定義成員變量,但是可以定義常量,其值不可以改變,默認使用public static final修飾。
接口中,沒有構造方法,不能創建對象。
接口中,沒有靜態代碼塊。
多態
多態: 是指同一行爲,具有多個不同表現形式。
多態的格式:
父類類型 變量名 = new 子類對象;
變量名.方法名();
父類類型:指子類對象繼承的父類類型,或者實現的父接口類型。
代碼如下:

Fu f = new Zi(); 
f.method();

當使用多態方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤;如果有,執行的是子類重寫 後方法。
代碼如下:
定義父類:

`public abstract class Animal { 
  public abstract void eat();
}`

定義子類:

class Cat extends Animal { 
public void eat() {
 System.out.println("喫魚"); 
		 } 
 }
 class Dog extends Animal { 
 public void eat() { 
 System.out.println("喫骨頭"); 
 		}
  }

定義測試類:

public class Test { 
public static void main(String[] args) {
 // 多態形式,創建對象 
 Animal a1 = new Cat();
  // 調用的是 Cat 的 eat 
  a1.eat(); 
  // 多態形式,創建對象 
  Animal a2 = new Dog(); 
  // 調用的是 Dog 的 eat 
  a2.eat();
      }
    }

多態的好處
實際開發的過程中,父類類型作爲方法形式參數,傳遞子類對象給方法,進行方法的調用,更能體現出多態的擴展 性與便利。代碼如下:
定義父類:

public abstract class Animal { 
public abstract void eat();
 }

定義子類:

class Cat extends Animal {
 public void eat() { 
 System.out.println("喫魚"); 
	 } 
 }
 class Dog extends Animal { 
 public void eat() { 
 System.out.println("喫骨頭");
  		}
   }

定義測試類:

public class Test { 
public static void main(String[] args) { 
// 多態形式,創建對象 
Cat c = new Cat(); 
Dog d = new Dog(); 
// 調用showCatEat 
showCatEat(c); 
// 調用showDogEat
showDogEat(d);
 /*以上兩個方法, 均可以被showAnimalEat(Animal a)方
 法所替代 而執行效果一致 */
 showAnimalEat(c); 
 showAnimalEat(d); 
 }
 public static void showCatEat (Cat c){ 
 c.eat(); 
 }
 public static void showDogEat (Dog d){ 
 d.eat(); 
 }
 public static void showAnimalEat (Animal a){ 
 a.eat(); 
     } 
 }

由於多態特性的支持,showAnimalEat方法的Animal類型,是Cat和Dog的父類類型,父類類型接收子類對象,當 然可以把Cat對象和Dog對象,傳遞給方法。
當eat方法執行時,多態規定,執行的是子類重寫的方法,那麼效果自然與showCatEat、showDogEat方法一致, 所以showAnimalEat完全可以替代以上兩方法。
不僅僅是替代,在擴展性方面,無論之後再多的子類出現,我們都不需要編寫showXxxEat方法了,直接使用 showAnimalEat都可以完成。
所以,多態的好處,體現在,可以使程序編寫的更簡單,並有良好的擴展。
引用類型轉換
多態的轉型分爲向上轉型與向下轉型兩種
向上轉型
多態本身是子類類型向父類類型向上轉換的過程,這個過程是默認的。 當父類引用指向一個子類對象時,便是向上轉型。
使用格式:

父類類型  變量名  =  new 子類類型();
如 Animal  a =  new Cat();

向下轉型
父類類型向子類類型向下轉換的過程,這個過程是強制的。一個已經向上轉型的子類對象,將父類引用轉爲子類引用,可以使用強制類型轉換的格式,便是向下轉型。
使用格式:

子類類型 變量名 = (子類類型) 父類變量名; 
如:Cat c =(Cat) a;

當使用多態方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤。也就是說,不能調用子類擁 有,而父類沒有的方法。編譯都錯誤,更別說運行了。這也是多態給我們帶來的一點"小麻煩"。所以,想要調用子 類特有的方法,必須做向下轉型。
轉型演示,代碼如下:
定義類:

abstract class Animal {
 abstract void eat(); 
 }
 class Cat extends Animal { 
 public void eat() { 
 System.out.println("喫魚"); 
 }
 public void catchMouse() { 
 System.out.println("抓老鼠"); 
 } 
 }
 class Dog extends Animal { 
 public void eat() { 
 System.out.println("喫骨頭"); 
 }
 public void watchHouse() { 
 System.out.println("看家"); 
      }
  }

定義測試類:

public class Test { 
public static void main(String[] args) { 
// 向上轉型 
Animal a = new Cat(); 
a.eat(); // 調用的是 Cat 的 eat 
// 向下轉型 
Cat c = (Cat)a; 
c.catchMouse(); // 調用的是 Cat 的 catchMouse
      } 
 }

轉型的異常
轉型的過程中,一不小心就會遇到這樣的問題,請看如下代碼:

public class Test { 
public static void main(String[] args) {
 // 向上轉型 
 Animal a = new Cat(); 
 a.eat(); // 調用的是 Cat 的 eat 
 // 向下轉型 
 Dog d = (Dog)a; 
 d.watchHouse(); // 調用的是 Dog 的 watchHouse 【運行報錯】 
     } 
 }

這段代碼可以通過編譯,但是運行時,卻報出了 ClassCastException ,類型轉換異常!這是因爲,明明創建了 Cat類型對象,運行時,當然不能轉換成Dog對象的。這兩個類型並沒有任何繼承關係,不符合類型轉換的定義。 爲了避免ClassCastException的發生,Java提供了 instanceof 關鍵字,給引用變量做類型的校驗,格式如下:

變量名 instanceof 數據類型 
如果變量屬於該數據類型,返回true。
如果變量不屬於該數據類型,返回false。

所以,轉換前,我們最好先做一個判斷,代碼如下:

public class Test { 
public static void main(String[] args) { 
// 向上轉型 
Animal a = new Cat(); 
a.eat(); // 調用的是 Cat 的 eat 
// 向下轉型 
if (a instanceof Cat){ 
Cat c = (Cat)a; 
c.catchMouse(); // 調用的是 Cat 的 catchMouse
 } else if (a instanceof Dog){ 
 Dog d = (Dog)a; 
 d.watchHouse(); // 調用的是 Dog 的 watchHouse
 			 } 
  		}
   }

接口多態的綜合案例
筆記本電腦
筆記本電腦(laptop)通常具備使用USB設備的功能。在生產時,筆記本都預留了可以插入USB設備的USB接口, 但具體是什麼USB設備,筆記本廠商並不關心,只要符合USB規格的設備都可以。 定義USB接口,具備最基本的開啓功能和關閉功能。鼠標和鍵盤要想能在電腦上使用,那麼鼠標和鍵盤也必須遵守 USB規範,實現USB接口,否則鼠標和鍵盤的生產出來也無法使用
案例分析
進行描述筆記本類,實現筆記本使用USB鼠
標、USB鍵盤
USB接口,包含開啓功能、關閉功能
筆記本類,包含運行功能、關機功能、使用USB設備功能
鼠標類,要實現USB接口,並具備點擊的方法
鍵盤類,要實現USB接口,具備敲擊的方法
案例實現
定義USB接口:

interface USB { 
void open();// 開啓功能 
void close();// 關閉功能
 }

定義鼠標類:

class Mouse implements USB { 
public void open() { 
System.out.println("鼠標開啓,紅燈閃一閃");
 }
 public void close() { 
 System.out.println("鼠標關閉,紅燈熄滅"); 
 }
 public void click(){ 
 System.out.println("鼠標單擊");
  }
   }

定義鍵盤類:

class KeyBoard implements USB { 
public void open() { 
System.out.println("鍵盤開啓,綠燈閃一閃");
 }
 public void close() {
  System.out.println("鍵盤關閉,綠燈熄滅");
   }
   public void type(){
    System.out.println("鍵盤打字");
     }
      }

定義筆記本類:

class Laptop { 
// 筆記本開啓運行功能 
public void run() { 
System.out.println("筆記本運行"); 
}
// 筆記本使用usb設備,這時當筆記本對象調用這個功能時,必須給其傳遞一個符合USB規則的USB設備 
public void useUSB(USB usb) { 
// 判斷是否有USB設備 
if (usb != null) { 
usb.open(); 
// 類型轉換,調用特有方法 
if(usb instanceof Mouse){ 
Mouse m = (Mouse)usb; 
m.click(); 
}else if (usb instanceof KeyBoard){ 
KeyBoard kb = (KeyBoard)usb; 
kb.type(); 
}
usb.close(); 
	} 
 }
public void shutDown() { 
System.out.println("筆記本關閉"); 
	} 
}

測試類:

public class Test { 
public static void main(String[] args) { 
// 創建筆記本實體對象 
Laptop lt = new Laptop(); 
// 筆記本開啓 
lt.run(); 
// 創建鼠標實體對象
Usb u = new Mouse(); 
// 筆記本使用鼠標 
lt.useUSB(u); 
// 創建鍵盤實體對象 
KeyBoard kb = new KeyBoard(); 
// 筆記本使用鍵盤 
lt.useUSB(kb); 
// 筆記本關閉 
lt.shutDown(); 
	}
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章