1. 程式人生 > 實用技巧 >Java基礎之:抽象類

Java基礎之:抽象類

Java基礎之:抽象類

當父類的某一些方法並不知道具體實現內容,但需要繼承給子類讓其在子類中實現時,就可以將這些方法宣告為抽象方法,而有抽象方法的類就叫做抽象類。使用abstract來宣告。

簡單案例

package com.atguigu.abstract_;
​
public class AbstractTest01 {
​
    public static void main(String[] args) {
        
        Cat cat = new Cat("小花貓");
        cat.eat();
    }
​
}
​
abstract class Animal { //抽象類
    private String name;
​
    public Animal(String name) {
        super();
        this.name = name;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
    
    //eat , 抽象方法
    public abstract void eat();
}
​
//解讀
//1. 當一個類繼承了抽象類,就要把抽象類的所有抽象方法實現
//2. 所謂方法實現,指的是 把方法體寫出, 方法體是空,也可以.
class Cat extends Animal { 
    public Cat(String name) {
        super(name);
        // TODO Auto-generated constructor stub
    }
    
    public void eat() {
        System.out.println(getName() +  " 愛吃 <・)))><<");
    }
}

語法說明

抽象類:

   訪問修飾符 abstract class 類名{}

抽象方法:(抽象方法沒有方法體!)

   訪問修飾符 abstract 返回型別 方法名(引數列表);

抽象類使用細節

  1. 抽象類不可以被例項化

  2. 抽象類不一定必須包含abstract方法,即抽象類不一定有抽象方法

  3. 一旦包含了抽象方法,則這個類必須宣告為abstract抽象類

  4. abstract只能修飾類與方法,不可以修飾其他內容

  5. 抽象類可以有任意成員(因為抽象類還是類),比如:非抽象方法、構造器、靜態屬性等等

  6. 抽象方法不能有主體,即不能實現

  7. 如果一個類繼承了抽象類,則它必須實現抽象類的所有抽象方法,除非它自己也宣告為abstract類。

  8. 抽象方法不能使用privatefinalstatic來修飾,因為這些關鍵字都是和重寫相違背的。

package class_abstract;
public class Abstract_Test {
​
    public static void main(String[] args) {
        //細節1:對於抽象類而言,不可以宣告例項
//      A a = new A(); //報錯:Cannot instantiate the type A
        
        //細節5.但當抽象類的子類,建立了例項之後,仍然可以使用A類中滿足訪問許可權的所有東西。(繼承的特點)
        B b = new B();
        b.setName("小范");
        b.getName();
    }
}
​
​
abstract class A{   //細節3:由於A類中的hi()方法是被abstract修飾的(即抽象方法),所以A類也必須宣告為抽象的
    
    private String name;    //細節5:抽象類中也可以有非抽象的東西,抽象類本質也是一個類
    
    public String getName() {   
        return name;
    }
    public void setName(String name) {  
        this.name = name;
    }
​
    public abstract void hi();  //細節6:對於抽象類而言,不可以有{},即方法體
    
    public abstract void hello();
}
​
class B extends A{
    //細節7:只要繼承抽象類,則必須實現抽象類中所有的抽象方法。否則編譯器報錯:
    //The type B must implement the inherited abstract method A.hi()....
    
    @Override
    public void hi() {
        System.out.println("B----hi");
    }
    
    @Override
    public void hello() {
        System.out.println("B-----hello");
    }
}
​
abstract class C extends A{
    /*  細節7:
     * 若繼承了抽象類,但子類本身也是一個抽象類,那麼可以不用實現抽象類中的抽象方法
     * 因為對於抽象子類C而言,是允許抽象方法存在的。
     * 一旦有非抽象子類D,繼承了子類C,那麼D同樣需要實現抽象子類C中的所有抽象方法
     */
​
//  private abstract int age; //abstract 只能修飾方法與類,不可以修飾屬性和其他的
    
    /*  細節8:
     * 對於抽象方法而言,不可以使用 private 或 final 修飾
     * 因為,抽象的本質就規範化父類的東西,讓子類去實現父類已經寫好的抽象方法
     * 而用private與final修飾的方法都不可以被子類繼承,這就與抽象的作用衝突了
     */
//  private abstract void m1();
    
    /*  細節8:
     * 對於抽象方法而言,也不可以使用 static 修飾
     * 因為,static修飾之後,方法不再是成員方法了,而是類方法。
     * 而子類是無法繼承類方法的,所以static與abstract的作用也是產生衝突了。
     */
//  public static abstract void m2();
}
​
class D extends C{
    @Override
    public void hi() {
        System.out.println("D-----hi");
    }
​
    @Override
    public void hello() {
        System.out.println("D-----hello");
    }
}

多型在抽象類中的實現

簡單案例

package class_abstract;
​
public class AbstractPolyArray {
​
    public static void main(String[] args) {
        //抽象類不可以例項化,但可以使用多型陣列
        Animal[] animal = new Animal[2];
        
        animal[0] = new Dog("小黑狗");
        animal[1] = new Cat("小花貓");
        
        //多型陣列的使用
        for (int i = 0; i < animal.length; i++) {
            show(animal[i]);
        }
    }
    
    //這裡不用擔心會傳入一個Animal型別的例項,因為Animal不能例項化
    //編譯器不會通過,所以只會傳入Animal的子類例項
    public static void show(Animal a) {
        a.eat();    //多型的使用
        
        if(a instanceof Dog) {
            ((Dog)a).watch();
        }else if(a instanceof Cat) {
            ((Cat)a).catchMouse();
        }
    }
}
​
abstract class Animal{
    private String name;
​
    public Animal(String name) {
        super();
        this.name = name;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
    
    //動物都有eat的動作,但我們並不知道每一個動物具體怎麼樣eat
    //所以這裡通過抽象提供了eat方法,需要子類來實現
    public abstract void eat();
}
​
class Dog extends Animal{
    public Dog(String name) {
        super(name);
    }
​
    @Override
    public void eat() {
        System.out.println(getName() + "啃骨頭......");
    }
    
    public void watch() {
        System.out.println(getName() + "守家.....");
    }
}
​
class Cat extends Animal{
    public Cat(String name) {
        super(name);
    }
​
    @Override
    public void eat() {
        System.out.println(getName() + "吃魚......");
    }
    
    public void catchMouse(){
        System.out.println(getName() + "抓老鼠.....");
    }
}

程式輸出

小黑狗啃骨頭......

小黑狗守家.....

小花貓吃魚......

小花貓抓老鼠.....

抽象類簡單應用案例

編寫一個Employee類,宣告為抽象類,包含如下三個屬性:name,id,salary。

  • 提供必要的構造器和抽象方法:work()。

  • 請使用繼承的思想,設計CommonEmployee類和Manager類,

  • 對於Manager類來說,他既是員工,還具有獎金(bonus)的屬性。

  • 要求類中提供必要的方法進行屬性訪問,實現work(),提示 "經理/普通員工 名字 工作中...."

package class_abstract;
public class Abstract_ClassWork {
    public static void main(String[] args) {
        Employee[] em = new Employee[2];
        em[0] = new Manager("小黃", 1001, 5000, 1120);
        em[1] = new CommonEmployee("小范", 1002, 3000);
        
        System.out.println(em[0].toString());
        em[0].work();
        
        System.out.println(em[1].toString());
        em[1].work();
    }
}
​
abstract class Employee{
    private String name;
    private int id;
    private double salary;
    public Employee(String name, int id, double salary) {
        this.name = name;
        this.id = id;
        this.salary = salary;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    
    public abstract void work();
    @Override
    public String toString() {
        return "name=" + name + ", id=" + id + ", salary=" + salary;
    }
}
​
//對於Manager類來說,他既是員工,還具有獎金(bonus)的屬性。
class Manager extends Employee{
    private double bonus;
    
    public Manager(String name, int id, double salary, double bonus) {
        super(name, id, salary);
        this.bonus = bonus;
    }
​
    public double getBonus() {
        return bonus;
    }
​
    public void setBonus(double bonus) {
        this.bonus = bonus;
    }
​
    //"經理/普通員工 名字 工作中...."  
    @Override
    public void work() {
        System.out.println("經理" + getName() + "工作中......");
    }
    
    @Override
    public String toString() {
        return super.toString() + ",bonus =" + bonus;
    }
}
​
class CommonEmployee extends Employee{
    public CommonEmployee(String name, int id, double salary) {
        super(name, id, salary);
        // TODO Auto-generated constructor stub
    }
​
    //"經理/普通員工 名字 工作中...."  
    @Override
    public void work() {
        System.out.println("普通員工" + getName() + "工作中......");
    }
}

程式輸出

name=小黃, id=1001, salary=5000.0,bonus =1120.0

經理小黃工作中......

name=小范, id=1002, salary=3000.0

普通員工小范工作中......

抽象類最佳實踐-模板設計模式

抽象類體現的就是一種模板模式的設計,抽象類作為多個子類的通用模板,子類在抽象類的基礎上進行擴充套件、改造,但子類總體上會保留抽象類的行為方式。

1) 當功能內部一部分實現是確定,一部分實現是不確定的。這時可以把不確定的部分暴露出去,讓子類去實現。

2) 編寫一個抽象父類,父類提供了多個子類的通用方法,並把一個或多個方法留給其子類實現,就是一種模板模式

簡單案例

1) 設計一個抽象類(Template),能完成如下功能:

2) 編寫方法caleTime() ,可以計算某段程式碼的耗時時間

3) 編寫抽象方法code()

4) 編寫一個子類Sub,繼承抽象類Template,並實現code方法。

package class_abstract;
public class Abstract_Template {
    public static void main(String[] args) {
        Template sub = new Sub();
        sub.caleTimes(); //實際是呼叫了Template中的caleTimes方法
        
        Template subStringB = new SubStringB();
        subStringB.caleTimes();
        
        //這裡可以看到 StringBuffer在拼接字串時,遠遠優於String拼接的效率
    }
}
​
abstract class Template{ //抽象類
    public abstract void code(); //抽象方法
    public void caleTimes(){ //  統計耗時多久是確定 
        //統計當前時間距離 1970-1-1 0:0:0 的時間差,單位ms
        long start = System.currentTimeMillis();
        code(); //這裡的code在呼叫時,就是指向子類中已經重寫實現了的code
        long end = System.currentTimeMillis();
        System.out.println("耗時:"+(end-start));
    }
}
​
class Sub extends Template{
    
    @Override
    public void code() {
        String x = "";
        for(int i = 0;i < 10000 ; i++) {    //拼接1W個hello 看處理時間
            x += "hello" + i;
        }
    }
}
​
class SubStringB extends Template{
    @Override
    public void code() {
        StringBuffer stringBuffer = new StringBuffer();
        for(int i = 0;i < 10000 ; i++) {    //拼接1W個hello 看處理時間
            stringBuffer.append("hello" + i);
        }
    }
}

程式輸出

耗時:606

耗時:2