重拾JavaSE基礎——抽象類、接口、代碼塊、final和枚舉

今天繼續回顧Java基礎,有些東西用得很少,你們看看知道語法就好前端

主要內容

  1. 抽象類java

    1. 抽象方法程序員

      1. 抽象方法的寫法
      2. 抽象方法是否能夠私有化
    2. 抽象類的特徵面試

      1. 抽象類有無構造器,可否實例化對象
      2. 抽象類的結構
    3. 抽象類的核心意義數據庫

      1. 被繼承
      2. 部分實現,部分抽象
  2. 接口設計模式

    1. 爲何要使用接口
    2. 接口和實現類的格式
    3. 接口的結構架構

      1. JDK1.8以前
      2. JDK1.8以後新增
    4. 接口VS父類
    5. 接口VS抽象類
  3. 代碼塊併發

    1. 靜態代碼塊
    2. 動態代碼塊
  4. final 關鍵字框架

    1. 修飾類
    2. 修飾方法
    3. 修飾變量
    4. finalabstract的關係
  5. 單例模式(概念)
  6. 枚舉ide

    1. 爲何要用枚舉
    2. 枚舉的寫法
    3. 枚舉是個普通的類
  7. 寫在最後

抽象類

自我學習Java以來,我在實際開發中基本沒用過抽象類。但框架和JDK中很喜歡使用抽象類。抽象類做爲一個父類,被用來描述一類事物應該具備的基本特徵和功能,子類在此基礎上進行擴展。是對子類的一種約束。

舉個例子吧,學校的老師、校長和學生都是學校的一員,必須具有工做的能力(學習能夠看做學生的工做),可是三者具體怎麼工做是有差別的,具體怎麼工做是本身決定的。看完這篇文章就會有概念了

構造方法

就上面的例子,若是寫成代碼的話應該是這樣的,SchoolMember類表明學校成員,是一個父類,TeacherStudentPrinciple都繼承SchoolMember類,他們都有work方法

public class SchoolMember {
    public String schoolName = "GDPU";
    public void work() {
        System.out.println("做爲學校的成員必定要工做")
    }
}
class Teacher extends SchoolMember{
    @Override
    public void work() {
        System.out.println("我是個普通教師,教好本身的科目就能夠了")
    }
}
class Student extends SchoolMember{
    @Override
    public void work() {
        System.out.println("我是個學生,讀好書對得住爸媽就ok了")
    }
}
class Principal extends SchoolMember{
    @Override
    public void work() {
        System.out.println("我是校長,要讓學校有條有理")
    }
}

你們應該注意到了,其實SchoolMember類中的work方法中的內容並無什麼做用,反而浪費了內存空間。還不如不寫

public class SchoolMember {
    public String schoolName = "GDPU";
    public void work() {
        
    }
}

有一天,小明被爸媽安排到這個學校,小明便成爲了學校的一員,但他無意向學在學校混日子,沒有工做(學習)的能力

class Principal extends SchoolMember{
    public void play() {
        System.out.println("我就不讀怎麼滴")
    }
}

學校以爲小明老這樣很影響學校風氣,因而把小明趕出了學校,學校一片祥和。但過一段時間又有像小明同樣的學生進了這個學校,學校受不了了,最後決定只讓一心向學的學生入學,便在本身的work方法上加了abstract關鍵字,變成抽象方法,學生必須重寫這個方法才能算是學校的一員,才能繼承SchoolMember

抽象方法的寫法

抽象方法被abstract修飾,沒有方法體,只有方法簽名

`public abstract 返回類型 方法簽名(參數列表);

抽象方法是否能夠私有化

能夠,可是做爲抽象方法不被子類重寫還不如寫成普通方法

抽象類的特徵

咱們能夠把抽象類的特徵歸納爲有得有失,抽象類獲得了抽象方法的能力,卻失去建立對象的能力。這裏要注意,雖然抽象類有對方法進行抽象的能力,但他能夠選擇使用或不使用這種能力,也就是說,抽象類不必定有抽象方法,可是有抽象方法的類必定是抽象類

抽象類有無構造器,可否實例化對象

  • 具備構造器

    抽象類誕生的意義就是要給子類繼承的,子類初始化必定會調用父類構造器super(...),因此抽象類必須有構造器

  • 不能實例化對象

    就拿上面的例子,若是`SchoolMember類被實例化,調用抽象方法將沒有意義

    SchoolMember s = new SchoolMember();
    s.work(); // 方法沒有實現,沒有意義

    抽象類原本就意味着一類事物是抽象的(不具體的),沒有必要將他實例化

抽象類的結構

抽象類除了能夠有普通類都有能夠有的成員變量成員方法代碼塊內部類*和構造器之外,還能夠有抽象方法

再次強調,能夠有不表明必定有哈

抽象類的核心意義

抽象類有沒有體現他存在的價值,主要是看兩方面,一是有沒有被子類繼承,二是有沒有作到部分實現部分抽象的效果

被繼承

一個抽象類若是不被子類繼承,還不如作回普通類,還能被實例化

部分實現,部分抽象

一個抽象類做爲父類,它能夠先爲子類實現相同的部分公共代碼,剩下的由子類自由發揮。這裏運用了設計模式中的模板模式

  • 設計模式:前輩在生產實踐中發明的優秀軟件架構或思想,後人能夠直接使用這些優秀架構和思想生產優秀的代碼,
  • 模板模式:使用部分實現部分抽象的思想編寫模板,相同的功能不須要重複寫,提升代碼的可擴性,系統的可維護性,這就相似於咱們使用英語做文模板,做文比較容易拿高分,第一段和最後一段都模板已經寫好了,中間的部分本身寫就得了

    abstract class EnglishModel{
        public void wirteTitle() {
            System.out.println("這是模板的標題");
        }
        public void wirteHead() {
            System.out.println("這是模板的第一段");
        }
        public void wirteTail() {
            System.out.println("這是模板的最後一段");
        }
        public abstract void wirteTitle();
    }
    
    class MyEnglishWord extends EnglishModel{
        
        @Override
        public void wirteTitle() {
             System.out.println("用模板真的爽");
        }
    }

    這樣咱們只要負責寫MyEnglishWord類就能夠完成這份連半個英文都沒有的英語做文了

接口

說到接口,一個帶我作項目的師兄跟我說,接口是業務代碼的抽象。當時沒懂,以後本身學框架搭項目寫代碼的時候慢慢就有感受啦,咱們寫Service層的代碼應該都是先建立一個接口再建立一個實現類實現這個接口的吧,爲何要這樣作呢

爲何要使用接口

個人理解是這樣的,一般一個項目是由不少人一塊兒作,你負責一個模塊我負責另外一個模塊,試想一下,若是沒有接口,我開發的是用戶基礎信息模塊,你開發的是訂單管理模塊,如今有一個場景,你建立訂單後須要查一下這個用戶是否是VIP,是的話給他免運費,那你的代碼會有這樣一句話

UserService userService = new UserService();
boolean isVIP = userService.isVIP(userId);

可是我可能尚未寫這個isVIP方法,可能我判斷是否是VIP的方法不叫這個名字,甚至我連這個UserService類都還沒建立呢,那你的代碼必定是被編譯器標紅,這就很難受了,你得跑過來問我判斷VIP的方法叫什麼名字,要傳入什麼參數,出來什麼結果,何時纔開始寫這個方法

若是我先建立接口能夠解決上面的問題

  • 你不用問我判斷VIP的方法叫什麼,你看個人接口文件就能夠了
  • 你的代碼不會被標紅,由於判斷`VIP的方法存在,只是還沒實現
  • 你不用管我是怎麼實現這個方法的,你調用就好了

因此總結一下:

  1. 接口是一種代碼規範,你我都遵照才能同時一塊兒作開發
  2. 接口也是業務代碼的抽象,調用者不須要知道這個方法內部的實現過程
  3. 同時Java的接口能夠彌補類的單繼承的短板

接口和實現類的格式

接口

public interface 接口名 extends 接口1, 接口2...

實現類

修飾符 class 實現類的名稱 implements 接口1, 接口2...

接口的結構

jdk1.8之後對接口的結構進行了修改,這裏分開講

JDK1.8之前

接口只有全局常量抽象方法l兩部分

  • 全局變量

    public static final 類型 常量名 = 常量;
    常量名通常用英文大寫加下劃線的形式, public static能夠省略不寫
  • 抽象方法

    `public abstract 返回類型 方法簽名(參數列表);
    這裏的 `public abstract能夠省略不寫

JDK1.8之後新增

  • 默認方法 (至關於實例成員方法,接口實現類能夠直接調用)

    class Main implements A{
        public static void main(String[] args) {
            Main m = new Main();
            m.test();
        }
    }
    interface A {
        public default void test() {
            // 默認方法
        }
    }

    其中public default中的public能夠省略。還有一種狀況,若是Main類實現了兩個接口,兩個接口都有同樣名字的方法怎麼辦?

    class Main implements A{
        public static void main(String[] args) {
            Main m = new Main();
            m.test();
        }
        
        @Override
        public void test() {
            // 真正執行的方法
            System.out.println("重寫方法");
        }
    }
    interface A {
        public default void test() {
            // 默認方法A
            System.out.println("默認方法A");
        }
    }
    interface B {
        public default void test() {
            // 默認方法B
            System.out.println("默認方法B");
        }
    }
    重寫方法
  • 靜態方法 (必須用接口名調用)

    public static void test(){
        // 靜態方法
        system.out.println("靜態方法");
    }
    Test.test();
  • 私有方法 (只有內部能夠調用)

    private void run() {
        // 私有方法
        system.out.println("私有方法");
    }

    這是JDK1.9後纔有的部分

接口VS父類

  • 繼承父類的叫子類,父類只有一個父類,能夠有多個子類
  • 實現接口的叫實現類,接口能夠有多個父接口,多個實現類
  • 若是父類和接口同時存在同樣的方法,優先執行父類中的方法

接口VS抽象類

  • 相同點:接口和抽象類都不能被實例化,均可以包含抽象方法
  • 不一樣點:抽象類具備普通類的結構,但他只能單繼承一個父類,接口的組成成分比抽象類少,但能夠繼承多個父接口

代碼塊

代碼塊也是類的五個組成成分之一,分爲靜態代碼塊動態代碼塊

靜態代碼塊

靜態代碼塊屬於類,語法以下,主要做用是初始化靜態資源,如靜態成員變量、數據庫鏈接等

public static String schoolName

static{
    // 靜態代碼塊
    schoolName = "GDPU";
}

動態代碼塊

動態代碼塊屬於對象,對象被建立的時候內部代碼會被執行,用於初始化對象的資源

private String studentName;

{
    // 動態代碼塊
    this.studentName = "Rhythm";
}

final 關鍵字

這個你們平時應該用過吧,它能夠在類、變量和方法上出現

修飾類

該類不能被繼承,斷子絕孫。String類就是用final修飾的

修飾方法

  • 修飾靜態成員方法 (沒有什麼效果)
  • 修飾實例成員方法 (方法不能被子類重寫)

變量

Java中變量分爲局部變量成員變量

成員變量
  • 靜態成員變量:這個其實就是咱們平時定義常量的方式

    public static final String USER_NAME = "Rhythm"
  • 實例成員變量:只容許一次復賦值

    private final int i = 10;
局部變量

final修飾局部變量是爲了保護數據,防止程序員不當心把值給改了。好比下面這段代碼,parse方法計算折後價,rate表示折扣,咱們不但願rate在計算中被修改,能夠加上final關鍵字

public class Test {
    
    public static void main(String[] args) {
        System.out.println(parse(10, 0.8));
        System.out.println(parse(100, 0.7));
    }
    
    public double parse(double rmb, final double rate) {
        return rmb * rate;
    }
}

finalabstract的關係

互斥關係final修飾的類不能被繼承,但抽象類不被繼承沒有意義,final修飾方法不能被重寫,抽象方法也沒有意義

單例模式 (概念)

單例模式保證對象在運行過程當中只被實例化一次,具體實現方式有不少種,這裏只是對單例模式的簡單引出,沒有考慮併發狀況

餓式

就是在類中的資源被使用時初始化對象

public class Main {

    public static void main(String[] args) {
        Demo demo = Demo.getDemo();
    }

}
class Demo {
    public static Demo demo;

    static {
        demo = new Demo();
        System.out.println("對象被初始化");
    }

    private Demo() {
        // 構造器私有
    }

    public static Demo getDemo() {
        return demo;
    }
    
    public static void other() {
        System.out.println("這是個其餘方法");
    }
}

缺點就是若是類中有其餘靜態成員方法如other被調用的時候對象也會被初始化

懶式

就是真的須要的時候才加載,你們看代碼就懂了

class Demo {
    public static Demo demo;

    private Demo() {
        // 構造器私有
    }

    public static Demo getDemo() {
        if (demo == null) {
            demo = new Demo();
        }
        return demo;
    }

    public static void other() {
        System.out.println("這是個其餘方法");
    }
}

調用類中的其餘方法不會初始化對象

注意:

  1. 構造器必須私有化
  2. 這裏只是簡單介紹下概念,之後再來總結詳細的單例模式

枚舉

枚舉在實際開發中經常使用到,這裏對他進行了總結

爲何要使用枚舉

你們在代碼中經常會使用常量來代替一些數字,提升代碼可讀性,對比一下下面兩段代碼,顯然第二種可讀性更高

public static void control(Integer i) {
    if (i = 1) {
        // 打開    
    } else {
        // 關閉
    }
}
public static void main(String[] args) {
    control(1)
}
public static final Integer OPEN = 1;
public static final Integer CLOSE = 2;

public static void control(Integer i) {
    if (i = OPEN) {
        // 打開    
    } else {
        // 關閉
    }
}
public static void main(String[] args) {
    control(OPEN)
}

可是對於咱們來講,咱們仍是能夠經過control(1)來調用方法的,爲了保證代碼的可讀性,咱們能夠對這些常量進行約束管理,把常量抽出來放到一個類中

class Counst {
    public static final Integer OPEN = 1;
    public static final Integer CLOSE = 2;
}

之後使用就要這樣寫

if(i = Counst.OPEN) {
    // 打開
}

因而Java將這些專門存放常量的類稱爲枚舉類,並賦予他特殊的語法

枚舉的寫法

舉個例子,咱們運行完業務代碼以後須要將數據返回到前端時要帶上狀態碼code和提示信息message,這些值都是固定的,咱們能夠把它們抽出來作成枚舉

public enum ResultCodeEnum {

    SUCCESS(200, "操做成功"),

    FAILED(500, "操做失敗");
   
    private long code;

    private String message;

    ResultCodeEnum(long code, String message) {
        this.code = code;
        this.message = message;
    }

    public long getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

總結起來就是:

  1. enum定義類
  2. 定義常量對應的變量,能夠定義多個,如上面的Integer codeString message
  3. 定義常量,如上面的SUCCESS(200, "操做成功"),用逗號隔開
  4. 提供getset方法
  5. 定義一些自定義方法,如

    public static String getMessageCode(Integer code) {
    
        for (ResultCodeEnum item : ResultCodeEnum.values()) {
            if (item.getCode() == code) {
                return item.message;
            }
        }
        
        return null;
    }

枚舉類是個普通的類

咱們能夠用javap查看一下枚舉類的字節碼

public final class ResultCodeEnum extends java.lang.Enum<ResultCodeEnum> {
  public static final ResultCodeEnum SUCCESS;
  public static final ResultCodeEnum FAILED;
  public static ResultCodeEnum[] values();
  public static ResultCodeEnum valueOf(java.lang.String);
  public long getCode();
  public java.lang.String getMessage();
  public static java.lang.String getMessageCode(java.lang.Integer);
  static {};

枚舉類繼承了lang.Enum,並初始化了SUCCESSFAILED兩個ResultCodeEnum對象,提供了幾個方法,因此咱們在使用過程當中把枚舉類當作普通類就能夠了

寫在最後

這篇文章主要講述了抽象類、接口、代碼塊、final關鍵字、單例模式和枚舉,有些咱們平時用不上的記住語法就好,面試的時候還能說一說,若是個人理解有誤的話歡迎你們評論告訴我

相關文章
相關標籤/搜索