<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          「萬字圖文」史上最姨母級Java繼承詳解

          共 20748字,需瀏覽 42分鐘

           ·

          2021-04-21 04:54


          點擊上方藍字關(guān)注我,設(shè)為星標,任何轉(zhuǎn)載請滴滴我授權(quán)

          課程導學

          在Java課堂中,所有老師不得不提到面向?qū)ο?/strong>(Object Oriented),而在談到面向?qū)ο蟮臅r候,又不得不提到面向?qū)ο蟮娜筇卣鳎悍庋b、繼承、多態(tài)。三大特征緊密聯(lián)系而又有區(qū)別,本課程就帶你學習Java的繼承

          你可能不知道繼承到底有什么用,但你大概率曾有過這樣的經(jīng)歷:寫Java項目/作業(yè)時候創(chuàng)建很多相似的類,類中也有很多相同的方法,做了很多重復(fù)的工作量,感覺很臃腫。而合理使用繼承就能大大減少重復(fù)代碼,提高代碼復(fù)用性。


          繼承的初相識

          學習繼承,肯定是先從廣的概念了解繼承是什么以及其作用,然后才從細的方面學習繼承的具體實現(xiàn)細節(jié),本關(guān)就是帶你先快速了解和理解繼承的重要概念。

          什么是繼承

          繼承(英語:inheritance)是面向?qū)ο筌浖夹g(shù)中的一個概念。它使得復(fù)用以前的代碼非常容易,能夠大大縮短開發(fā)周期,降低開發(fā)費用。

          Java語言是非常典型的面向?qū)ο蟮恼Z言,在Java語言中繼承就是子類繼承父類的屬性和方法,使得子類對象(實例)具有父類的屬性和方法,或子類從父類繼承方法,使得子類具有父類相同的方法。父類有時也叫基類、超類;子類有時也被稱為派生類。

          我們來舉個例子:我們知道動物有很多種,是一個比較大的概念。在動物的種類中,我們熟悉的有貓(Cat)、狗(Dog)等動物,它們都有動物的一般特征(比如能夠吃東西,能夠發(fā)出聲音),不過又在細節(jié)上有區(qū)別(不同動物的吃的不同,叫聲不一樣)。在Java語言中實現(xiàn)Cat和Dog等類的時候,就需要繼承Animal這個類。繼承之后Cat、Dog等具體動物類就是子類,Animal類就是父類。


          為什么需要繼承

          你可能會疑問為什么需要繼承?在具體實現(xiàn)的時候,我們創(chuàng)建Dog,Cat等類的時候?qū)崿F(xiàn)其具體的方法不就可以了嘛,實現(xiàn)這個繼承似乎使得這個類的結(jié)構(gòu)不那么清晰。

          如果僅僅只有兩三個類,每個類的屬性和方法很有限的情況下確實沒必要實現(xiàn)繼承,但事情并非如此,事實上一個系統(tǒng)中往往有很多個類并且有著很多相似之處,比如貓和狗同屬動物,或者學生和老師同屬人。各個類可能又有很多個相同的屬性和方法,這樣的話如果每個類都重新寫不僅代碼顯得很亂,代碼工作量也很大。

          這時繼承的優(yōu)勢就出來了:可以直接使用父類的屬性和方法,自己也可以有自己新的屬性和方法滿足拓展,父類的方法如果自己有需求更改也可以重寫。這樣使用繼承不僅大大的減少了代碼量,也使得代碼結(jié)構(gòu)更加清晰可見


          所以這樣從代碼的層面上來看我們設(shè)計這個完整的Animal類是這樣的:

          class Animal
          {
              public int id;
              public String name;
              public int age;
              public int weight;

              public Animal(int id, String name, int age, int weight) {
                  this.id = id;
                  this.name = name;
                  this.age = age;
                  this.weight = weight;
              }
              //這里省略get set方法
              public void sayHello()
              
          {
                  System.out.println("hello");
              }
              public void eat()
              
          {
                  System.out.println("I'm eating");
              }
              public void sing()
              
          {
                  System.out.println("sing");
              }
          }

          而Dog,Cat,Chicken類可以這樣設(shè)計:

          class Dog extends Animal//繼承animal
          {
              public Dog(int id, String name, int age, int weight) {
                  super(id, name, age, weight);//調(diào)用父類構(gòu)造方法
              }
          }
          class Cat extends Animal{

              public Cat(int id, String name, int age, int weight) {
                  super(id, name, age, weight);//調(diào)用父類構(gòu)造方法
              }
          }
          class Chicken extends Animal{

              public Chicken(int id, String name, int age, int weight) {
                  super(id, name, age, weight);//調(diào)用父類構(gòu)造方法
              }
              //雞下蛋
              public void layEggs()
              
          {
                  System.out.println("我是老母雞下蛋啦,咯噠咯!咯噠咯!");
              }
          }

          各自的類繼承Animal后可以直接使用Animal類的屬性和方法而不需要重復(fù)編寫,各個類如果有自己的方法也可很容易地拓展。上述代碼中你需要注意extends就是用來實現(xiàn)繼承的。

          繼承的分類

          繼承分為單繼承和多繼承,Java語言只支持類的單繼承,但可以通過實現(xiàn)接口的方式達到多繼承的目的。我們先用一張表概述一下兩者的區(qū)別,然后再展開講解。


          定義優(yōu)缺點
          單繼承
          一個子類只擁有一個父類優(yōu)點:在類層次結(jié)構(gòu)上比較清晰
          缺點:結(jié)構(gòu)的豐富度有時不能滿足使用需求
          多繼承(Java不支持,但可以用其它方式滿足多繼承使用需求)
          一個子類擁有多個直接的父類優(yōu)點:子類的豐富度很高
          缺點:容易造成混亂

          單繼承

          單繼承,是一個子類只擁有一個父類,如我們上面講過的Animal類和它的子類。單繼承在類層次結(jié)構(gòu)上比較清晰,但缺點是結(jié)構(gòu)的豐富度有時不能滿足使用需求

          多繼承(Java不支持,但可以實現(xiàn))

          多繼承,是一個子類擁有多個直接的父類。這樣做的好處是子類擁有所有父類的特征,子類的豐富度很高,但是缺點就是容易造成混亂。下圖為一個混亂的例子。


          Java雖然不支持多繼承,但是Java有三種實現(xiàn)多繼承效果的方式,分別是內(nèi)部類、多層繼承和實現(xiàn)接口。

          內(nèi)部類可以繼承一個與外部類無關(guān)的類,保證了內(nèi)部類的獨立性,正是基于這一點,可以達到多繼承的效果。

          多層繼承:子類繼承父類,父類如果還繼承其他的類,那么這就叫多層繼承。這樣子類就會擁有所有被繼承類的屬性和方法。


          實現(xiàn)接口無疑是滿足多繼承使用需求的最好方式,一個類可以實現(xiàn)多個接口滿足自己在豐富性和復(fù)雜環(huán)境的使用需求。類和接口相比,類就是一個實體,有屬性和方法,而接口更傾向于一組方法。舉個例子,就拿斗羅大陸的唐三來看,他存在的繼承關(guān)系可能是這樣的:


          如何實現(xiàn)繼承

          實現(xiàn)繼承除了上面用到的extends外,還可以用implements這個關(guān)鍵字實現(xiàn)。下面,讓我給你逐一講解一下。

          extends關(guān)鍵字

          在Java中,類的繼承是單一繼承,也就是說一個子類只能擁有一個父類,所以extends只能繼承一個類。其使用語法為:

          class 子類名 extends 父類名{}

          例如Dog類繼承Animal類,它是這樣的:

          class Animal{} //定義Animal類
          class Dog extends Animal{} //Dog類繼承Animal類

          子類繼承父類后,就擁有父類的非私有的屬性和方法。如果不明白,請看這個案例,在IDEA下創(chuàng)建一個項目,創(chuàng)建一個test類做測試,分別創(chuàng)建Animal類和Dog類,Animal作為父類寫一個sayHello()方法,Dog類繼承Animal類之后就可以調(diào)用sayHello()方法。具體代碼為:

          class Animal {
              public void  sayHello()//父類的方法
              
          {
                  System.out.println("hello,everybody");
              }
          }
          class Dog extends Animal//繼承animal
          { }
          public class test {
              public static void main(String[] args) {
                 Dog dog=new Dog();
                 dog.sayHello();
              }
          }

          點擊運行的時候Dog子類可以直接使用Animal父類的方法。


          implements 關(guān)鍵字

          使用implements 關(guān)鍵字可以變相使Java擁有多繼承的特性,使用范圍為類實現(xiàn)接口的情況,一個類可以實現(xiàn)多個接口(接口與接口之間用逗號分開)。Java接口是一系列方法的聲明,一個接口中沒有方法的具體實現(xiàn) 。子類實現(xiàn)接口的時候必須重寫接口中的方法。

          我們來看一個案例,創(chuàng)建一個test2類做測試,分別創(chuàng)建doA接口和doB接口,doA接口聲明sayHello()方法,doB接口聲明eat()方法,創(chuàng)建Cat2類實現(xiàn)doA和doB接口,并且在類中需要重寫sayHello()方法和eat()方法。具體代碼為:

          interface doA{
               void sayHello();
          }
          interface doB{
               void eat();
              //以下會報錯 接口中的方法不能具體定義只能聲明
              //public void eat(){System.out.println("eating");}
          }
          class Cat2 implements  doA,doB{
              @Override//必須重寫接口內(nèi)的方法
              public void sayHello() {
                  System.out.println("hello!");
              }
              @Override
              public void eat() {
                  System.out.println("I'm eating");
              }
          }
          public class test2 {
              public static void main(String[] args) {
                  Cat2 cat=new Cat2();
                  cat.sayHello();
                  cat.eat();
              }
          }

          Cat類實現(xiàn)doA和doB接口的時候,需要實現(xiàn)其聲明的方法,點擊運行結(jié)果如下,這就是一個類實現(xiàn)接口的簡單案例:


          繼承的特點

          繼承的主要內(nèi)容就是子類繼承父類,并重寫父類的方法。使用子類的屬性或方法時候,首先要創(chuàng)建一個對象,而對象通過構(gòu)造方法去創(chuàng)建,在構(gòu)造方法中我們可能會調(diào)用子父類的一些屬性和方法,所以就需要提前掌握this和super關(guān)鍵字。創(chuàng)建完這個對象之后,在調(diào)用重寫父類的方法,并區(qū)別重寫和重載的區(qū)別。所以本節(jié)根據(jù)this、super關(guān)鍵字—>構(gòu)造函數(shù)—>方法重寫—>方法重載的順序進行講解。

          this和super關(guān)鍵字

          this和super關(guān)鍵字是繼承中非常重要的知識點,分別表示當前對象的引用和父類對象的引用,兩者有很大相似又有一些區(qū)別。

          this表示當前對象,是指向自己的引用。

          this.屬性 // 調(diào)用成員變量,要區(qū)別成員變量和局部變量
          this.() // 調(diào)用本類的某個方法
          this() // 表示調(diào)用本類構(gòu)造方法

          super表示父類對象,是指向父類的引用。

          super.屬性 // 表示父類對象中的成員變量
          super.方法() // 表示父類對象中定義的方法
          super() // 表示調(diào)用父類構(gòu)造方法

          此外,this和super關(guān)鍵字只能出現(xiàn)在非static修飾的代碼中。

          this()和super()都只能在構(gòu)造方法的第一行出現(xiàn),如果使用this()表示調(diào)用當前類的其他構(gòu)造方法,使用super()表示調(diào)用父類的某個構(gòu)造方法,所以兩者只能根據(jù)自己使用需求選擇其一。

          寫一個小案例,創(chuàng)建D1類和子類D2如下:

          class D1{
              public D1() {}//無參構(gòu)造
              public void sayHello() {
                  System.out.println("hello");
              }
          }
          class D2 extends D1{
              public String name;
              public D2(){
                  super();//調(diào)用父類構(gòu)造方法
                  this.name="BigSai";//給當前類成員變量賦值
              }
              @Override
              public void sayHello() {
                  System.out.println("hello,我是"+this.name);
              }
              public void test()
              
          {
                  super.sayHello();//調(diào)用父類方法
                  this.sayHello();//調(diào)用當前類其他方法
              }
          }
          public class test8 {
              public static void main(String[] args) {
                  D2 d2=new D2();
                  d2.test();
              }
          }

          執(zhí)行的結(jié)果為:


          構(gòu)造方法

          構(gòu)造方法是一種特殊的方法,它是一個與類同名的方法。對象的創(chuàng)建就通過構(gòu)造方法來完成,其主要的功能是完成對象的初始化。但在繼承中構(gòu)造方法是一種比較特殊的方法(比如不能繼承),所以要了解和學習在繼承中構(gòu)造方法的規(guī)則和要求。

          構(gòu)造方法可分為有參構(gòu)造和無參構(gòu)造,這個可以根據(jù)自己的使用需求合理設(shè)置構(gòu)造方法。但繼承中的構(gòu)造方法有以下幾點需要注意:

          父類的構(gòu)造方法不能被繼承:

          因為構(gòu)造方法語法是與類同名,而繼承則不更改方法名,如果子類繼承父類的構(gòu)造方法,那明顯與構(gòu)造方法的語法沖突了。比如Father類的構(gòu)造方法名為Father(),Son類如果繼承Father類的構(gòu)造方法Father(),那就和構(gòu)造方法定義:構(gòu)造方法與類同名沖突了,所以在子類中不能繼承父類的構(gòu)造方法,但子類會調(diào)用父類的構(gòu)造方法。

          子類的構(gòu)造過程必須調(diào)用其父類的構(gòu)造方法:

          Java虛擬機構(gòu)造子類對象前會先構(gòu)造父類對象,父類對象構(gòu)造完成之后再來構(gòu)造子類特有的屬性,這被稱為內(nèi)存疊加。而Java虛擬機構(gòu)造父類對象會執(zhí)行父類的構(gòu)造方法,所以子類構(gòu)造方法必須調(diào)用super()即父類的構(gòu)造方法。就比如一個簡單的繼承案例應(yīng)該這么寫:

          class A{
              public String name;
              public A() {//無參構(gòu)造
              }
              public A (String name){//有參構(gòu)造
              }
          }
          class B extends A{
              public B() {//無參構(gòu)造
                 super();
              }
              public B(String name) {//有參構(gòu)造
                //super();
                 super(name);
              }
          }

          如果子類的構(gòu)造方法中沒有顯示地調(diào)用父類構(gòu)造方法,則系統(tǒng)默認調(diào)用父類無參數(shù)的構(gòu)造方法。

          你可能有時候在寫繼承的時候子類并沒有使用super()調(diào)用,程序依然沒問題,其實這樣是為了節(jié)省代碼,系統(tǒng)執(zhí)行時會自動添加父類的無參構(gòu)造方式,如果不信的話我們對上面的類稍作修改執(zhí)行:

          image-20201026201029796

          方法重寫(Override)

          方法重寫也就是子類中出現(xiàn)和父類中一模一樣的方法(包括返回值類型,方法名,參數(shù)列表),它建立在繼承的基礎(chǔ)上。你可以理解為方法的外殼不變,但是核心內(nèi)容重寫

          在這里提供一個簡單易懂的方法重寫案例:

          class E1{
              public void doA(int a){
                  System.out.println("這是父類的方法");
              }
          }
          class E2 extends E1{
              @Override
              public void doA(int a) {
                  System.out.println("我重寫父類方法,這是子類的方法");
              }
          }

          其中@Override注解顯示聲明該方法為注解方法,可以幫你檢查重寫方法的語法正確性,當然如果不加也是可以的,但建議加上。

          對于重寫,你需要注意以下幾點:

          從重寫的要求上看:

          • 重寫的方法和父類的要一致(包括返回值類型、方法名、參數(shù)列表)
          • 方法重寫只存在于子類和父類之間,同一個類中只能重載

          從訪問權(quán)限上看:

          • 子類方法不能縮小父類方法的訪問權(quán)限
          • 子類方法不能拋出比父類方法更多的異常
          • 父類的私有方法不能被子類重寫

          從靜態(tài)和非靜態(tài)上看:

          • 父類的靜態(tài)方法不能被子類重寫為非靜態(tài)方法
          • 子類可以定義于父類的靜態(tài)方法同名的靜態(tài)方法,以便在子類中隱藏父類的靜態(tài)方法(滿足重寫約束)
          • 父類的非靜態(tài)方法不能被子類重寫為靜態(tài)方法

          從抽象和非抽象來看:

          • 父類的抽象方法可以被子類通過兩種途徑重寫(即實現(xiàn)和重寫)
          • 父類的非抽象方法可以被重寫為抽象方法

          當然,這些規(guī)則可能涉及一些修飾符,在第三關(guān)中會詳細介紹。

          方法重載(Overload)

          如果有兩個方法的方法名相同,但參數(shù)不一致,那么可以說一個方法是另一個方法的重載。方法重載規(guī)則如下:

          • 被重載的方法必須改變參數(shù)列表(參數(shù)個數(shù)或類型或順序不一樣)
          • 被重載的方法可以改變返回類型
          • 被重載的方法可以改變訪問修飾符
          • 被重載的方法可以聲明新的或更廣的檢查異常
          • 方法能夠在同一個類中或者在一個子類中被重載
          • 無法以返回值類型作為重載函數(shù)的區(qū)分標準

          重載可以通常理解為完成同一個事情的方法名相同,但是參數(shù)列表不同其他條件也可能不同。一個簡單的方法重載的例子,類E3中的add()方法就是一個重載方法。

          class E3{
              public int add(int a,int b){
                  return a+b;
              }
              public double add(double a,double b) {
                  return a+b;
              }
              public int add(int a,int b,int c) {
                  return a+b+c;
              }
          }

          方法重寫和方法重載的區(qū)別

          方法重寫和方法重載名稱上容易混淆,但內(nèi)容上有很大區(qū)別,下面用一個表格列出其中區(qū)別:

          區(qū)別點方法重寫方法重載
          結(jié)構(gòu)上垂直結(jié)構(gòu),是一種父子類之間的關(guān)系水平結(jié)構(gòu),是一種同類之間關(guān)系
          參數(shù)列表不可以修改可以修改
          訪問修飾符子類的訪問修飾符范圍必須大于等于父類訪問修飾符范圍可以修改
          拋出異常子類方法異常必須是父類方法異常或父類方法異常子異常可以修改

          繼承與修飾符

          Java修飾符的作用就是對類或類成員進行修飾或限制,每個修飾符都有自己的作用,而在繼承中可能有些特殊修飾符使得被修飾的屬性或方法不能被繼承,或者繼承需要一些其他的條件,下面就詳細介紹在繼承中一些修飾符的作用和特性。

          Java語言提供了很多修飾符,修飾符用來定義類、方法或者變量,通常放在語句的最前端。主要分為以下兩類:

          • 訪問修飾符
          • 非訪問修飾符

          這里訪問修飾符主要講解public,protected,default,private四種訪問控制修飾符。非訪問修飾符這里就介紹static修飾符,final修飾符和abstract修飾符。

          訪問修飾符

          public,protected,default(無修飾詞),private修飾符是面向?qū)ο笾蟹浅V匾闹R點,而在繼承中也需要懂得各種修飾符使用規(guī)則。

          首先我們都知道不同的關(guān)鍵字作用域不同,四種關(guān)鍵字的作用域如下:


          同一個類同一個包不同包子類不同包非子類
          private?


          default??

          protect???
          public????
          1. private:Java語言中對訪問權(quán)限限制的最窄的修飾符,一般稱之為“私有的”。被其修飾的屬性以及方法只能被該類的對象訪問,其子類不能訪問,更不能允許跨包訪問。

          2. default:(也有稱friendly)即不加任何訪問修飾符,通常稱為“默認訪問權(quán)限“或者“包訪問權(quán)限”。該模式下,只允許在同一個包中進行訪問。

          3. protected:介于public 和 private 之間的一種訪問修飾符,一般稱之為“保護訪問權(quán)限”。被其修飾的屬性以及方法只能被類本身的方法及子類訪問,即使子類在不同的包中也可以訪問。

          4. public:Java語言中訪問限制最寬的修飾符,一般稱之為“公共的”。被其修飾的類、屬性以及方法不僅可以跨類訪問,而且允許跨包訪問。

          Java 子類重寫繼承的方法時,不可以降低方法的訪問權(quán)限子類繼承父類的訪問修飾符作用域不能比父類小,也就是更加開放,假如父類是protected修飾的,其子類只能是protected或者public,絕對不能是default(默認的訪問范圍)或者private。所以在繼承中需要重寫的方法不能使用private修飾詞修飾。

          如果還是不太清楚可以看幾個小案例就很容易搞懂,寫一個A1類中用四種修飾詞實現(xiàn)四個方法,用子類A2繼承A1,重寫A1方法時候你就會發(fā)現(xiàn)父類私有方法不能重寫,非私有方法重寫使用的修飾符作用域不能變小(大于等于)。


          正確的案例應(yīng)該為:

          class A1 {
              private void doA(){ }
              void doB(){}//default
              protected void doC(){}
              public void doD(){}
          }
          class A2 extends A1{

              @Override
              public void doB() { }//繼承子類重寫的方法訪問修飾符權(quán)限可擴大

              @Override
              protected void doC() { }//繼承子類重寫的方法訪問修飾符權(quán)限可和父類一致

              @Override
              public void doD() { }//不可用protected或者default修飾
          }

          還要注意的是,繼承當中子類拋出的異常必須是父類拋出的異常或父類拋出異常的子異常。下面的一個案例四種方法測試可以發(fā)現(xiàn)子類方法的異常不可大于父類對應(yīng)方法拋出異常的范圍。


          正確的案例應(yīng)該為:

          class B1{
              public void doA() throws Exception{}
              public void doB() throws Exception{}
              public void doC() throws IOException{}
              public void doD() throws IOException{}
          }
          class B2 extends B1{
              //異常范圍和父類可以一致
              @Override
              public void doA() throws Exception { }
              //異常范圍可以比父類更小
              @Override
              public void doB() throws IOException { }
              //異常范圍 不可以比父類范圍更大
              @Override
              public void doC() throws IOException { }//不可拋出Exception等比IOException更大的異常
              @Override
              public void doD() throws IOException { }
          }

          非訪問修飾符

          訪問修飾符用來控制訪問權(quán)限,而非訪問修飾符每個都有各自的作用,下面針對static、final、abstract修飾符進行介紹。

          static 修飾符

          static 翻譯為“靜態(tài)的”,能夠與變量,方法和類一起使用,稱為靜態(tài)變量,靜態(tài)方法(也稱為類變量、類方法)。如果在一個類中使用static修飾變量或者方法的話,它們可以直接通過類訪問,不需要創(chuàng)建一個類的對象來訪問成員。

          我們在設(shè)計類的時候可能會使用靜態(tài)方法,有很多工具類比如MathArrays等類里面就寫了很多靜態(tài)方法。static修飾符的規(guī)則很多,這里僅僅介紹和Java繼承相關(guān)用法的規(guī)則:

          • 構(gòu)造方法不允許聲明為 static 的。
          • 靜態(tài)方法中不存在當前對象,因而不能使用 this,當然也不能使用 super。
          • 靜態(tài)方法不能被非靜態(tài)方法重寫(覆蓋)
          • 靜態(tài)方法能被靜態(tài)方法重寫(覆蓋)

          可以看以下的案例證明上述規(guī)則:


          源代碼為:

          class C1{
              public  int a;
              public C1(){}
             // public static C1(){}// 構(gòu)造方法不允許被聲明為static
              public static void doA() {}
              public static void doB() {}
          }
          class C2 extends C1{
              public static  void doC()//靜態(tài)方法中不存在當前對象,因而不能使用this和super。
              
          {
                  //System.out.println(super.a);
              }
              public static void doA(){}//靜態(tài)方法能被靜態(tài)方法重寫
             // public void doB(){}//靜態(tài)方法不能被非靜態(tài)方法重寫
          }

          final修飾符

          final變量:

          • final 表示"最后的、最終的"含義,變量一旦賦值后,不能被重新賦值。被 final 修飾的實例變量必須顯式指定初始值(即不能只聲明)。final 修飾符通常和 static 修飾符一起使用來創(chuàng)建類常量。

          final 方法:

          • 父類中的 final 方法可以被子類繼承,但是不能被子類重寫。聲明 final 方法的主要目的是防止該方法的內(nèi)容被修改。

          final類:

          • final 類不能被繼承,沒有類能夠繼承 final 類的任何特性。

          所以無論是變量、方法還是類被final修飾之后,都有代表最終、最后的意思。內(nèi)容無法被修改。

          abstract 修飾符

          abstract 英文名為“抽象的”,主要用來修飾類和方法,稱為抽象類和抽象方法。

          抽象方法:有很多不同類的方法是相似的,但是具體內(nèi)容又不太一樣,所以我們只能抽取他的聲明,沒有具體的方法體,即抽象方法可以表達概念但無法具體實現(xiàn)。

          抽象類有抽象方法的類必須是抽象類,抽象類可以表達概念但是無法構(gòu)造實體的類。

          抽象類和抽象方法內(nèi)容和規(guī)則比較多。這里只提及一些和繼承有關(guān)的用法和規(guī)則:

          • 抽象類也是類,如果一個類繼承于抽象類,就不能繼承于其他的(類或抽象類)
          • 子類可以繼承于抽象類,但是一定要實現(xiàn)父類們所有abstract的方法。如果不能完全實現(xiàn),那么子類也必須被定義為抽象類
          • 只有實現(xiàn)父類的所有抽象方法,才能是完整類。

          比如我們可以這樣設(shè)計一個People抽象類以及一個抽象方法,在子類中具體完成:

          abstract class People{
              public abstract void sayHello();//抽象方法
          }
          class Chinese extends People{
              @Override
              public void sayHello() {//實現(xiàn)抽象方法
                  System.out.println("你好");
              }
          }
          class Japanese extends People{
              @Override
              public void sayHello() {//實現(xiàn)抽象方法
                  System.out.println("口你七哇");
              }
          }
          class American extends People{
              @Override
              public void sayHello() {//實現(xiàn)抽象方法
                  System.out.println("hello");
              }
          }

          Object類和轉(zhuǎn)型

          提到Java繼承,不得不提及所有類的根類:Object(java.lang.Object)類,如果一個類沒有顯式聲明它的父類(即沒有寫extends xx),那么默認這個類的父類就是Object類,任何類都可以使用Object類的方法,創(chuàng)建的類也可和Object進行向上、向下轉(zhuǎn)型,所以O(shè)bject類是掌握和理解繼承所必須的知識點。而Java向上和向下轉(zhuǎn)型在Java中運用很多,也是建立在繼承的基礎(chǔ)上,所以Java轉(zhuǎn)型也是掌握和理解繼承所必須的知識點。

          Object類概述

          1. Object是類層次結(jié)構(gòu)的根類,所有的類都隱式的繼承自O(shè)bject類。

          2. Java所有的對象都擁有Object默認方法

          3. Object類的構(gòu)造方法有一個,并且是無參構(gòu)造

          Object是java所有類的父類,是整個類繼承結(jié)構(gòu)的頂端,也是最抽象的一個類。像toString()、equals()、hashCode()、wait()、notify()、getClass()等都是Object的方法。你以后可能會經(jīng)常碰到,但其中遇到更多的就是toString()方法和equals()方法,我們經(jīng)常需要重寫這兩種方法滿足我們的使用需求。

          **toString()**方法表示返回該對象的字符串,由于各個對象構(gòu)造不同所以需要重寫,如果不重寫的話默認返回類名@hashCode格式。

          如果重寫toString()方法后直接調(diào)用toString()方法就可以返回我們自定義的該類轉(zhuǎn)成字符串類型的內(nèi)容輸出,而不需要每次都手動的拼湊成字符串內(nèi)容輸出,大大簡化輸出操作。

          **equals()方法主要比較兩個對象是否相等,因為對象的相等不一定非要嚴格要求兩個對象地址上的相同,有時內(nèi)容上的相同我們就會認為它相等,比如String 類就重寫了euqals()**方法,通過字符串的內(nèi)容比較是否相等。


          向上轉(zhuǎn)型

          向上轉(zhuǎn)型 : 通過子類對象(小范圍)實例化父類對象(大范圍),這種屬于自動轉(zhuǎn)換。用一張圖就能很好地表示向上轉(zhuǎn)型的邏輯:


          父類引用變量指向子類對象后,只能使用父類已聲明的方法,但方法如果被重寫會執(zhí)行子類的方法,如果方法未被重寫那么將執(zhí)行父類的方法。

          向下轉(zhuǎn)型

          向下轉(zhuǎn)型 : 通過父類對象(大范圍)實例化子類對象(小范圍),在書寫上父類對象需要加括號()強制轉(zhuǎn)換為子類類型。但父類引用變量實際引用必須是子類對象才能成功轉(zhuǎn)型,這里也用一張圖就能很好表示向上轉(zhuǎn)型的邏輯:


          子類引用變量指向父類引用變量指向的對象后(一個Son()對象),就完成向下轉(zhuǎn)型,就可以調(diào)用一些子類特有而父類沒有的方法 。

          在這里寫一個向上轉(zhuǎn)型和向下轉(zhuǎn)型的案例:

          Object object=new Integer(666);//向上轉(zhuǎn)型

          Integer i=(Integer)object;//向下轉(zhuǎn)型Object->Integer,object的實質(zhì)還是指向Integer

          String str=(String)object;//錯誤的向下轉(zhuǎn)型,雖然編譯器不會報錯但是運行會報錯

          子父類初始化順序

          在Java繼承中,父子類初始化先后順序為:

          1. 父類中靜態(tài)成員變量和靜態(tài)代碼塊

          2. 子類中靜態(tài)成員變量和靜態(tài)代碼塊

          3. 父類中普通成員變量和代碼塊,父類的構(gòu)造函數(shù)

          4. 子類中普通成員變量和代碼塊,子類的構(gòu)造函數(shù)

          總的來說,就是靜態(tài)>非靜態(tài),父類>子類,非構(gòu)造函數(shù)>構(gòu)造函數(shù)。同一類別(例如普通變量和普通代碼塊)成員變量和代碼塊執(zhí)行從前到后,需要注意邏輯。

          這個也不難理解,靜態(tài)變量也稱類變量,可以看成一個全局變量,靜態(tài)成員變量和靜態(tài)代碼塊在類加載的時候就初始化,而非靜態(tài)變量和代碼塊在對象創(chuàng)建的時候初始化。所以靜態(tài)快于非靜態(tài)初始化。

          而在創(chuàng)建子類對象的時候需要先創(chuàng)建父類對象,所以父類優(yōu)先于子類。

          而在調(diào)用構(gòu)造函數(shù)的時候,是對成員變量進行一些初始化操作,所以普通成員變量和代碼塊優(yōu)于構(gòu)造函數(shù)執(zhí)行。

          至于更深層次為什么這個順序,就要更深入了解JVM執(zhí)行流程啦。下面一個測試代碼為:

          class Father{
              public Father() {
                  System.out.println(++b1+"父類構(gòu)造方法");
              }//父類構(gòu)造方法 第四
              static int a1=0;//父類static 第一 注意順序
              static {
                  System.out.println(++a1+"父類static");
              }
              int b1=a1;//父類成員變量和代碼塊 第三
              {
                  System.out.println(++b1+"父類代碼塊");
              }
          }
          class Son extends Father{
              public Son() {
                  System.out.println(++b2+"子類構(gòu)造方法");
              }//子類構(gòu)造方法 第六
              static {//子類static第二步
                  System.out.println(++a1+"子類static");
              }
              int b2=b1;//子類成員變量和代碼塊 第五
              {
                  System.out.println(++b2 + "子類代碼塊");
              }
          }
          public class test9 {
              public static void main(String[] args) {
                  Son son=new Son();
              }
          }

          執(zhí)行結(jié)果:


          結(jié)語

          好啦,本次繼承就介紹到這里啦,Java面向?qū)ο笕筇卣髦焕^承——優(yōu)秀的你已經(jīng)掌握。再看看Java面向?qū)ο笕筇匦裕悍庋b、繼承、多態(tài)。最后問你能大致了解它們的特征嘛?

          封裝:是對類的封裝,封裝是對類的屬性和方法進行封裝,只對外暴露方法而不暴露具體使用細節(jié),所以我們一般設(shè)計類成員變量時候大多設(shè)為私有而通過一些get、set方法去讀寫。

          繼承:子類繼承父類,即“子承父業(yè)”,子類擁有父類除私有的所有屬性和方法,自己還能在此基礎(chǔ)上拓展自己新的屬性和方法。主要目的是復(fù)用代碼

          多態(tài):多態(tài)是同一個行為具有多個不同表現(xiàn)形式或形態(tài)的能力。即一個父類可能有若干子類,各子類實現(xiàn)父類方法有多種多樣,調(diào)用父類方法時,父類引用變量指向不同子類實例而執(zhí)行不同方法,這就是所謂父類方法是多態(tài)的。

          最后送你一張圖捋一捋其中的關(guān)系吧。


          近期精彩:
          硬核!手寫一個優(yōu)先隊列
          圖解|雙軸快排分析
          MongoDB助力一個物流訂單系統(tǒng)
          面試官:什么是緩存穿透、緩存雪崩、緩存擊穿?
          16張圖帶你徹底搞懂基數(shù)排序
          8張圖帶你分析Redis與MySQL數(shù)據(jù)一致性問題

          好了,不說了,咱們下次再見!本文同時收錄在回車課堂!歡迎查看。最后bigsai請你們在看、關(guān)注支持一波原創(chuàng)小博主,謝謝!


          喜歡就點個在看再走吧

          瀏覽 44
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费高清无码视频 | 日批免费观看 | 肏逼毛片 | A V视频在线播放 | 国产菊门残忍扩张视频 |