抽象類和介面的詳解(例項)
抽象類和介面在我們的程式碼中,生活中息息相關,與上圖所示,操縱複雜,密切相關,那麼問題來了,何謂抽象類,何謂介面?
帶著這層薄紗,慢慢揭開這層薄紗;也許在古代,新婚之夜,透過這層薄紗,你看到的或者是驚喜,或許是驚悚,不要怕,無論是驚悚還是驚喜,她都會伴你一生。
曾幾何時?你還會在面試當中與面試官對答如流的解釋抽象類和介面嗎?
面試官:解釋一下抽象類和介面的區別?
me:
1、抽象類和介面都不能直接例項化,如果要例項化,抽象類變數必須指向實現所有抽象方法的子類物件,介面變數必須指向實現所有介面方法的類物件。
2、抽象類要被子類繼承,介面要被類實現。
3、介面只能做方法申明,抽象類中可以做方法申明,也可以做方法實現
4、接口裡定義的變數只能是公共的靜態的常量,抽象類中的變數是普通變數。
5、抽象類裡的抽象方法必須全部被子類所實現,如果子類不能全部實現父類抽象方法,那麼該子類只能是抽象類。同樣,一個實現介面的時候,如不能全部實現介面方法,那麼該類也只能為抽象類。
6、抽象方法只能申明,不能實現,介面是設計的結果 ,抽象類是重構的結果
7、抽象類裡可以沒有抽象方法
8、如果一個類裡有抽象方法,那麼這個類只能是抽象類
9、抽象方法要被實現,所以不能是靜態的,也不能是私有的。
10、介面可繼承介面,並可多繼承介面,但類只能單根繼承。
好吧,對答如流,可能是背會了,而且很熟,我們不僅僅是為了會背,如果過一段時間不忘記,能夠根深蒂固,那只有融入到我們的思維中,形成我們自己的語言,不僅僅是文字的闡述,更是以圖的形式存在,記憶在右腦當中,那麼下面讓我們探討一下,在現實與程式碼中他們的作用。
我看網上有個例子很好,特此舉例(介面卡的概念):
沒有使用介面卡的時候,實現介面的類要實現介面的所有方法,即便只是對其中一個方法進行了實現
![]()
建立一個抽象類實現介面(介面卡概念),再用抽象類的子類重寫想要實現的方法
tips:對於介面,你需要繼承接口裡邊所有的實現,而建立一個抽象類實現介面,那麼可以用一個普通類實現這個抽象類就可以實現你需要的方法,方便了許多。
下面就是一個抽象類和介面引發的血案:
涉及到
1)掌握抽象類和介面的例項化操作。
2)掌握模板設計的作用。
3)掌握工廠設計模式的作用。
4)掌握代理設計模式的作用。
5)掌握介面卡模式的作用。
6)掌握抽象類與介面的使用區別。
具體內容呢?
1、 在java中,可以通過物件的多型性,為抽象類和介面例項化,這樣再使用抽象類和介面的時候就可以呼叫本子類中所覆寫過的方法。
之所以抽象類和介面不能直接例項化,是因為其內部包含了抽象方法,抽象方法本身是未實現的方法,所以無法呼叫。
通過物件多型性可以發現,子類發生了向上轉型關係之後,所呼叫的全部方法,都是被覆寫過的方法。如下:
abstract class A{ // 定義抽象類A
public abstract void print() ; // 定義抽象方法print()
};
class B extends A { // 定義子類,繼承抽象類
public void print(){ // 覆寫抽象方法
System.out.println("Hello World!") ;
}
};
public class AbstractCaseDemo01{
public static void main(String args[]){
A a = new B() ; // 通過子類為抽象類例項化,向上轉型。
a.print() ;
}
};
執行結果:
Hello World!
可以繼續利用此概念,為介面例項化。
package com.zhijin;
interface A{ // 定義抽象類A
public abstract void print() ; // 定義抽象方法print()
};
class B implements A { // 定義子類,繼承抽象類
public void print(){ // 覆寫抽象方法
System.out.println("Hello World!!!") ;
}
};
public class ThisDemo06{
public static void main(String args[]){
A a = new B() ; // 通過子類為抽象類例項化
a.print() ;
}
};
所以,如果想要使用抽象類和介面,則只能按照以上操作完成。
2、抽象類的實際使用場景–設計模式(模板設計)
super : 動物
子類:貓、狗
abstract class Animal{
private String name ; // 定義name屬性
private int age ; // 定義age屬性
public Animal(String name,int age){
this.name = name ;
this.age = age ;
}
public String getName(){
return this.name ;
}
public int getAge(){
return this.age ;
}
public void say(){ // 人說話是一個具體的功能
System.out.println(this.getContent()) ; // 輸出內容
}
public abstract String getContent() ; // 說話的內容由子類決定
};
class Cat extends Animal{
private String call;
public Cat(String name,int age,String call){
super(name,age) ; // 呼叫父類中的構造方法
this.call= call ;
}
public String getContent(){
return " 姓名:" + super.getName() +
";年齡:" + super.getAge() +
";叫聲:" + this.call;
}
};
class Dog extends Animal{
private String call ;
public Dog (String name,int age,String call){
super(name,age) ; // 呼叫父類中的構造方法
this.call= call;
}
public String getContent(){
return "姓名:" + super.getName() +
";年齡:" + super.getAge() +
";叫聲:" + this.call ;
}
};
public class AbstractCaseDemo02{
public static void main(String args[]){
Animal per1 = null ; // 宣告Animal 物件
Animal per2 = null ; // 宣告Animal 物件
per1 = new Cat("貓兒",2,"喵兒") ;
per2 = new Dog("小狗",10,"wangwang") ;
per1.call() ;
per2.call() ;
}
};
執行結果:
姓名:張三;年齡:2;喵兒
姓名:李四;年齡:10;wangwang
tips:抽象類就相當於一個模板,現實的模板:
3、介面的實際應用–制定標準
比如說“U盤和印表機都可以插在電腦上使用,因為他們都實現了USB標準的介面”
interface USB{ // 定義了USB介面
public void start() ; // USB裝置開始工作
public void stop() ; // USB裝置結束工作
}
class Computer{
public static void plugin(USB usb){ // 電腦上可以插入USB裝置,向上轉型,這裡就相當於一個介面,只有符合這個介面的標準的類的物件(即只有繼承這個介面的類的物件),才能被這個方法呼叫。
usb.start() ;
System.out.println("=========== USB 裝置工作 ========") ;
usb.stop() ;
}
};
class Flash implements USB{
public void start(){ // 覆寫方法
System.out.println("U盤開始工作。") ;
}
public void stop(){ // 覆寫方法
System.out.println("U盤停止工作。") ;
}
};
class Print implements USB{
public void start(){ // 覆寫方法
System.out.println("印表機開始工作。") ;
}
public void stop(){ // 覆寫方法
System.out.println("印表機停止工作。") ;
}
};
public class InterfaceCaseDemo02{
public static void main(String args[]){
Computer.plugin(new Flash()) ;
Computer.plugin(new Print()) ;
}
};
執行結果:
U盤開始工作。
=========== USB 裝置工作 ========
U盤停止工作。
印表機開始工作。
=========== USB 裝置工作 ========
印表機停止工作。
4、工廠設計模式–介面應用
interface Fruit{ // 定義一個水果介面
public void eat() ; // 吃水果
}
class Apple implements Fruit{
public void eat(){
System.out.println("** 吃蘋果。") ;
}
};
class Orange implements Fruit{
public void eat(){
System.out.println("** 吃橘子。") ;
}
};
public class InterfaceCaseDemo03{
public static void main(String args[]){
Fruit f = new Apple() ; // 例項化介面
f.eat() ;
}
};
執行結果:
吃蘋果。
看起來上邊的程式碼沒有什麼大問題,但是擴充套件性呢?
JVM工作原理:程式-》JVM(相當於客戶端)-》作業系統。
問題的解決:客戶端通過過渡端,得到特定子類的介面例項,返回介面例項給客戶端,介面例項呼叫介面中的方法。
此時就涉及到了工廠模式
interface Fruit{ // 定義一個水果介面
public void eat() ; // 吃水果
}
class Apple implements Fruit{
public void eat(){
System.out.println("** 吃蘋果。") ;
}
};
class Orange implements Fruit{
public void eat(){
System.out.println("** 吃橘子。") ;
}
};
class Factory{ // 定義工廠類
public static Fruit getInstance(String className){ //注意這裡的方法是static修飾的,因為在主方法中是Factory呼叫
Fruit f = null ;
if("apple".equals(className)){ // 判斷是否要的是蘋果的子類
f = new Apple() ;
}
if("orange".equals(className)){ // 判斷是否要的是橘子的子類
f = new Orange() ;
}
return f ;
}
};
public class InterfaceCaseDemo04{
public static void main(String args[]){
Fruit f = Factory.getInstance(”apple“) ; // 例項化介面
f.eat() ;
}
};
執行結果:
**吃蘋果
5、代理模式–介面應用
這裡說的代理模式有很多種,討債公司代理要討債的人,江歌案中(日本律師代理江母),下面我們講一下我們經常使用的代理伺服器;
interface Network{
public void browse() ; // 瀏覽
}
class Real implements Network{ //真實上網操作類
public void browse(){
System.out.println("上網瀏覽資訊") ;
}
};
class Proxy implements Network{ //代理類。
private Network network ; // 代理物件
public Proxy(Network network){ //初始化,把真實物件傳給代理物件,向上轉型操作。
this.network = network ;
}
public void check(){
System.out.println("檢查使用者是否合法。") ;
}
public void browse(){
this.check() ;
this.network.browse() ; // 呼叫真實的主題操作,這裡呼叫的是真實類裡的物件。
}
};
public class ProxyDemo{
public static void main(String args[]){
Network net = null ;
net = new Proxy(new Real()) ;// 指定代理操作,這裡兩次向上轉型操作,第一次向上是例項化代理類,
第二次向上轉型是括號中,把真實類物件傳入,以便在代理類中呼叫真實類中的方法。
net.browse() ; // 客戶只關心上網瀏覽一個操作
}
};
執行結果:
檢查使用者是否合法
上網瀏覽資訊
5、介面卡模式–上邊剛開始的案例
在Java中,一個子類實現一個介面,則肯定在子類中複寫此介面中全部抽象方法,那麼這就造成提供的抽象方法過多,而實際不需要,那麼我們會選擇一箇中間過渡(也就是介面卡模式)
interface Window{ // 定義Window介面,表示視窗操作
public void open() ; // 開啟
public void close() ; // 關閉
public void activated() ; // 視窗活動
public void iconified() ; // 視窗最小化
public void deiconified();// 視窗恢復大小
}
abstract class WindowAdapter implements Window{
public void open(){} ; // 開啟
public void close(){} ; // 關閉
public void activated(){} ; // 視窗活動
public void iconified(){} ; // 視窗最小化
public void deiconified(){};// 視窗恢復大小
};
class WindowImpl extends WindowAdapter{
public void open(){
System.out.println("視窗開啟。") ;
}
public void close(){
System.out.println("視窗關閉。") ;
}
};
public class AdapterDemo{
public static void main(String args[]){
Window win = new WindowImpl() ;
win.open() ;
win.close() ;
}
};
執行結果:
視窗開啟。
視窗關閉。
6、內部類的擴充套件
abstract class A{ // 定義抽象類
public abstract void printA() ; // 抽象方法
interface B{ // 定義內部介面
public void printB() ; // 定義抽象方法
}
};
class X extends A{ // 繼承抽象類
public void printA(){
System.out.println("HELLO --> A") ;
}
class Y implements B{ // 定義內部類實現內部介面
public void printB(){
System.out.println("HELLO --> B") ;
}
};
};
public class InnerExtDemo01{
public static void main(String args[]){
A.B b = new X().new Y() ; //參考前面內部類的知識,實現內部類例項的方法:外部類.內部類 內部類物件= 外部類物件.new 內部類
b.printB() ;
}
};
執行結果:
HELLO-->B
含有內部介面的抽象類的子類,必須也要定義內部類繼承該介面。
interface A{ // 定義介面
public void printA() ; // 抽象方法
abstract class B{ // 定義內部抽象類
public abstract void printB() ; // 定義抽象方法
}
};
class X implements A{ // 實現介面
public void printA(){
System.out.println("HELLO --> A") ;
}
class Y extends B{ // 繼承抽象類
public void printB(){
System.out.println("HELLO --> B") ;
}
};
};
public class InnerExtDemo02{
public static void main(String args[]){
A.B b = new X().new Y() ;
b.printB() ;
}
};
含有內部抽象類的介面,對於繼承他的子類來說,除了要覆寫介面中的抽象方法,還要專門定義一個內部類繼承抽象類,並覆寫全部抽象類。
從開發中,一般不推薦,看起來這些程式碼有點混亂。
總結:
Tips:在開發中,一個類永遠不要去繼承一個已經實現好的類,要麼繼承抽象類,要麼實現介面,如果兩個類同時可以使用的話,優先使用介面,避免單繼承的侷限性。
1)抽象類和介面類的例項化,通過多型性(向上轉型,向下轉型)。
2)抽象類表示一個模板,介面表示一個標準。
3)常見的設計模式:模板設計,工廠設計,代理設計,介面卡設計。