首頁 > 人文

學習完介面,重新回頭看一遍

由 碼一碼 發表于 人文2022-11-29

簡介}classImpl{}如果這個類實現A3介面 需要實現幾個方法classImpl2extendsA5implementsA3,A4{先寫繼承類 後寫實現介面publicvoidm1() {}publicvoidm2() {}pub

數學中實際意義是什麼意思

其他講解: 從新認識介面

講完多型之後咱們再次講講介面

interface

A1

{

public

void

m1

();

}

interface

A2

{

public

void

m2

();

}

interface

A3

extends

A1

A2

{

//介面直接可以多繼承

public

void

m3

();

}

interface

A4

{

public

void

m4

();

}

abstract

class

A5

{

public

abstract

void

m5

();

}

class

Impl

{}

//如果這個類實現A3介面 需要實現幾個方法

class

Impl2

extends

A5

implements

A3

A4

{

//先寫繼承類 後寫實現介面

public

void

m1

() {}

public

void

m2

() {}

public

void

m3

() {}

public

void

m4

() {}

public

void

m5

() {}

}

分析

說白了就是換了一個說法繞開了單繼承

如果把介面

看做

是類的話

形成了一個特殊的多繼承

那impl2這個類少說也要有這5個方法

差一個都會使這個類是抽象類,一旦這個問題出現了,那多型這事情就複雜了過去我們只有一個父類,現在如果你new

一個Impl2物件

那它能放在

A1

A2

A3

A4

A5這5個引用中,

public

static

void

main

String

[]

args

) {

Impl2

i

=

new

Impl2

();

System

out

println

i

instanceof

A1

);

System

out

println

i

instanceof

A2

);

System

out

println

i

instanceof

A3

);

System

out

println

i

instanceof

A4

);

System

out

println

i

instanceof

A5

);

//所以

A1

a1

=

i

A2

a2

=

i

A3

a3

=

i

A4

a4

=

i

A5

a5

=

i

a1

m1

();

//每個引用能使用的功能也不一樣了

a2

m2

();

//a3 由於實現了A1 A2所以 a3可以呼叫三個方法

a3

m1

();

a3

m2

();

a3

m3

();

a4

m4

();

a5

m5

();

/*

現在是5個引用共同指向了同一個物件 且每個引用型別都不一樣

從多型第二條來說 你用不同的引用能呼叫的方法是不同的

所以無論你用什麼引用呼叫到的都是 impl2中實現的方法之一

*/

}

——

究竟用介面整出來的多繼承有多複雜咱們花時間講一下,讓大家體會一下,以前我們講單繼承簡單沒有太多感受。

關於強制型別轉換我們總結的規律是

子類引用賦值給父類引用可以直接賦值,父類引用賦值給子類引用必須強制轉換

那麼不相干的兩個類能進行型別轉換嗎

class

Animal

{ }

class

Dog

extends

Animal

{ }

class

Student

{}

interface

Person

{}

class

Snoopy

extends

Dog

implements

Person

{}

//史努比

public

class

介面

{

public

static

void

main

String

[]

args

) {

Animal

a

=

new

Dog

();

Dog

d

=

Dog

a

/*

講的時候是說告訴編譯器 我偏要把a引用裡邊裝的物件 當狗看你不要妨礙我 我作出這樣的表態 編譯器就放我通過了現在不提執行期,因為在執行期 永遠都可能會出現型別轉換異常 除非用instanceof 判斷下

*/

Student

s

=

Student

a

//編譯失敗

/*

現在我偏要把a引用中的物件當人看 在編譯的時候就通不過,你非要當人看老子說不 行,那為什麼同樣是強轉我把a物件當狗看是可以編譯透過,當人看就不行了呢。咱們說這樣一個原則 在強轉的時候編譯器什麼時候是放行的呢,—— 編譯器發現你的這種堅持有可能成功。如果編譯器透過邏輯的推理發現是不可能成功它肯定不會放你過 去,然後報錯,

例子:

一個小孩18歲考上了清華大學,然後說清華我不上了,不去報道我要去創業,我要想比爾蓋茨一樣,比爾蓋茨整了一個微軟,我整一個巨硬,他是windows 我是door 門 比他的大,我就要創業 我覺得上學沒用,他爸會不會同意,有可能同意還有清華畢業找不到工作呢,你願意去創業雖然是很另類的一條路也許是能走通的,

還有一個小孩考上清華說我不去我要去踢球 我要成為一個專業的足球運動員 我的理想是帶領中國隊獲得世界冠軍,他爸能打死他你不可能成功啊,

回到程式碼

編譯器說你非要把a當狗看,是有可能成功的,也許裝的是貓你認了,但是如果你裝的是狗不就成功了嗎但是你非要把一個動物當成人看 能成功嗎 是絕對不能成功的

所以說

子類引用賦值給父類引用能直接賦值,

父類引用賦值給子類引用需要強制型別轉換

不想關

的沒有父子類關係的不能強轉

在往下看 現在有一個介面 person

*/

Person

p

=

Person

a

/*

編譯竟然通過了,還是照我們的推理為甚編譯放我們透過,是因為編譯發現這句話有 肯能是成功的,它發現你有可能成功就把你放過去了,那怎麼就有可能成功呢?這個 a是anmial 怎麼會是一個person呢,什麼情況下這個a會裝一個person物件

有沒有可能有這樣一個類既繼承了狗類又實現了person介面 假設這個類是史努比

class Snoopy extends Dog implements Student{}

萬一這個a裡邊裝的是一個史努比物件 它是狗的子類 狗又是動物的子類 所以它完 全可以裝在a裡邊,那麼它能不能強轉成人呢

可以把 因為史努比不光是狗 還實現了人介面 它同樣是可以放在person引用中

但是如果這個person 是類就不可能了不可能有一個類既繼承了狗類還繼承了類, 介面就有可能了

所有強轉的時候如果兩邊如果有一邊是介面型別 那編譯器一定會放你透過,因為 只要兩邊有介面 這個強轉就有成功的可能當然執行能不能成功就是另一回事了。

在單繼承中 你能說一個豬可能是一個人嗎 不可能 豬就是豬 人就是人

但是在多繼承中就麻煩了,你完全就可以把一個豬看成是一個人,因為這個豬有可能 是豬八戒,豬八戒就是多繼承吧 又是豬又是人

所以當下次看到Person p=(Person)a; 不要奇怪 他們倆雖然沒有關係 但是可能有一個類和他們兩個都有關係

也就是 Animal a=new Snoopy();

*/

}

}

其他講解:

再次分析介面的作用

——

介面的作用,

介面的語法只不過是在抽象類的基礎上做了一個延伸,它是一個抽象類

我寫成介面就突破了java單繼承的限制,但其實介面在我看來或者是大部分學員看來介面可能是我們學習java一個最大的障礙,介面這一關你過了,那你面向物件的思想應該就初步形成了。我們應該想方設法的想明白,介面這玩意有什麼用它的最大作用是什麼

從java的發展歷來講

java脫胎於C

++

但是又取得了超過c

++

的成就,那麼最核心的東西是什麼。一定是c

++

中沒有的而java

中有的,而介面就是重要的一個方面。

1

那介面到底有什麼用,

下面我們分析介面的多繼承,其實你要說介面是實現多繼承的,那是隻知其然不知其所以然,我們說過java是抄襲C

++

C

++

天然就支援多繼承java把它改成了單繼承,如果java的設計者認為多繼承這個事很重要為什麼不把c

++

中的多繼承抄過來,你也像C

++

一樣允許你的類之間多繼承多好你先把人家的多繼承去掉了改成了單繼承然後你又後悔了,覺得多繼承還是好,於是設計了一個介面的語法,讓人們用介面去實現多繼承,那這不吃飽了撐的嗎?

那麼相比用介面實現多繼承比在C

++

中直接實現多繼承要好,那好在哪裡,

“面向物件來自於客觀世界”

那麼在客觀世界中是單繼承多還是多繼承多啊

我可以這樣說多繼承是普遍存在的,任何一個物件拿出來它都是有好多父類的,父類是哪裡

來的

,父類是從子類的共性中抽取出來的,我把這一部分特點抽象出來形成一個父類

把那一部分特點抽取出來形成一個父類,

例如

磚頭

——->

父類

建築材料

——->

我能不能說它是一個特殊的武器啊

手機

——->

可以說是一個特殊的電話,還可以是照相機,音樂播放器,攝像機。。。。

。。

那一個杯子除了喝水還能幹什麼

,我還說這是一個圓規呢

所以當年人們在設計C

++

的時候把類設計成多繼承是有道理的,面向物件就是從生活中歸納出來的,而我們立刻就會發現,多繼承有問題類之間會形成一個網狀的結構形成一個圖,那java呢為什麼要單繼承為什麼簡單,因為類之間會形成一個簡單的樹狀結構,可

是java又怎麼實現多繼承的問題呢,

確實是一個物件它既是這個又是那個,怎麼實現多繼承呢

——

用介面

A

介面1

____

/

\

介面2

____

B

C

___

介面3

/

\___

介面4

/

\

D

E

__

介面5

透過這個圖們可以得出一個簡單結論

利用介面實現多繼承,它就不會破壞類之間樹狀關係的簡單性這棵樹是由類和類之間形成的是不包含介面的,所以我永遠認為這ABCDE形成樹的是類之間的關係,這永遠是一棵樹

能聽懂嗎

能接受嗎

這不就是在玩文字遊戲嗎

你說這個樹是類之間的介面不算,那介面是啥啊

——

特殊的抽象類

>

這個不就自己抽自己一個嘴巴嗎這個時候java的設計者給出了一個很有說服力的一個理由介面不是類,

儘管在本質上介面是一個特殊的抽象類,但是介面終歸是介面,

一個是class

一個是interface

它是不一樣的

而我這個樹狀結構是由類和類之間形成的,類之間永遠是單繼承。java的設計者認為世間萬物雖然都是多繼承的產物,但是在一個類的諸多父類當中總有一個類居於主要地位剩下的所有的其他父類都是次要的。C

++

的多繼承是不分主次的,而java把主要共性抽象成父類,次要的那些共性抽象成了介面,所有我們做出區別類是主要的介面是次要的,這應該是說把面向物件的思想向前推進了一步,c

++

的設計者只看到了生活中類關係的多重性,沒有看到`

一個手機它可以是一個MP3,照相機,攝像機,遊戲機等這個那個,但是你可聽明白了

它主要是個電話剩下的都是次要的,懂了吧。有誰是因為想拍好的照片去買個手機,它拍照功能再強,如果很看重這個不如買個正兒八經的相機好使吧,所以手機在人們眼裡,首先是一個電話,然後它

的其他什麼功能都是次要的,而主要功能用父類表示,

次要功能用介面去表示。而我說的那個樹是由主要型別之間形成的。介面不算。

例子

板磚是有人拿它當兇器

但是它再怎麼講它的主要用途還是建築材料,全世界有十萬塊板

磚,其中未必有一塊是來當兇器的,大部分還是被拿去蓋房子了吧。這有一個燒磚的工

廠,你不能認為它是一個兵工廠。明白了嗎。

所以java用介面實現多繼承比C

++

用類實現多繼承要更高明一些,一方面更符合客觀事實,把類分清主次

二呢

沒有破壞樹狀關係的簡單性

介面實現多繼承只是一種簡單的理解那介面真正的作用是什麼呢

——

介面的真正作用來自於它這樣一個特點,它裡邊所有的方法

除去靜態和預設方法

都是抽象方法

只有宣告沒有實現。意味著這個物件有什麼功能能做什麼

具體怎麼做沒有定義。所以我們理解為

***

定義介面就是定義了一個標準

我約定某一個物件它應該有什麼方法

我把這個標準制定出來

有什麼好處呢

解耦合

準起到的作用就是弱耦合,我們說面向物件四句話各司其職,弱耦合性,可重用,可擴充套件。

弱耦合

A物件呼叫B物件的方法,如果AB緊緊聯絡在一起對B的修改會導致對A的修改這就

不是弱耦合

比如修改B中方法的返回值

弱耦合指的是

A物件現在呼叫B物件的方法這個時候忽然出現一個C物件,C和B乾的是一件事而且比B做的好而A物件就有道理不用B去用C了吧,那麼在從A呼叫B改成A呼叫C的過程中

A不用修改這就是弱耦合的

弱耦合靠的就是標準,在生活中任何的弱耦合背後都有一個標準在發生作用

檯燈和燈泡

各自的廠商生產時候遵循的是同一個標準

USB標準等,

java中我定義一個介面就是定義了一個標準,那麼實現這個介面的類叫標準的實現者,而使用這個標準的類叫標準的使用者標準額出現實際上就是把標準的實現者和使用者分離做到解耦合

——

也就是把強耦合關係解開變成弱耦合關係

檯燈和燈泡案例

耦合的寫法

public

class

TestInterface

{

public

static

void

main

String

[]

args

) {

//1買一個檯燈

Lamp

l

=

new

Lamp

();

//如果現在呼叫開關方法一定會報錯,現在燈泡為空 會 得到一個空指標異常

//2 買一個燈泡

YellowLight

light

=

new

YellowLight

();

//3 安裝

l

setLight

light

);

//4開燈

l

powerOn

();

}

}

class

Lamp

{

//檯燈類

//檯燈應該有一個燈泡的屬性

YellowLight

light

=

null

//安裝燈泡

public

void

setLight

YellowLight

light

){

this

light

=

light

}

//開關

public

void

powerOn

(){

light

lightOn

();

//檯燈應該呼叫燈泡的發光方法 物件的使用者是檯燈

}

}

class

YellowLight

{

//黃燈泡

public

void

lightOn

(){

System

out

println

“黃光”

);

}

}

class

RedLight

{

//紅燈泡

public

void

dianliang

(){

//這個燈泡方法未必叫lightOn

System

out

println

“紅光”

);

}

}

/*

現在市場上又多了一個紅燈泡,紅色比較曖昧,我想給我的檯燈換一個紅燈泡怎麼換 你是不是得修改檯燈,現在臺燈類只能安裝黃燈泡 現在要按一個紅的 屬性型別 引數 發光的方法都需要去改。從實際生活出發 燈泡類是燈泡廠商寫的 比如黃的飛利浦的 紅的是松下的檯燈是檯燈廠商寫的 主函式是咱自己寫的 代表的咱自己的工作如果我買回家一個檯燈 這個檯燈安了黃的 我想換個紅的安不上 我需要把檯燈送回廠家從新改裝成安紅燈泡的檯燈 費時費力如果我還想換一個其他顏色的呢檯燈和燈泡之間不是弱耦合的 我每次燈泡的改變都會引起檯燈的修改,

*/

弱耦合寫法

利用介面

public

class

TestInterface

{

public

static

void

main

String

[]

args

) {

//1 買檯燈

Lamp

l

=

new

Lamp

();

//2 買黃燈

YellowLight

y

=

new

YellowLight

();

//3 安裝點亮

l

setLight

y

);

l

powerOn

();

//4。買紅燈

RedLight

r

=

new

RedLight

();

//5 安裝點亮

l

setLight

r

);

l

powerOn

();

//以後在有什麼其他燈泡 只要實現了light就能直接安裝在我的檯燈上

}

}

//燈泡介面

interface

Light

{

//1 首先定義一個介面 讓所有的燈泡廠商都實現這個介面

//點亮

void

on

();

}

class

YellowLight

implements

Light

{

//黃燈泡

@Override

public

void

on

() {

System

out

println

“黃光”

);

}

}

class

RedLight

implements

Light

{

//紅燈泡

@Override

public

void

on

() {

System

out

println

“紅光”

);

}

}

class

Lamp

{

//檯燈類

// 屬性型別就是燈泡 多型

private

Light

light

=

null

public

void

setLight

Light

light

) {

//多型用在引數上 只要傳遞的是燈泡的子類就行

this

light

=

light

}

public

void

powerOn

(){

light

on

();

//只要實現了介面的燈泡都有on方法

}

}

總結

介面是一種能力是一種約定

把標準的使用者和標準的實現者分離。

開始A呼叫B的方法

b

m

();

A

————->

B

b

m

();

現在透過介面I

A呼叫的是介面I中的方法

i

m

()

而介面中沒有實現找的其實是實現類B中實現的方法

A

————

I

——-

B

學習完介面,重新回頭看一遍

Tags:介面繼承燈泡檯燈一個