Java 抽象類詳解


  在《Java中的抽象方法和接口》中,介紹了抽象方法與接口,以及做了簡單的比較。

  這里我想詳細探討下抽象類。

  

  一、抽象類的定義

  被關鍵字“abstract”修飾的類,為抽象類。(而且,abxtract只能修飾類和方法)

  下面顯示了一個最簡單的空抽象類

public abstract class AbstractClass {
    public static void main(String[] args) {
        AbstractClass abstractClass=new AbstractClass();
    }
}

  當對這個空的抽象類進行實例化時,編譯器會報錯:

  'AbstractClass' is abstract; cannot be instantiated'

   現在對這個抽象類進行擴展,添加屬性和方法:

public abstract class AbstractClass {
    public int constInt = 5;

    /*重載method()*/
    public void method() { }

    //沒有編譯錯誤
    public abstract void method(int a);

    public static void main(String[] args) {
        AbstractClass abstractClass=new AbstractClass() {
            @Override
            public void method(int a) {
                System.out.println("實例化抽象類");
            }
        };
System.out.println(abstractClass.constInt); abstractClass.method(
5); } } //運行結果 /*   5   實例化抽象類 */

   在這個抽象類中我添加了一個實例屬性,一個抽象方法,以及該抽象方法的重載實例方法,這些都是合法的。

  在main方法中,直接對抽象類通過new操作符進行實例化,出乎意料的是,IDE直接提示了這種操作——這里生成了一個匿名內部類(Anonymous Inner)。

  下面是關於匿名內部類的知識點:

  • 一種特殊的局部內部類,沒有類名,沒有class關鍵字,也不能使用extends和implements等關鍵字修飾。
  • 匿名內部類不能是抽象類(即不能擁有抽象方法),必須實現它的抽象父類或者接口的所有抽象方法。 
  • 因為沒有類名,所以匿名內部類只能被使用一次,通常用來簡化代碼編寫。
  • 使用匿名內部類的前提條件:繼承一個父類或者實現一個接口。

  第四點和第一點略有矛盾,其實就是使用的時候通過new操作符指定要繼承的父類或要實現的接口(事實上,new是直接調用某個構造函數。new真的是個牛逼的操作符啊),然后再通過直接定義類體(類體中實現某些方法),構建新類的實例。

  所以,在我上面的代碼中,生成了一個繼承AbstractClass的新匿名內部類的實例,這個類中實現了父類的抽象method方法,獲得的實例我們賦給了abstractClass,並通過實例調用了新的method(int 5)方法。

 

   二、抽象類與抽象方法

  抽象方法只有方法聲明,沒有方法體的方法。它將由子類(可能是抽象類,也可能是非抽象類)進行實現。

  通過上面空的抽象類的方法可知,擁有一個抽象方法並不是構建一個抽象類充分必要條件。

  那么,一個有抽象方法的普通類是合法的嗎?大概率是不合法的,因為如果這樣的設計是合法的又有什么意義呢?

  實際上,如果我們在一個非抽象類中定義一個抽象方法,IDE會提示:

  “Abstract method in non-abstract class”

  而如果我們一定要運行如下所示的一段錯誤代碼:

public class AbstractTest {

    public abstract void test();

    public static void main(String[] args) {
        
    }
}

  編譯器的報錯信息為:

  Error:java: AbstractTest不是抽象的, 並且未覆蓋biguo.classConstruct.AbstractTest中的抽象方法test()

  所以抽象方法只能存在於抽象類中。

 

  三、抽象方法可以是static的嗎?

  在進行下面的測試時,突然想到一個很有意思的問題,抽象類中的抽象方法可以是靜態的嗎?

  先下結論:NO,這是的修飾符組合是不合法的——Error: java: 非法的修飾符組合: abstract和static

public abstract class AbstractTest {
 //非法的修飾符組合
    public static abstract void test();

    public static void main(String[] args) {
        
    }
}

  static成員方法意味着,不需要實例化可以使用(在類的內部或者通過類訪問)。但是呢,也可以通過實例進行訪問,這樣做不會報錯,但會得到IDE的警告,因為違反了static的設計語義;

  abstract方法意味着沒有方法體(區別下“有方法體,但方法體為空”),即只是一個方法聲明,需要被子類去實現。

  我們先要清楚,抽象類中是可以擁有static方法的,比如,我們把main方法放在一個抽象類中,程序是可以由此運行的。

  既然這樣,一個“static abstract”組合的方法,對這個抽象類完全沒有存在的意義了!!因為它沒有方法體,無法通過類來使用。

  在StackOverFlow上有探討,說完全可以允許這樣的“static abstract”方法,因為在非抽象子類中,實現這個抽象方法后的子方法仍然是static,是子類的類方法。

  這樣的說法有一點點意義,但是它仍然無法解決的是,“static abstract”方法對父類中“static”語義的違背

 

  static方法可以被子類重寫嗎?

  答案是:static方法不能被子類重寫。(涉及到重寫的定義)

  但是!我們確實又可以在子類中重新定義一個與父類static方法一模一樣的方法,如下的test()方法。

package biguo.classConstruct;

public class AbstractTest {

    public static  void test(){
        System.out.println("This is AbstractTest's static test!");
    }

    public static  void print(){
        System.out.println("This is AbstractTest's static print!");
    }

    public static void main(String[] args) {
        AbstractTest.test();
        AbstractTestSon.test();
        AbstractTestSon.print();
        System.out.println();

        AbstractTestSon abstractTestSon=new AbstractTestSon();
        abstractTestSon.print();
        abstractTestSon.test();
    }
}

class AbstractTestSon extends AbstractTest{
    public static void test(){
        System.out.println("This is AbstractTest-Son's static test!");
    }
}


//輸出
/*
This is AbstractTest's static test!
This is AbstractTest-Son's static test!
This is AbstractTest's static print!

This is AbstractTest's static print!
This is AbstractTest-Son's static test!
*/ 

  通過輸出結果可以看到:父類中未被子類重寫的static方法是可以被子類及其對象訪問到的,但是被子類重寫過的方法,則子類及其對象只能調用自己的方法了。

 

  為什么這種情況不能叫做子類“重寫”了父類的方法呢,而是叫”方法隱藏(method hidding)“?

  在www.geeksforgeeks.org的這篇文章中,對此做了解釋:

  因為,方法重寫(Overriding)是OOP語言的一種特性,在Java中,它是實現“運行時多態”一種方式!!而父類子類的相同簽名的同名方法的情況,there won’t be any run-time polymorphism。

  還篇文章(”5 Java concepts explained: Overloading, overriding, shadowing, hiding, and obscuring“),解釋了這些概念的差別,但是卻沒有提到上述的method hidding情況。(所以我以為隱藏只是繼承關系的類中變量之間的行為)。

 

  四、抽象類的繼承

  從抽象父類派生的子類如果不能實現所有的抽象方法,它也必須聲明為抽象的。

  抽象類可以定義構造方法,且能被子類在構造方法中調用。

  一個非抽象類的子類,可以聲明為抽象類。

 

  五、final與abstract的矛盾

  final關鍵字可以修飾類、方法、變量。

  final修飾的類不能被派生;final修飾的方法,禁止子類重寫。

  所以我們可以看出,final和abstract就是冰火不容的~

 


免責聲明!

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



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