原來你是這樣的JAVA[03]-繼承、多態、抽象類

1、繼承


Java中的繼承使用關鍵字extends ,跟C#的語法略有差異。
java

1.子類構造器


java會自動在子類的構造器中插入對父類構造器的調用,也就是說在子類能夠訪問父類以前已經完成了父類的初始化。
若是想調用帶參數的父類構造器,應該使用super關鍵字。git

/**
  * @author 陳敬
  * @date 18/1/17
  */
public class Product {
     private String name;

    public Product(String name) {
         this.name = name;
         System.out.println("[Product constructor]");
     }
}

public class Bread extends Product {
     private int price;

    public Bread(String name, int price) {
         super(name);//調用父類構造器 this.price = price;
         System.out.println("[Bread constructor]");
     }
}

咱們建立一個Bread類的實例,看看調用順序。github

@Test
public void testConstructor(){
     Bread bread=new Bread("毛毛蟲麪包",10);
}

打印結果:
[Product constructor]
[Bread constructor]編輯器

2.調用父類方法


子類是不能直接訪問到父類的私有域的,若是想訪問只能藉助父類公開的get訪問器。子類調用父類中的方法也須要使用super關鍵字。ide

public class Product {
    private String name;

    public String getName() {
        return name;
    }

    public Product(String name) {
        this.name = name;
    }
}
public class Bread extends Product {
    public Bread(String name) {
        super(name);
    }

    public void display(){
        System.out.println(getName());
    }
}

而後寫個單元測試:單元測試

   @Test
    public void testPrivate(){
        Bread bread=new Bread("毛毛蟲麪包");
        bread.display();//毛毛蟲麪包
    }

須要說明一點,super並非一個對象的引用,不能將super賦值給變量,它只是一個特殊的關鍵字,告訴編輯器要調用父類中的方法。測試

3.關於重載


若是父類中存在重載方法,子類又進行了重載,會覆蓋父類中的方法嗎?實際上,父類和子類中的方法均可以正常重載,不會被覆蓋。
首先在父類Product中添加方法getDescription():this

public class Product {
     ……

    public String getDescription() {
         return "[Product]name="+name;
     }
}

而後在子類中重載該方法:spa

public class Bread extends Product {
     ……

    public String getDescription(String storeName) {
        return "[Bread]storename="+storeName;
     }
}

增長一個單元測試:code

public class ExtendClassTests {
     @Test
     public void testOverload(){
         Bread bread=new Bread("豆沙麪包",9);
         System.out.println(bread.getDescription());
         System.out.println(bread.getDescription("味多美"));
     }
}

輸出:
[Product]name=豆沙麪包
[Bread]storename=味多美

4.繼承準則


繼承準則:儘可能少用繼承。通常用繼承表達行爲間的差別,用組合表示狀態上的變化。

2、多態

 

1.變量多態


在Java中對象變量是多態的,一個Product變量能夠引用Product對象,也能夠引用一個Product子類的對象。

@Test
public void testParent(){
Product product=new Bread("毛毛蟲麪包",10);
product.display();

//強制類型轉換
if(product instanceof Bread){
Bread brand=(Bread)product;
brand.display("味多美");
}
}

因爲Bread實例向上轉型爲Product類型,因此不能再調用Bread.getDescription(String storeName)方法。
若是須要將父類強制轉換爲子類時,要先經過instanceof檢測對象類型,咱們最好儘可能避免使用強制類型轉換。

2.動態綁定


所謂動態綁定,就是在運行時根據對象的類型決定要調用的方法。在java中,動態綁定是默認行爲,不須要添加額外的關鍵字實現多態。

再寫個demo來看一下,在父類和子類中重載了display方法。

public class Product {
     private String name;

    public Product(String name) {
         this.name = name;
     }

    public void display() {
         System.out.println("[Product]getDescription()");
     }
}

public class Bread extends Product {
     private int price;

    public Bread(String name, int price) {
         super(name);
         this.price = price;
     }

    @Override
     public void display() {
         System.out.println("[Bread]getDescription()");
     }
     public void display(String storeName) {
         System.out.println("[Bread]getDescription(String storeName)");
     }
}

添加單元測試:

@Test
public void dynamicBind(){
     Product product=new Product("product");
     product.display();  //[Product]getDescription()

    Bread bread=new Bread("毛毛蟲",9);
     bread.display();  //[Bread]getDescription()
     bread.display("maimai");  //[Bread]getDescription(String storeName)

    Product product1=bread;
     product1.display();  //[Bread]getDescription()
}

 

虛擬機爲每一個類建立一個方法表,列出全部方法的簽名和實際調用的方法。這樣一來,當動態調用方法的時候,只須要查找方法表就能快速的找到真正調用的方法了。
Product:
     display()->Product.display()
Bread:
     display()->Bread.display()
     display(String name)->Bread.display(String name)
 
完整源碼參見:https://github.com/cathychen00/cathyjava     /_08_extend

3、抽象類

定義抽象方法用用abstract關鍵字,它僅有聲明而沒有方法體。
包含抽象方法的類叫作抽象類,若是一個類包含一個或多個抽象方法,那麼必須被定義爲抽象類。
若是一個類從抽象類繼承,那麼必須爲抽象類中的全部抽象方法提供實現,不然該類也必須被定義爲抽象類。
看一個場景:咱們有一些定時任務,要進行的工做流程相似,只有具體一部分細節內容不一樣。咱們能夠定義一個抽象基類BaseJob,再不一樣的部分封裝爲抽象方法,具體的實如今子類中進行。

public abstract class BaseJob {
     public void run(){
         System.out.println("==START "+getDescription()+"==");
         String lastJobId=getLastJobId();
         execute(lastJobId);
         writeLog();
         System.out.println("==END "+getDescription()+"==");
     }

    protected abstract String getDescription(); protected abstract void execute(String jobId); private void writeLog() {
         System.out.println("write log to DB");
     }

    private String getLastJobId() {
         return "job1221";
     }
}
public class ArticleJob extends BaseJob {
     @Override
     protected String getDescription() {
         return "抓取文章任務";
     }

    @Override
     protected void execute(String jobId) {
         System.out.println("抓取站點新聞文章 jobid="+jobId);
     }

    public static void main(String[] args) {
         BaseJob articleJob=new ArticleJob();
         articleJob.run();
     }
}

建立單元測試,調用ArticleJob看看。

@Test
public void articleJob(){
     BaseJob articleJob=new ArticleJob();
     articleJob.run();
}

運行結果:

==START 抓取文章任務==
抓取站點新聞文章 jobid=job1221
write log to DB
==END 抓取文章任務==

當再次添加符合該流程的定時任務時,只須要新建一個類,實現BaseJob就能夠了。

完整例子:https://github.com/cathychen00/cathyjava /09_abstract

相關文章
相關標籤/搜索