主頁 > 後端開發 > 萬字長文詳細總結!關于繼承、重寫與多載、封裝、介面的硬核干貨

萬字長文詳細總結!關于繼承、重寫與多載、封裝、介面的硬核干貨

2021-02-01 06:11:37 後端開發

在這里插入圖片描述


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點個贊喲,感謝您的支持!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/255056.html

標籤:Java

上一篇:Spring整合SpringMVC + Mybatis基礎框架的組態檔

下一篇:5年老碼農,update陳述句誤用一個雙引號,生產資料全變0了

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more