今天繼續回顧Java基礎,有些東西用得很少,你們看看知道語法就好前端
抽象類java
抽象方法程序員
抽象類的特徵面試
抽象類的核心意義數據庫
接口設計模式
接口的結構架構
JDK1.8
以前JDK1.8
以後新增VS
父類VS
抽象類代碼塊併發
final
關鍵字框架
final
和abstract
的關係枚舉ide
自我學習Java以來,我在實際開發中基本沒用過抽象類。但框架和JDK
中很喜歡使用抽象類。抽象類做爲一個父類,被用來描述一類事物應該具備的基本特徵和功能,子類在此基礎上進行擴展。是對子類的一種約束。
舉個例子吧,學校的老師、校長和學生都是學校的一員,必須具有工做的能力(學習能夠看做學生的工做),可是三者具體怎麼工做是有差別的,具體怎麼工做是本身決定的。看完這篇文章就會有概念了
就上面的例子,若是寫成代碼的話應該是這樣的,SchoolMember
類表明學校成員,是一個父類,Teacher
、Student
和Principle
都繼承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
的方法存在,只是還沒實現因此總結一下:
接口
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"; }
這個你們平時應該用過吧,它能夠在類、變量和方法上出現
該類不能被繼承,斷子絕孫。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; } }
final
和abstract
的關係互斥關係,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("這是個其餘方法"); } }
調用類中的其餘方法不會初始化對象
注意:
- 構造器必須私有化
- 這裏只是簡單介紹下概念,之後再來總結詳細的單例模式
枚舉在實際開發中經常使用到,這裏對他進行了總結
你們在代碼中經常會使用常量來代替一些數字,提升代碼可讀性,對比一下下面兩段代碼,顯然第二種可讀性更高
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; } }
總結起來就是:
enum
定義類Integer code
和String message
SUCCESS(200, "操做成功")
,用逗號隔開get
和set
方法定義一些自定義方法,如
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
,並初始化了SUCCESS
和FAILED
兩個ResultCodeEnum
對象,提供了幾個方法,因此咱們在使用過程當中把枚舉類當作普通類就能夠了
這篇文章主要講述了抽象類、接口、代碼塊、final
關鍵字、單例模式和枚舉,有些咱們平時用不上的記住語法就好,面試的時候還能說一說,若是個人理解有誤的話歡迎你們評論告訴我