Java學習之介面與多型篇
Java學習之介面與多型篇
0x00 前言
在前面寫的幾篇文章裡,講了封裝和繼承,那麼這篇文章就來講講第三大面向物件的特性多型吧,在此之前先接著上篇文章的抽象先來看看Java裡面介面的應用。
0x01 介面
介面在java裡面屬於引用資料型別,是方法的集合。如果說的類的內部封裝了成員變數、構造方法和成員方法,那麼介面內部主要封裝了方法,包括抽象方法,預設方法和靜態方法,私有方法。
介面的定義和定義類差不多,定義介面需要使用到interface關鍵字。雖然說他也會被編譯為class檔案,但是他並不是一個類,而是另一種引用資料型別。
引用資料型別:陣列、介面、類
介面的使用,他和抽象類一樣不能直接被建立,但是他可以被實現使用implements關鍵字。
一個實現介面的類可以看作是介面的子類,需要實現接口裡面所有的抽象方法,建立該類的物件,就可以呼叫方法了,否則他必須是抽象的子類。
定義格式:
public interface 介面名{
}
抽象方法使用 abstract 關鍵字修飾,可以省略,沒有方法體。該方法供子類實現使用。
public interface InterFaceName {
public abstract void method();
}
接口裡的預設方法和靜態方法
預設方法:使用default進行修飾,不能省略,提供給子類或者子類重寫使用。
靜態方法:使用static修飾,提供給介面直接呼叫。
public interface MyInterface { public static void method(){ //執行語句 } public default void method2(){ //執行語句 } }
接口裡的私有方法和靜態方法
私有方法和定義私有變數一樣,使用private修飾,提供給介面的預設方法和靜態方法呼叫。
pubilc interface InterfaceName{
}
實現介面
類和介面的關係為實現關係,這被稱為類實現介面,這類可以稱為介面的實現類,基本上和抽象類有點類似,也可以稱為介面的子類。實現的動作蕾仕於繼承,格式幾乎一致,但是是使用implements關鍵字,而不是extends。
實現介面必須重寫所有的抽象方法
集成了介面的預設方法,可以直接呼叫也可以重寫
介面程式碼: public interface MyInterface { void eat(); void sleep(); } 實現類程式碼: public class Zi implements MyInterface { @Override public void eat() { System.out.println("吃"); } @Override public void sleep() { System.out.println("睡"); } } main方法程式碼: public static void main(String[] args) { Zi zi = new Zi(); zi.eat(); zi.sleep(); }
介面預設方法程式碼實現:
定義介面:
public interface MyInterface {
default void eat() {
System.out.println("吃");
}
public default void sleep(){
System.out.println("睡");
}
}
實現類程式碼:
public class Zi implements MyInterface {
}
main方法程式碼:
public static void main(String[] args) {
Zi zi = new Zi();
zi.sleep();
zi.eat();
}
這裡介面定義的第一個方法直接defalut使用這個關鍵字定義預設方法,第二個加上了public這兩種寫法都可以,前者是省略式的寫法。
實現類的任何程式碼都沒寫,只寫了一個繼承,預設方法是預設就定義好的,可以不用寫,從例項化物件直接呼叫,當然也可以在實現類裡面進行重寫。
介面靜態方法使用
靜態與.class 檔案相關,只能使用介面名呼叫,不可以通過實現類的類名或者實現類的物件呼叫。
介面定義:
public interface MyInterface {
static void eat() {
System.out.println("吃");
}
}
實現類定義:
public class Zi implements MyInterface {
}
main方法程式碼:
public static void main(String[] args) {
MyInterface.eat();
}
這裡可以看到在main方法裡面,例項化一個物件,然後去訪問eat方法是報錯的,定義為靜態的方法必須使用介面名進行訪問。
私有方法的使用
私有方法:只有預設方法可以訪問。
私有靜態方法:只有預設方法和靜態方法可以呼叫。
如果一個介面有多個預設方法並且重複,那麼就可以提取出來封裝到私有方法裡面,供預設方法去呼叫他。
public interface LiveAble {
default void func(){
func1();
func2();
} p
rivate void func1(){
System.out.println("跑起來~~~");
} p
rivate void func2(){
System.out.println("跑起來~~~");
}
}
這種寫法是jdk9的新特性,jdk8沒有,此段描述純屬複製。
0x02 介面多實現
在前面說到因為java只支援單繼承,所以一個類只能繼承一個父類,但是對於介面來說,一個類是可以實現多個介面的,這就叫做介面的多實現,並且一個類裡面可以繼承一個父類,同時也實現多個介面。
格式:
class 類名 extends 父類名 impls 介面1,介面2{
...
}
或者
class 父類名 impls 介面1,介面2{
...
}
介面多實現多抽象方法
如果在多個介面中有重名的方法,該重名的方法只需重寫一次。
介面多實現多個預設方法
介面中多個預設方法,實現類都可以繼承使用。但是如果有重名的方法就必須重寫一次。
介面1:
public interface MyInterface {
default void method1(){
System.out.println("我是一個沒有感情的預設方法");
}
}
介面2:
public interface MyInterface2 {
default void method1(){
System.out.println("我是一個沒有感情的預設方法");
}
}
實現類:
public class Zi implements MyInterface,MyInterface1 {
@Override
public void method1() {
System.out.println("沒有感情的方法");
}
}
main方法程式碼:
public static void main(String[] args) {
Zi zi = new Zi();
zi.method1();
}
而介面中的靜態方法就不會存在重名的問他,靜態方法訪問可以直接使用介面名訪問靜態方法。
優先順序問題
一個類中如果計策了一個父類又實現了多個介面時,父類中的成員方和介面中的預設方法重名,子類就近選擇執行的父類成員方法。
注意事項:
介面中,無法定義成員變數,但是可以定義常量,其值不可以改變,預設使用public static final修飾。
介面中,沒有構造方法,不能建立物件。
介面中,沒有靜態程式碼塊。
0x03 多型
多型是面向物件的第三大特性。
多型:指同一種行為,具有不同的表現形式。
多型前提:
1.繼承或者實現
2.方法的重寫
3.父類引用指向子類[格式]
格式:
父類型別 變數名 = new 子類物件;
變數名.方法名();
父類型別:指向子類物件繼承的父類型別,或者是實現的父類介面型別。
當使用多型方式呼叫方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤;如果有,執行的是子類重寫
後方法。
父類程式碼:
public abstract class Animal {
public abstract void play();
}
子類1程式碼:
public class Cat extends Animal {
@Override
public void play() {
System.out.println("擼貓");
}
}
子類2 程式碼:
public class Dog extends Animal{
@Override
public void play() {
System.out.println("擼狗");
}
}
main方法程式碼:
public static void main(String[] args) {
Animal cat = new Cat();
Animal dog = new Dog();
cat.play();
dog.play();
}
0x04 多型引用型別轉換
多型的轉型分為向上轉型喝向下轉型兩種:
我們經常用到的就是向上轉型,多型本身就是子類型別向父類型別向上轉換的過程。
當父類引用指向子類物件的時候,這就是向上轉型。
程式碼:
Antmal cat = new Cat();
向下轉型:父類型別向子類型別向下轉換的過程,這個過程是強制的。
一個已經向上轉型的子類物件,將父類引用轉為子類引用,可以使用強制型別轉換的格式,便是向下轉型。
格式:
子類型別 變數名 =(子類型別)父類變數名;
在這裡來說一下為什麼要使用轉型
當使用多型方式呼叫方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤。也就是說,不能呼叫子類擁
有,而父類沒有的方法。編譯都錯誤,更別說運行了。這也是多型給我們帶來的一點"小麻煩"。所以,想要呼叫子
類特有的方法,必須做向下轉型。
總結:如果要呼叫子類擁有而父類沒有的方法的時候就要使用到向下轉型。
父類程式碼:
public abstract class Animal {
public abstract void eat();
}
子類:
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("吃魚");
}
public void catchMouse(){
System.out.println("抓魚");
}
}
main方法程式碼:
public static void main(String[] args) {
Animal cat = new Cat();
cat.eat();
Cat c =(Cat)cat;
c.catchMouse();
}
為了避免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
}
}
}
0x05 結尾
介面與多型篇章更新完成,檢測每天一個小總結。