1. 程式人生 > >Java面向對象(接口、多態)

Java面向對象(接口、多態)

聲明 講課 概述 條件 默認修飾符 implement 復用性 接口實現類 2.3

面向對象

今日內容介紹

u 接口

u 多態

第1章 接口

1.1 接口概念

接口是功能的集合,同樣可看做是一種數據類型,是比抽象類更為抽象的”類”。

接口只描述所應該具備的方法,並沒有具體實現,具體的實現由接口的實現類(相當於接口的子類)來完成。這樣將功能的定義與實現分離,優化了程序設計。

請記住:一切事物均有功能,即一切事物均有接口。

1.2 接口的定義

與定義類的class不同,接口定義時需要使用interface關鍵字。

定義接口所在的仍為.java文件,雖然聲明時使用的為interface關鍵字的編譯後仍然會產生.class文件。這點可以讓我們將接口看做是一種只包含了功能聲明的特殊類。

定義格式:

public interface 接口名 {

抽象方法1;

抽象方法2;

抽象方法3;

}

使用interface代替了原來的class,其他步驟與定義類相同:

l 接口中的方法均為公共訪問的抽象方法

l 接口中無法定義普通的成員變量

1.3 類實現接口

類與接口的關系為實現關系,即類實現接口。實現的動作類似繼承,只是關鍵字不同,實現使用implements。

其他類(實現類)實現接口後,就相當於聲明:”我應該具備這個接口中的功能”。實現類仍然需要重寫方法以實現具體的功能。

格式:

class implements 接口 {

重寫接口中方法

}

在類實現接口後,該類就會將接口中的抽象方法繼承過來,此時該類需要重寫該抽象方法,完成具體的邏輯。

l 接口中定義功能,當需要具有該功能時,可以讓類實現該接口,只聲明了應該具備該方法,是功能的聲明。

l 在具體實現類中重寫方法,實現功能,是方法的具體實現。

於是,通過以上兩個動作將功能的聲明與實現便分開了。(此時請重新思考:類是現實事物的描述,接口是功能的集合。)

1.4 接口中成員的特點

1、接口中可以定義變量,但是變量必須有固定的修飾符修飾,public static final 所以接口中的變量也稱之為常量,其值不能改變。後面我們會講解staticfinal關鍵字

2、接口中可以定義方法,方法也有固定的修飾符,public abstract

3、接口不可以創建對象。

4、子類必須覆蓋掉接口中所有的抽象方法後,子類才可以實例化。否則子類是一個抽象類。

interface Demo { ///定義一個名稱為Demo的接口。

public static final int NUM = 3;// NUM的值不能改變

public abstract void show1();

public abstract void show2();

}

//定義子類去覆蓋接口中的方法。類與接口之間的關系是 實現。通過 關鍵字 implements

class DemoImpl implements Demo { //子類實現Demo接口。

//重寫接口中的方法。

public void show1(){}

public void show2(){}

}

1.5 接口的多實現

了解了接口的特點後,那麽想想為什麽要定義接口,使用抽象類描述也沒有問題,接口到底有啥用呢?

接口最重要的體現:解決多繼承的弊端。將多繼承這種機制在java中通過多實現完成了。

interface Fu1

{

void show1();

}

interface Fu2

{

void show2();

}

class Zi implements Fu1,Fu2// 多實現。同時實現多個接口。

{

public void show1(){}

public void show2(){}

}

怎麽解決多繼承的弊端呢?

弊端:多繼承時,當多個父類中有相同功能時,子類調用會產生不確定性。

其實核心原因就是在於多繼承父類中功能有主體,而導致調用運行時,不確定運行哪個主體內容。

為什麽多實現能解決了呢?

因為接口中的功能都沒有方法體,由子類來明確。

1.6 類繼承類同時實現接口

接口和類之間可以通過實現產生關系,同時也學習了類與類之間可以通過繼承產生關系。當一個類已經繼承了一個父類,它又需要擴展額外的功能,這時接口就派上用場了。

子類通過繼承父類擴展功能,通過繼承擴展的功能都是子類應該具備的基礎功能。如果子類想要繼續擴展其他類中的功能呢?這時通過實現接口來完成。

class Fu {

public void show(){}

}

interface Inter {

pulbic abstract void show1();

}

class Zi extends Fu implements Inter {

public void show1() {

}

}

接口的出現避免了單繼承的局限性。父類中定義的事物的基本功能。接口中定義的事物的擴展功能。

1.7 接口的多繼承

學習類的時候,知道類與類之間可以通過繼承產生關系,接口和類之間可以通過實現產生關系,那麽接口與接口之間會有什麽關系。

多個接口之間可以使用extends進行繼承。

interface Fu1{

void show();

}

interface Fu2{

void show1();

}

interface Fu3{

void show2();

}

interface Zi extends Fu1,Fu2,Fu3{

void show3();

}

在開發中如果多個接口中存在相同方法,這時若有個類實現了這些接口,那麽就要實現接口中的方法,由於接口中的方法是抽象方法,子類實現後也不會發生調用的不確定性。

1.8 接口的思想

前面學習了接口的代碼體現,現在來學習接口的思想,接下裏從生活中的例子進行說明。

舉例:我們都知道電腦上留有很多個插口,而這些插口可以插入相應的設備,這些設備為什麽能插在上面呢?主要原因是這些設備在生產的時候符合了這個插口的使用規則,否則將無法插入接口中,更無法使用。發現這個插口的出現讓我們使用更多的設備。

總結:接口在開發中的它好處

1、接口的出現擴展了功能。

2、接口其實就是暴漏出來的規則。

3、接口的出現降低了耦合性,即設備與設備之間實現了解耦。

接口的出現方便後期使用和維護,一方是在使用接口(如電腦),一方在實現接口(插在插口上的設備)。例如:筆記本使用這個規則(接口),電腦外圍設備實現這個規則(接口)。

1.9 接口和抽象的區別

明白了接口思想和接口的用法後,接口和抽象類的區別是什麽呢?接口在生活體現也基本掌握,那在程序中接口是如何體現的呢?

通過實例進行分析和代碼演示抽象類和接口的用法。

1、舉例:

犬:

行為:

吼叫;

吃飯;

緝毒犬:

行為:

吼叫;

吃飯;

緝毒;

2、思考:

由於犬分為很多種類,他們吼叫和吃飯的方式不一樣,在描述的時候不能具體化,也就是吼叫和吃飯的行為不能明確。當描述行為時,行為的具體動作不能明確,這時,可以將這個行為寫為抽象行為,那麽這個類也就是抽象類。

可是當緝毒犬有其他額外功能時,而這個功能並不在這個事物的體系中。這時可以讓緝毒犬具備犬科自身特點的同時也有其他額外功能,可以將這個額外功能定義接口中。

如下代碼演示:

interface 緝毒{

public abstract void 緝毒();

}

//定義犬科的這個提醒的共性功能

abstract class 犬科{

public abstract void 吃飯();

public abstract void 吼叫();

}

// 緝毒犬屬於犬科一種,讓其繼承犬科,獲取的犬科的特性,

//由於緝毒犬具有緝毒功能,那麽它只要實現緝毒接口即可,這樣即保證緝毒犬具備犬科的特性,也擁有了緝毒的功能

class 緝毒犬 extends 犬科 implements 緝毒{

public void 緝毒() {

}

void 吃飯() {

}

void 吼叫() {

}

}

class 緝毒豬 implements 緝毒{

public void 緝毒() {

}

}

3、通過上面的例子總結接口和抽象類的區別:

相同點:

l 都位於繼承的頂端,用於被其他類實現或繼承;

l 都不能直接實例化對象;

l 都包含抽象方法,其子類都必須覆寫這些抽象方法;

區別:

l 抽象類為部分方法提供實現,避免子類重復實現這些方法,提高代碼重用性;接口只能包含抽象方法;

l 一個類只能繼承一個直接父類(可能是抽象類),卻可以實現多個接口;(接口彌補了Java的單繼承)

l 抽象類是這個事物中應該具備的你內容, 繼承體系是一種 is..a關系

l 接口是這個事物中的額外內容,繼承體系是一種 like..a關系

二者的選用:

l 優先選用接口,盡量少用抽象類;

l 需要定義子類的行為,又要為子類提供共性功能時才選用抽象類;

第2章 多態

2.1 多態概述

多態是繼封裝、繼承之後,面向對象的第三大特性。

現實事物經常會體現出多種形態,如學生,學生是人的一種,則一個具體的同學張三既是學生也是人,即出現兩種形態。

Java作為面向對象的語言,同樣可以描述一個事物的多種形態。如Student類繼承了Person類,一個Student的對象便既是Student,又是Person

Java中多態的代碼體現在一個子類對象(實現類對象)既可以給這個子類(實現類對象)引用變量賦值,又可以給這個子類(實現類對象)的父類(接口)變量賦值。

Student類可以為Person類的子類。那麽一個Student對象既可以賦值給一個Student類型的引用,也可以賦值給一個Person類型的引用。

最終多態體現為父類引用變量可以指向子類對象。

多態的前提是必須有子父類關系或者類實現接口關系,否則無法完成多態。

在使用多態後的父類引用變量調用方法時,會調用子類重寫後的方法。

2.2 多態的定義與使用格式

多態的定義格式:就是父類的引用變量指向子類對象

父類類型 變量名 = new 子類類型();

變量名.方法名();

l 普通類多態定義的格式

父類 變量名 = new 子類();

如: class Fu {}

class Zi extends Fu {}

//類的多態使用

Fu f = new Zi();

l 抽象類多態定義的格式

抽象類 變量名 = new 抽象類子類();

如: abstract class Fu {

public abstract void method();

}

class Zi extends Fu {

public void method(){

System.out.println(“重寫父類抽象方法”);

}

}

//類的多態使用

Fu fu= new Zi();

l 接口多態定義的格式

接口 變量名 = new 接口實現類();

如: interface Fu {

public abstract void method();

}

class Zi implements Fu {

public void method(){

System.out.println(“重寫接口抽象方法);

}

}

//接口的多態使用

Fu fu = new Zi();

l 註意事項

同一個父類的方法會被不同的子類重寫。在調用方法時,調用的為各個子類重寫後的方法。

Person p1 = new Student();

Person p2 = new Teacher();

p1.work(); //p1會調用Student類中重寫的work方法

p2.work(); //p2會調用Teacher類中重寫的work方法

當變量名指向不同的子類對象時,由於每個子類重寫父類方法的內容不同,所以會調用不同的方法。

2.3 多態-成員的特點

掌握了多態的基本使用後,那麽多態出現後類的成員有啥變化呢?前面學習繼承時,我們知道子父類之間成員變量有了自己的特定變化,那麽當多態出現後,成員變量在使用上有沒有變化呢?

多態出現後會導致子父類中的成員變量有微弱的變化。看如下代碼

class Fu {

int num = 4;

}

class Zi extends Fu {

int num = 5;

}

class Demo {

public static void main(String[] args) {

Fu f = new Zi();

System.out.println(f.num);

Zi z = new Zi();

System.out.println(z.num);

}

}

l 多態成員變量

當子父類中出現同名的成員變量時,多態調用該變量時:

編譯時期:參考的是引用型變量所屬的類中是否有被調用的成員變量。沒有,編譯失敗。

運行時期:也是調用引用型變量所屬的類中的成員變量。

簡單記:編譯和運行都參考等號的左邊。編譯運行看左邊。

多態出現後會導致子父類中的成員方法有微弱的變化。看如下代碼

class Fu {

int num = 4;

void show() {

System.out.println("Fu show num");

}

}

class Zi extends Fu {

int num = 5;

void show() {

System.out.println("Zi show num");

}

}

class Demo {

public static void main(String[] args) {

Fu f = new Zi();

f.show();

}

}

l 多態成員方法

編譯時期:參考引用變量所屬的類,如果沒有類中沒有調用的方法,編譯失敗。

運行時期:參考引用變量所指的對象所屬的類,並運行對象所屬類中的成員方法。

簡而言之:編譯看左邊,運行看右邊。

2.4 instanceof關鍵字

我們可以通過instanceof關鍵字來判斷某個對象是否屬於某種數據類型。如學生的對象屬於學生類,學生的對象也屬於人類。

使用格式:

boolean b = 對象 instanceof 數據類型;

Person p1 = new Student(); // 前提條件,學生類已經繼承了人類

boolean flag = p1 instanceof Student; //flag結果為true

boolean flag2 = p2 instanceof Teacher; //flag結果為false

2.5 多態-轉型

多態的轉型分為向上轉型與向下轉型兩種:

l 向上轉型:當有子類對象賦值給一個父類引用時,便是向上轉型,多態本身就是向上轉型的過程。

使用格式:

父類類型 變量名 = new 子類類型();

如:Person p = new Student();

l 向下轉型:一個已經向上轉型的子類對象可以使用強制類型轉換的格式,將父類引用轉為子類引用,這個過程是向下轉型。如果是直接創建父類對象,是無法向下轉型的!

使用格式:

子類類型 變量名 = (子類類型) 父類類型的變量;

:Student stu = (Student) p; //變量p 實際上指向Student對象

2.6 多態的好處與弊端

當父類的引用指向子類對象時,就發生了向上轉型,即把子類類型對象轉成了父類類型。向上轉型的好處是隱藏了子類類型,提高了代碼的擴展性。

但向上轉型也有弊端,只能使用父類共性的內容,而無法使用子類特有功能,功能有限制。看如下代碼

//描述動物類,並抽取共性eat方法

abstract class Animal {

abstract void eat();

}

// 描述狗類繼承動物類重寫eat方法增加lookHome方法

class Dog extends Animal {

void eat() {

System.out.println("啃骨頭");

}

void lookHome() {

System.out.println("看家");

}

}

// 描述貓類繼承動物類重寫eat方法增加catchMouse方法

class Cat extends Animal {

void eat() {

System.out.println("吃魚");

}

void catchMouse() {

System.out.println("抓老鼠");

}

}

public class Test {

public static void main(String[] args) {

Animal a = new Dog(); //多態形式創建一個狗對象

a.eat(); // 調用對象中的方法,會執行狗類中的eat方法

// a.lookHome();//使用Dog類特有的方法,需要向下轉型不能直接使用

// 為了使用狗類的lookHome方法,需要向下轉型

// 向下轉型過程中,可能會發生類型轉換的錯誤ClassCastException異常

// 那麽在轉之前需要做健壯性判斷

if( !a instanceof Dog){ // 判斷當前對象是否是Dog類型

System.out.println("類型不匹配,不能轉換");

return;

}

Dog d = (Dog) a; //向下轉型

d.lookHome();//調用狗類的lookHome方法

}

}

我們來總結一下:

l 什麽時候使用向上轉型:

當不需要面對子類類型時,通過提高擴展性,或者使用父類的功能就能完成相應的操作,這時就可以使用向上轉型。

如:Animal a = new Dog();

a.eat();

l 什麽時候使用向下轉型

當要使用子類特有功能時,就需要使用向下轉型。

如:Dog d = (Dog) a; //向下轉型

d.lookHome();//調用狗類的lookHome方法

l 向下轉型的好處:可以使用子類特有功能。

l 弊端是:需要面對具體的子類對象;在向下轉型時容易發生ClassCastException類型轉換異常。在轉換之前必須做類型判斷。

如:if( !a instanceof Dog){…}

2.7 多態-舉例

我們明確多態使用,以及多態的細節問題後,接下來練習下多態的應用。

l 畢老師和畢姥爺的故事

/*

描述畢老師和畢姥爺,

畢老師擁有講課和看電影功能

畢姥爺擁有講課和釣魚功能

*/

class 畢姥爺 {

void 講課() {

System.out.println("政治");

}

void 釣魚() {

System.out.println("釣魚");

}

}

// 畢老師繼承了畢姥爺,就有擁有了畢姥爺的講課和釣魚的功能,

// 但畢老師和畢姥爺的講課內容不一樣,因此畢老師要覆蓋畢姥爺的講課功能

class 畢老師 extends 畢姥爺 {

void 講課() {

System.out.println("Java");

}

void 看電影() {

System.out.println("看電影");

}

}

public class Test {

public static void main(String[] args) {

// 多態形式

畢姥爺 a = new 畢老師(); // 向上轉型

a.講課(); // 這裏表象是畢姥爺,其實真正講課的仍然是畢老師,因此調用的也是畢老師的講課功能

a.釣魚(); // 這裏表象是畢姥爺,但對象其實是畢老師,而畢老師繼承了畢姥爺,即畢老師也具釣魚功能

// 當要調用畢老師特有的看電影功能時,就必須進行類型轉換

畢老師 b = (畢老師) a; // 向下轉型

b.看電影();

}

}

學習到這裏,面向對象的三大特征學習完了。

總結下封裝、繼承、多態的作用:

l 封裝:把對象的屬性與方法的實現細節隱藏,僅對外提供一些公共的訪問方式

l 繼承:子類會自動擁有父類所有可繼承的屬性和方法。

l 多態:配合繼承與方法重寫提高了代碼的復用性與擴展性;如果沒有方法重寫,則多態同樣沒有意義。

第3章 筆記本電腦案例

3.1 案例介紹

定義USB接口(具備開啟功能、關閉功能),筆記本要使用USB設備,即筆記本在生產時需要預留可以插入USB設備的USB接口,即就是筆記本具備使用USB設備的功能,但具體是什麽USB設備,筆記本並不關心,只要符合USB規格的設備都可以。鼠標和鍵盤要想能在電腦上使用,那麽鼠標和鍵盤也必須遵守USB規範,不然鼠標和鍵盤的生產出來無法使用

進行描述筆記本類,實現筆記本使用USB鼠標、USB鍵盤

l USB接口,包含開啟功能、關閉功能

l 筆記本類,包含運行功能、關機功能、使用USB設備功能

l 鼠標類,要符合USB接口

l 鍵盤類,要符合USB接口

3.2 案例需求分析

階段一:

使用筆記本,筆記本有運行功能,需要筆記本對象來運行這個功能

階段二:

想使用一個鼠標,又有一個功能使用鼠標,並多了一個鼠標對象。

階段三:

還想使用一個鍵盤 ,又要多一個功能和一個對象

問題:每多一個功能就需要在筆記本對象中定義一個方法,不爽,程序擴展性極差。

降低鼠標、鍵盤等外圍設備和筆記本電腦的耦合性。

3.3 實現代碼步驟

l 定義鼠標、鍵盤,筆記本三者之間應該遵守的規則

interface USB {

void open();// 開啟功能

void close();// 關閉功能

}

l 鼠標實現USB規則

class Mouse implements USB {

public void open() {

System.out.println("鼠標開啟");

}

public void close() {

System.out.println("鼠標關閉");

}

}

l 鍵盤實現USB規則

class KeyBoard implements USB {

public void open() {

System.out.println("鍵盤開啟");

}

public void close() {

System.out.println("鍵盤關閉");

}

}

l 定義筆記本

class NoteBook {

// 筆記本開啟運行功能

public void run() {

System.out.println("筆記本運行");

}

// 筆記本使用usb設備,這時當筆記本對象調用這個功能時,必須給其傳遞一個符合USB規則的USB設備

public void useUSB(USB usb) {

// 判斷是否有USB設備

if (usb != null) {

usb.open();

usb.close();

}

}

public void shutDown() {

System.out.println("筆記本關閉");

}

}

public class Test {

public static void main(String[] args) {

// 創建筆記本實體對象

NoteBook nb = new NoteBook();

// 筆記本開啟

nb.run();

// 創建鼠標實體對象

Mouse m = new Mouse();

// 筆記本使用鼠標

nb.useUSB(m);

// 創建鍵盤實體對象

KeyBoard kb = new KeyBoard();

// 筆記本使用鍵盤

nb.useUSB(kb);

// 筆記本關閉

nb.shutDown();

}

}

第4章 總結

4.1 知識點總結

l 接口:理解為是一個特殊的抽象類,但它不是類,是一個接口

l 接口的特點:

1,定義一個接口用interface關鍵字

interface Inter{}

2,一個類實現一個接口,實現implements關鍵字

class Demo implements Inter{}

3, 接口不能直接創建對象

通過多態的方式,由子類來創建對象,接口多態

l 接口中的成員特點:

成員變量:

只能是final 修飾的常量

默認修飾符: public static final

構造方法:

成員方法:

只能是抽象方法

默認修飾符: public abstract

l 類與類,類與接口,接口與接口之間的關系

類與類之間:繼承關系,單繼承,可以是多層繼承

類與接口之間: 實現關系,單實現,也可以多實現

接口與接口之間:繼承關系,單繼承,也可以是多繼承

Java中的類可以繼承一個父類的同時,實現多個接口

l 多態:理解為同一種物質的多種形態

l 多態使用的前提:

1,有繼承或者實現關系

2,要方法重寫

3,父類引用指向子類對象

l 多態的成員訪問特點:

方法的運行看右邊,其他都看左邊

l 多態的好處:

提高了程序的擴展性

l 多態的弊端:

不能訪問子類的特有功能

l 多態的分類

l 類的多態

abstract class Fu {

public abstract void method();

}

class Zi extends Fu {

public void method(){

System.out.println(“重寫父類抽象方法”);

}

}

//類的多態使用

Fu fu= new Zi();

l 接口的多態

interface Fu {

public abstract void method();

}

class Zi implements Fu {

public void method(){

System.out.println(“重寫接口抽象方法);

}

}

//接口的多態使用

Fu fu = new Zi();

l instanceof 關鍵字

格式: 對象名 instanceof 類名

返回值: true, false

作用: 判斷指定的對象 是否為 給定類創建的對象

Java面向對象(接口、多態)