萬字長文詳細總結!關於繼承、重寫與重載、封裝、接口的硬核干貨


在這里插入圖片描述


Java語言在面向對象方面的知識點復雜繁瑣,但是幾乎是每個小伙伴學習編程必須踩的坑,其實,面向對象的底層都是一些計算機底層知識的結合,所以,不注重基礎的程序猿,一定不是一個可以走的遠的程序猿。那么,今天,我們先逐一地深入了解繼承、重載、接口和構造器的知識。

在學習這篇文章之前,如果有小伙伴對new過程發生了什么很感興趣的話,可以先用幾分鍾看完這篇文章。

new的過程發生了什么?看完這一篇就懂了

好了,正題開始。

在這里插入圖片描述

類的繼承

類的繼承是指從已經定義好存在的類中派生出一個新的類,我們在定義新類時,可以基於一個已經設計好的類,從已存在的類中繼承有用的功能(屬性和方法),這時已經存在的類便稱為父類,而這個繼承的新類則稱為子類。在類的繼承中,父類一般會具有所有子類的共有特性,而子類則可以增加一些個性化的方法。而子類還可以繼續派生出新的子類因此位於上層的類在概念上會更抽象,而位於下層的類就會更具體。

假設一個場景,你有一個朋友,家里超級有錢,你為了能夠繼承他的家產,認他做了爸爸,這個時候,假如他去了美國,感染了新冠肺炎,你以為你可以順理成章地繼承他的百萬油田,千萬礦山了,但是有一句話說得好,[你是想笑死我,然后繼承我的支付寶花唄借唄嗎😂],原來,他平時消費都用花唄,借唄,京東白條,光信用卡就幾十張了[少年,你渴望力量嗎],這個時候你只好認栽,好的壞的都繼承了。

所以,這個例子就說明了,繼承就是不好的好的都繼承了,所以父類的全局屬性,方法都可以在子類中輕松的訪問到

  • 父類和子類

在Java中實現繼承十分地簡單,具體語法如下:


<修飾符> class <子類名字> extends <父類名字> {

      [<成員變量定義>]
      [<方法的定義>]

}

當然,通常所說的子類一般指的是某個父類地直接子類,所以父類也可稱為該子類的直接超類。如果存在多層繼承關系的話,那么,就得按情況具體分析。

在Java程序中,一個類只能有一個父類,也就是說在extends關鍵字后面只能有一個類,它不支持多重繼承而接口是允許的

來看一個例子


public class Test {                                               
 public static void main(String[] args) {
  SubClass sc = new SubClass("valdcode","codevald",21);

 }

}

class SuperClass {

 //父類
 
 String name;
 int age;

 //構造方法
 SuperClass(String name,int age) {
 
  System.out.println("大家好,我是" + name);
  System.out.println("今年" + age + "歲");
 }
}

class SubClass extends SuperClass {

 //定義子類
 
 SubClass(String wechat,String name,int age) {
 
  super(name,age);
  System.out.println(1 > 0 ? "目前專注於研究計算機底層知識的研究" : "否");
  System.out.println("我的微信號是" + wechat + ",有問題歡迎添加一起交流");
 
 }

}

}

在上面的代碼中,類SubClass是一個子類,繼承了父類SuperClass的屬性和方法。在子類的構造方法中,通過Super()調用了父類的構造方法。這充分地說明子類是可以適用父類的屬性和方法。在main()函數里面,調用子類SubClass的構造方法,子類調用父類的構造方法Super(),設置對應的參數值

來看下控制台會輸出什么。

那么,在剛才的例子中,涉及到一個概念,構造方法,構造方法是Java類中比較重要的特殊方法,一個子類可以訪問父類的構造方法。在Java中調用父類構造方法會使用super(參數)這個方法。

再來看下一段代碼


public class JavaEnginer extends Enginer {
 //super和this
 //定義子類
 JavaEnginer() {
  super(); //調用父類無參數構造器
  prt("A Java Enginer."); //調用父類中的方法prt()
 }
 JavaEnginer(String name) {
  super(name);
  prt("A Java Enginer: " + name);
 }
  
 JavaEnginer(String name,String official_Accounts) {
  this(name); //調用有參數的構造器
  prt(name + "'s Official Accounts: " + official_Accounts); //調用父類中的方法
 }
 
 public static void main(String[] args) {
  JavaEnginer je = new JavaEnginer(); 
  je = new JavaEnginer("codevald"); 
  je = new JavaEnginer("codevald","@CodeVald");
 }
}

class Enginer {
 public static void  prt(String s) {

  //定義靜態方法prt()
  System.out.println(s); //輸出
 }

 //沒有參數的構造器
 Enginer() {
  prt("A Enginer"); //輸出文本
 }
 Enginer(String name) {
  prt("A enginer: " + name); //輸出文本
 }
}

調用父類的構造方法

在上面的代碼中,在子類中使用了super和this兩個不同的關鍵字表示不同的意思。在super后面加參數調用的是父類中具有相同形參的構造函數。

比如這個

JavaEnginer(String name) {
  super(name);
  prt("A Java Enginer: " + name);
 }

而在this后面加參數調用的是當前類中具有另一個形參的構造函數。比如這段

JavaEnginer(String name,String official_Accounts) {
  this(name); //調用有參數的構造器
  prt(name + "'s Official Accounts: " + official_Accounts); //調用父類中的方法
 }
 }

而在子類中,可以隨意調用父類中的方法,當然了,也可以加上this表示調用當前類繼承父類中的方法或者加上super的形式,也是可以正常編譯的。

比如下面這段

JavaEnginer() {
  super(); //調用父類無參數構造器
  prt("A Java Enginer."); //調用父類中的方法prt()
    /**
    *this.prt("A Java Enginer.") or 
    *super.prt("A Java Enginer.")都可以
    **
 }

來看下最終的運行結果

在這里插入圖片描述

訪問父類的屬性和方法

在Java中,子類可以輕松的訪問父類的屬性和方法,當然還是用剛才提過的super()了。具體語法格式 就是 super.[方法或全局變量]

來新建一個Demo

//子類
public class AccessSuperClassProperty extends SuperClass {
 public String name = "CodeVald";
 //輸出子類的name
 public void printOwner() {
  System.out.println(name);
 }

 public void printSuper() {
  System.out.println(super.name);
 }

 public static void main(String[] args) {
  AccessSuperClassProperty scp = new AccessSuperClassProperty();
  System.out.println(scp.name); //直接輸出子類的name
  scp.printOwner(); //直接輸出子類的name
  scp.printSuper(); //直接輸出父類的name
 }
}

//父類
class SuperClass {
 public String name = "codevald"
}

在上面的代碼中,分別在子類和父類中創建了同一個名字的變量屬性name,name的初始值不同。

然后,通過super來訪問與方法調用者對應的父類的對象

當系統創建AccessSuperClassProperty對象時,會對應創建一個父類對象SuperClass,不過父類對象的屬性只有通過super作為調用者才可以訪問到而已。

來看下代碼執行后的結果

在上述的代碼中,覆蓋的是父類的屬性,那么在子類的方法中可以通過父類名作為調用者來訪問被覆蓋的類屬性,如果子類中沒有包含和父類同名的屬性,則子類可以繼承父類屬性,那么在子類實例方法中就無需顯式地適用super作為調用者。所以,如果在某個方法中訪問一個同名的屬性,且沒有顯式地指定調用者,那么系統查找地順序為

  • 查找方法中是否有名為x的局部變量
  • 查找當前類中是否包含名為x的屬性
  • 查找x的直接父類中是否包含名為x的屬性,依次上溯到x的父類,直到java.lang.object類。如果最終沒有找到,就提示編譯錯誤

多重繼承

在Java中,多重繼承指的是A繼承了B,而C繼承了A,那么,這種就叫做多重繼承

來看個Demo

public class TestDemo {

 public static void main(String[] args) {
 
  Singer s1 = new Singer();
  Singer s2 = new Singer(s1);
  s1.print();
  s2.print();
  
  FolkSinger fs1 = new FolkSinger();
  FolkSinger fs2 = new FolkSinger("沈以城","《綠洲》");
  FolkSinger fs3 = new FolkSinger(fs2);
  
  fs1.print();
  fs2.print();
  fs3.print();
    
  FavoriteFolkSinger ffs1 = new FavoriteFolkSinger();  
  FavoriteFolkSinger ffs2 = new FavoriteFolkSinger("沈以城","《綠洲》");
  FavoriteFolkSinger ffs3 = new FavoriteFolkSinger(ffs2);
    
  ffs1.print();
  ffs2.print();
  ffs3.print();
 
 }
}

class FavoriteFolkSinger extends FolkSinger {

 FavoriteFolkSinger() {
  super();
 }
 
 FavoriteFolkSinger(FavoriteFolkSinger fs) {
  super(fs);
 }

 FavoriteFolkSinger(String name,String songTitle) {
 
  super(name,songTitle);

 }
 
 @Override 
 
 void print() {
  
  System.out.println("codevald最喜歡的" + "歌手為: " + super.name + " 代表作為: " + super.songTitle);
 }
}

class FolkSinger extends Singer {

 FolkSinger() {
 
  super("徐秉龍","《迂回》");
 
 }

 FolkSinger(FolkSinger fs) {
 
  super(fs);

 }
 
 FolkSinger(String name,String songTitle) {
 
  super(name,songTitle);
 }
 
 @Override 
 
 void print() {
  
  System.out.println("歌手為: " + super.name + " 代表作為: " + super.songTitle);
  
 }
}

class Singer {

 String name;//歌手名字
 String songTitle;  //歌曲名字
  
 Singer() {
 
 //構造方法用於初始化
 
  name = "民謠歌手,流行音樂歌手,嘻哈歌手";
  songTitle = "民謠歌曲,流行音樂,嘻哈音樂";

 }
 
 //定義參數為引用類型的構造方法

 Singer(Singer s) {
  name = s.name;
  songTitle = s.songTitle;
 }
 
 //定義構造方法,並且有參數
 
 Singer(String name,String songTitle) {
 
  this.name = name;
  this.songTitle = songTitle;

 }
  
 void print() {
 
  //輸出
  
  System.out.println("歌手類型為: " +this.name + " 歌曲類型為: " + this.songTitle);
 
 
 }

}

代碼執行后結果如下圖所示

在這里插入圖片描述

重寫和重載

- 重寫

在面向對象編程中,經常會聽到重寫重載兩個詞。這兩個都是十分重要的概念,雖然兩者的名字十分接近,但是實際上卻相差的很遠,並不是一個概念。

重寫是建立在繼承關系之上的,在子類中重新編寫來自父類的方法以達到自己的需求的目的

那么在重寫的時候,有一些規則需要遵守

  • 重寫方法的訪問權限應該與父類中的方法的訪問權限相同或者可以進一步擴大,但是絕不能縮小
  • 表示為final的方法不能進行重寫,靜態的方法不能重寫
  • 重寫方法的返回值類型必須與被重寫方法的返回值類型相同
  • 重寫方法的參數列表必須與被重寫方法的參數列表相同
  • 抽象方法必須在具體類中重寫
  • 無論被重寫方法是否拋出異常,重寫的方法都可以拋出任何非強制異常,但是,重寫的方法不能拋出新的強制性異常,或者比被重寫方法聲明的更廣泛的強制性異常,反之則可以成立

在編寫Java程序的時候,有時候經常需要子類重寫父類,新定義的類具有新的功能,下面給出一個實例文件,感受下重寫的重要性

public class TestOverride {

 public static void main(String[] args) {
  SubClass s = new SubClass("計科1班",21,52,"廣州市");
  s.print(); //調用子類的print()
 }
}

class SubClass extends SuperClass {

 String saddr; 
 SubClass(String name,int id,int number,String address) {
  
 super(name,id,number);
 saddr = address;
 
  
 }
 @Override
 void print() {
  System.out.println("codevald" + "所在的班級為" + sname + ",學號為" + sid + 
     ",總人數為" + snumber + " 學校所在地址為: " + saddr);
 }
}

class SuperClass {

 String sname; //名字
 int sid; //工號
 int snumber; //人數
  
 SuperClass(String sname,int sid,int snumber) {
 
  this.sname = sname;
  this.sid = sid;
  this.snumber = snumber;
 }
  
 void print() {
  System.out.println("組織名為: " + sname + " 工號為: " + sid + " 組織人數為:" + snumber);
 }

}

運行結果

在這里插入圖片描述

但是,在編寫重寫方法的時候,要注意權限問題,如果把上面的子類中print()方法權限改寫為private,編譯時將會報錯。

- 重載

在Java中,重載大大減少了程序猿的變成負擔,開發者不需要記住那些復雜而難記的方法名稱或者參數即可實現項目的開發需求。

那么,在Java的程序中,同一類中可以有兩個或者多個方法具有相同的方法名,只要他們的參數不同即可,這就是方法重載。

重載的規則十分簡單,參數決定重載方法的調用。當調用重載方法時,要確定調用哪個參數是基於其參數的。

來看下具體的實例,感受下重載的魅力。

public class TestOverload {

 public void test() {
  System.out.println("無參數的方法");
 }
 
 public void test(String message) {
  System.out.println("重載的test方法" + message);
 }

 public static void main(String[] args) {
   TestOverload o = new TestOverload();
   int i = 0;
   do {
    System.out.print(args[i]);
    i++;

   } while (i < args.length);
   System.out.println("");
   o.test();
   o.test("@CodeVald");
 
 }
}

上面分別定義了兩個同名方法test(),但是其方法的形參列表不同,系統可以自動區分這兩個方法,這兩種類型的方法稱為方法重載。

在上面的例子中,我編寫了一段代碼,利用do {} while ()語句輸出了運行時自定義的字符串,在運行時在文件的后面假如自定義的字符串即可輸出,其實這里是利用了main()函數后面的字符串數組args[],它可以檢測輸入的字符串並存儲起來

來看下運行結果

重寫和重載其實十分容易,我寫了一段話來區分它們

重寫發生在具有繼承關系的類之間,而重載則是在同一個類中有多個同名的方法,主要通過參數來區別。[繼承可重寫,方法可重載]。

那么,在重寫中還需要注意:

在重寫了父類中的方法,按正常的辦法子類對象是無法訪問到父類中被覆蓋的方法的,但是也是可以在子類中調用父類的被覆蓋的方法。如果需要訪問到被覆蓋的方法,則可以適用super[被覆蓋的是實例方法]或者父類名[被覆蓋的是類方法]作為調用者來調用父類中被覆蓋的方法。

如果父類方法具有私有訪問權限,則該方法對其子類是隱藏的,其子類無法重寫該方法。那么要是在子類中定義了一個與父類私有方法相同的名字,相同的形參列表,相同的返回值類型的方法,這時候並不是重寫,只是重新定義了一個新方法。

封裝

封裝(encapsulation)是面向對象的特征之一,是指將對象的狀態信息隱藏在對象內部,不允許外部程序直接訪問對象的內部信息,通過該類對外所暴露的方法實現對內部信息的操作和訪問。在Java中封裝類或對象的目的如下所示:

  • 隱藏類的實現細節
  • 讓調用者只能通過實現預定的方法訪問數據,在訪問代碼中加入控制邏輯,限制對屬性的不合理訪問
  • 進行數據檢查
  • 需求更改時,便於修改,提高代碼的可維護性

使用封裝特性時,訪問控制符的知識也需要先了解一下

在Java中,提供了3個訪問控制符,分別是private、protected、public,分別代表了3個訪問控制級別,如果不適用,會使用默認的default,具體說明如下:

private: 如果類里的一個成員(包括屬性和方法)使用private訪問修飾符時,那么這個成員只能在類的內部被訪問。所以,使用private修飾屬性可以把屬性隱藏在類的內部。

default:如果類里的一個成員(包括屬性和方法)或者一個頂級類不適用任何訪問控制符來修飾,則稱它為默認訪問控制,由default訪問控制符修飾的成員或者頂級類可以被相同包下其他類訪問。

protected 如果一個成員(包括屬性和方法)使用protected訪問控制符修飾,那么這個成員既可以被同一個包中的其他類訪問,也可以被不同個包中的其他子類訪問, 通常情況下,如果使用protected修飾一個辦法,那么通常是希望子類重寫這個方法。

public 這是一個最寬松的訪問控制級別,如果一個成員(包括屬性和方法)或者一個頂級類使用了public來修飾,那么這個成員或者頂級類就可以被所有類訪問,這時不管訪問類和被訪問類是否處於同一個包中,他們是否具有父子繼承關系。

訪問控制控制一個類的成員是否可以被其他類訪問。對於局部變量來說,其作用域就是他所在的方法,不可能被其它類來訪問,因此他們不能使用訪問控制符來修飾

Java中的頂級類可以使用訪問控制符來修飾,但是頂級類只能有兩種訪問控制級別,分別是public和default(默認的)。頂級類不能使用private和protected修飾。由於頂級類既不處於任何類的內部,也沒有外部類的子類,因此private和protected訪問控制符對頂級類沒有意義。

來看個例子

public class Person {

 //定義類Person
 
 private String name; //定義私有屬性變量
 private int age;
 
 public Person() {
  //定義沒有參數的公有構造方法
 
 
 }

 public Person(String name,int age) {
  //定義有參數的公有構造方法
  
  this.name = name;
  this.age = age;
 
 
 }

 public void setName(String name) {
 
  //執行校驗,要求用戶名長度必須在1-10位之間
 
  if (name.length() > 10 || name.length() < 1) {
  
   System.out.println("您設置的人名不符合要求.");
   return;
  
  } else {
  
   this.name = name;
  
  }
  
 }
  
  
  public String getName() {
  
   //返回屬性name
   return this.name;
  
  
  }
  
  
  public void setAge(int age) {
  
   //執行校驗,要求用戶年齡必須在0-100之間
   
   if (age > 100 || age < 0) {
   
    System.out.println("您設置的年齡不合法.");
   
   } else {
   
    this.age = age;
   
   }
  
  
  
  }
  
  
  public int getAge() {
  
   return this.age;
  
  }
  

 
}
 }
}

上面編寫的類的name和age屬性只能在類內才可以操作和訪問,在Person類之外只能通過各自的對應的setter和getter方法來操作和訪問。

編寫測試代碼如下

public class TestPerson {
 
 public static void main(String[] args) {
 
  Person p = new Person();
  
  //屬性已被隱藏,所以下面的語句將出現編譯錯誤
  
  //p.age = 100;
  
  //下面的語句編譯時不會出現錯位,但運行時會提示age屬性不合法
  
  p.setAge(200);
 
  //如果想要獲取屬性,必須通過暴漏的getter方法去訪問p的age屬性
  //如果沒有設置屬性的值,int類型的成員變量默認初始化為0,String類型的成員變量默認初始化為Null
  
  System.out.println("未設置屬性變量age時: " + p.getAge());
 
  p.setAge(50);
  
  System.out.println("成功設置屬性變量age后: " + p.getAge());
 
  //同樣要用setter()方法來設置p的name屬性
  p.setName("codevald");
  System.out.println("成功設置屬性變量 name 后:" + p.getName());
  

 }

}

執行結果為

在這里插入圖片描述

那么,總結一下,在使用Java的訪問控制符時,應該遵循以下原則:

  • 類里的絕大部分屬性都應該使用private來修飾,除了靜態變量(用static修飾的),才考慮用public來修飾,除此之外,有一些方法是輔助所在的該類的一些其他方法,這些方法被其他方法調用實現功能,也應該使用private修飾
  • 如果一個類是其他類的父類,該類所包含的大部分方法僅僅希望被其他子類重寫,而不想被外界直接調用的話,最好使用protected修飾這些方法。還有一些作為父類的抽象類,有時候希望使用多態操作,那么父類的方法則必須用public修飾
  • 暴露出來給其他類自由調用的方法應該使用public修飾,因此,類的構造器應該用public修飾,暴露出來給其他類創建該類的對象。

接口

在Java語言中,接口是一種和類十分相似的東西,定義接口的方法和定義類的方法差不多,在接口里面也可以包含方法,在接口里可以派生新的類。

- 接口的定義

接口里的方法和抽象類中的方法一樣,它的方法也是抽象的,所以接口是不能具體化成對象的,他只是規定應該怎么做,而不管具體怎么做。定義完接口,任何類都可以實現這個接口。而且類不支持多繼承類,但是類可以實現多個接口,在Java中創建接口的語法如下

[public] interface <接口名> {

    常量;
    抽象方法;
}



  • 接口的修飾符只能是public,因為這樣接口才能被任何包中的接口或者類訪問
  • interface:Java中接口的關鍵字
  • 接口名: 規則在Java中和類名一樣
  • 常量: 在接口中不能聲明變量,因為i接口具備三個特性:公有(public)、靜態(static)、常量(final)

那么,在接口里,是使用implement用於接口的繼承的,這和類不一樣,類的繼承是使用extends的,只要一個類沒有聲明為final或者這個類是abstract就能繼承。Java不支持多重繼承,但是可以使用接口來實現,就用到了implements,使用implements可以實現多個接口,只需要用逗號分開來。 接口的多繼承語法如下:

class A extends B implements C,D,E {


}

- 接口里的方法

在接口里,所有的方法都是公有的,抽象的,因此在方法聲明的時候,可以省略關鍵字public、abstract,來看個Demo,看下是不是這樣子。


class TestJieKou {

 //測試類
 
 public static void main(String[] args) {
 
  JieKou1 jiekou = new JieKou1();
  
  jiekou.fun1();
  jiekou.fun2();
  jiekou.fun3();
  jiekou.fun4();
  jiekou.fun5();

 
 }

}


interface JieKou {
 //定義接口
 
 void fun1()//在接口中定義方法fun1()
 
 public void fun2()//在接口中定義方法fun2()
 
 abstract void fun3()//在接口中定義方法fun3()
 
 public abstract void fun4()//在接口中定義方法fun4()
 
 abstract public void fun5()//在接口中定義方法fun5()



}


class JieKou1 implements JieKou {

 //繼承接口JieKou
 
 public void fun1() {
 
  System.out.println("接口里第一種方法沒有修飾符");
 
 }

 public void fun2() {
  
  System.out.println("接口里第二種方法有修飾符public"); 
 
 }
 
 public void fun3() {
  
  System.out.println("接口里第三種方法有修飾符abstract"); 
 
 }

 public void fun4() {
  
  System.out.println("接口里第四種方法有修飾符public、abstract"); 
 
 }


 public void fun5() {
  
  System.out.println("接口里第五種方法有修飾符abstract、public"); 
 
 }

}



編譯運行程序后的結果

在這里插入圖片描述

- 引用接口

在引用接口前需要先實現這個接口,在Java中實現接口的格式如下


<修飾符> class <類名> implements <接口名> {

  ...
  ...
  ...


}




下面這個Demo實現計算器的基本功能加減乘除

class JiekouShiXian {

 public static void main(String[] args) {
 
  Calculator cal = new Calculator();
  System.out.println("1200 + 1200 = " + cal.add(1200,1200));
  System.out.println("2400 - 1100 = " + cal.subtract(2400,1100));
  System.out.println("1100 * 1100 = " + cal.multiply(1100,1100));
  System.out.println("5200 / 2  = " + cal.divide(5200,2));
 
 }

}

interface Add {

 int add(int a,int b);

}

interface Subtract {

 int subtract(int a,int b);

}

interface Multiply {

 int multiply(int a,int b);

}

interface Divide {

 int divide(int a,int b);


}

class Calculator implements Add,Subtract,Multiply,Divide {

 public int add(int a,int b) {
  
  return a + b;
 
 }
 
 public int subtract(int a,int b) {
 
  return a - b;
 
 }
 
 public int multiply(int a,int b) {
 
  return a * b;
 
 }
 
 public int divide(int a,int b) {
 
  return a / b;
 
 }

}

在上面的代碼中,分別定義了四個接口,分別表示實現加、減、乘、除的功能,然后定義了一個類實現四個接口,重寫四個接口的內置方法,並編寫測試類測試功能

來看下運行結果

在編寫程序的時候,用戶可以建立接口類型的引用變量,接口的引用變量能夠存儲一個 指向對象的引用值,這個對象可以實現任何任何該接口類的實例,用戶可以通過接口都熬用該對象的方法。下面的代碼演示了使用引用接口的過程


class JiekouYinYong {

 public static void main(String[] args) {
 
  

  Calculator cal = new Calculator();
  
  Add add = cal;
  Subtract subtract = cal;
  Multiply multiply = cal;
  Divide divide = cal;
  
  //調用cal的方法
  System.out.println("1200 + 1200 = " + cal.add(1200,1200));
  System.out.println("2400 - 1100 = " + cal.subtract(2400,1100));
  System.out.println("1100 * 1100 = " + cal.multiply(1100,1100));
  System.out.println("5200 / 2  = " + cal.divide(5200,2));
  
  
  //調用接口Add中的add方法
  
  System.out.println("1200 + 1200 = " + add.add(1200,1200));

  //調用接口Subtract中的subtract方法

  System.out.println("2400 - 1100 = " + subtract.subtract(2400,1100));

  //調用接口Multiply中的multiply方法

  System.out.println("1100 * 1100 = " + multiply.multiply(1100,1100));

  //調用接口Divide中的divide方法

  System.out.println("5200 / 2 =  " + divide.divide(5200,2));

 
 }



}




interface Add {

 int add(int a,int b);

}

interface Subtract {

 int subtract(int a,int b);

}

interface Multiply {

 int multiply(int a,int b);


}

interface Divide {

 int divide(int a,int b);


}

class Calculator implements Add,Subtract,Multiply,Divide {

 public int add(int a,int b) {
  
  return a + b;
 
 }
 
 public int subtract(int a,int b) {
 
  return a - b;
 
 }
 
 public int multiply(int a,int b) {
 
  return a * b;
 
 }
 
 public int divide(int a,int b) {
 
  return a / b;
 
 }



}

運行結果

在這里插入圖片描述

- 接口間的繼承

接口間是完全支持多繼承的,這和類不一樣,即一個接口可以有多個直接父接口。和類的繼承相似,子接口擴展某個父接口,並獲得父接口里定義的所有抽象方法、常量屬性、內部類和枚舉類定義。

通過個具體的例子來看下


public class JiCheng {
 
 public static void main(String[] args) {

  System.out.println(interfaceC.name_A);
  System.out.println(interfaceC.name_B);
  System.out.println(interfaceC.name_C);

 }


}



interface interfaceA {

 String name_A = "codevald";

 void printA();

}


interface interfaceB {

 String name_B = new String("codevald");
 
 void printB();

}

interface interfaceC extends interfaceA,interfaceB {

 String name_C = "code" + new String("vald");
 
 void printC();


}

在上面的代碼中,接口interfaceC繼承了interfaceA和interfaceB,所以interfaceC獲得了他們的常量。

看下運行結果

- 接口的私有方法

  • 在Java7或者更早的版本中,在一個接口里只能定義常量或者抽象方法這兩種元素,不能再接口中提供方法的實現。如果要提供抽象方法和非抽象方法(方法與實現)的組合,只能使用抽象類
  • 在Java8版本中,在接口里引入了默認方法和靜態方法這兩個新功能。所以,在Java8版本的接口中,可以定義的成員有常量、抽象方法、默認方法和靜態方法
  • 在Java9版本中,一個接口可以定義的成員有常量、抽象方法、默認方法、靜態方法、私有方法和私有靜態方法

來看下我編寫的在Java7、Java8、Java9中接口方法的用法

在7版本或者更早的版本的接口中可能只包含抽象方法,這些接口方法必須由實現接口的類來實現

先定義Interface7


public interface Interface7 {
 public abstract void method();
}

然后定義一個類實現接口


public class Class7 implements Interface7 {

 public void method() {
 
 
  System.out.println("Hello,I am codevald.");
 
 
 }

 public static void main(String[] args) {
 
  Interface7 instance = new Class7();
  instance.method();

 }

}

執行結果

在Java8版本中,在接口中除了可以定義公共抽象方法外,還可以包含公共靜態方法和公共默認方法

定義一個接口



public interface Interface8 {

 public abstract void method1();
 public default void method2() {
 
  System.out.println("Hello,I am default method.");
 
 }
 
 public static void method3() {
  
  System.out.println("Hello,I am static method.");
 
 }

}

再定義一個類實現方法


public class Class8 implements Interface8 {

 @Override 
 
 public void method1() {
 
  System.out.println("public abstract method.");
 
 }


 public static void main(String[] args) {
 
 
  Interface8 instance = new Class8();
  instance.method1();
  instance.method2(); 
  Interface8.method3();

 
 }

}

看下運行結果

在這里插入圖片描述

從Java9版本開始,就可以在接口中添加私有方法和私有靜態方法了,這些私有方法可以提高代碼的可重用性。方法之間需要共享代碼,私有接口方法就允許這樣做,但不能將私有方法暴露到它的實現類中。

先定義一個接口


public interface Interface9 {


 public abstract void method1();
 
 public default void method2() {
 
  method4();
  method5();
  
  System.out.println("Hello,I am default method.");

 }
 
 public static void method3() {
 
  method5();
  System.out.println("Hello,I am static method.");
 
 
 }
 
 
 private void method4() {
 
  System.out.println("Hello,I am private method.");
 
 
 }
 
 private static void method5() {
 
  System.out.println("Hello,I am private static method.");
 
 
 }


}

再定義一個類


public class Class9 implements Interface9 {

 public void method1() {
 
  System.out.println("Hello,I am abstract method.");
 
 }
 
 public static void main(String[] args) {
 
  Interface9 instance = new Class9();
  instance.method1();
  instance.method2();
  Interface9.method3();
 
 }


}

看下運行結果

在這里插入圖片描述

-接口和抽象類的區別聯系

相同之處

  • 接口和抽象類都不能實例化
  • 接口和抽象類都可以包含抽象方法,實現接口或者繼承抽象類的子類都必須實現這些抽象方法

不同之處

  • 接口中不包含構造器,抽象類可以包含構造器。抽象類里的構造器並不能創建對象,而是讓子類調用這些構造器完成屬於抽象類的初始化

  • 接口里不能包含初始化塊,但抽象類可以包含初始化塊

  • 一個類最多只能有一個父類,包含抽象類,但一個類可以實現多個接口


寫到這里就結束啦,這些雖然是很基礎的東西,不過后面設計模式,包括實際的項目實操,很多都會用到這些很基礎的東西,如果基礎不牢,很容易就半途而廢,“泰山不讓土壤,故能成其大;河海不擇細流,故能就其深.”,一個想要走的更遠的程序猿,更要注意平時底層基礎的積累,這樣子,遇到問題,才能不慌不忙的提出解決問題的方案。

如果覺得這篇文章不錯的話,記得幫我@codevald點個贊喲,感謝您的支持!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM