- Java提供了final關鍵字來修飾變數,方法和類,系統不允許為final變數重新賦值,子類不允許覆蓋父類的final方法,final類不能派生子類。
- Abstract 和 interface兩個關鍵字分別用於定義抽象類和介面,抽象類和介面都是從多個子類中抽象出來的共同特徵,但抽象類主要作為多個類的模板,而介面則定義了多類應該遵守的規則。
基本資料型別 | 包裝類 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
double | Double |
float | Float |
boolean | Boolean |
Java8 增強的包裝類
- 自動裝箱,就是把一個基本型別變數直接賦值給對應的包裝類變數,或者OBJECT。
- 自動拆箱,允許直接把包裝類物件直接賦值給一個對應的基本型別變數。
- 包裝型別的變數是引用資料型別,但包裝類的例項可以與數值型別的值作比較,這種比較是直接取出包裝類例項所包裝的數值來進行比較的。
- 兩個包裝類例項比較,只有兩個包裝類引用指向同一個物件才會返回true。
-
對於Integer包裝型別的數值超過了(-128 — 127)比較為false的解釋如下:
-
系統會把一個-128到127之間的整數自動裝箱成Integer例項,並放入了一個名為cache的陣列中快取起來,如果以後把一個-128到127之間的整數自動裝箱成一個Integer例項的時候,實際上是直接指向對應的陣列元素,因此-128到127之間的同一個整數自動裝箱成Integer例項時,永遠都是引用此cache陣列的同一個陣列元素,所以他們全部相等,但每次把一個不在-128到127範圍內的整數自動裝箱成Integer例項時,系統總是重新建立一個Integer例項,所以比較為false。
public static void main(String[] args) {
Integer valueOf = 112; Integer valueOf1 = 112; //dsds : true System.out.println("dsds : "+(valueOf == valueOf1)); Integer valueOf2 = 128; Integer valueOf3 = 128; //dsds : false System.out.println("dsds : "+(valueOf2 == valueOf3));
}
-
- Boolean值的比較:true > false
處理物件
- 當使用==來判斷兩個變數是否相等時,如果兩個變數是基本型別,且都是數值型別,則只要兩個變數的值相等就會返回true。
- 對於兩個引用變數必須引用一個物件的時候==才會返回true,==不可用於比較型別上沒有父子關係的兩個物件。
- 常量池:constant pool,專門用於管理在編譯時被確定並且被儲存在以編譯的.class檔案中的一些資料。包括了關於類,方法,介面中的常量,還包括字串常量。
- JVM常量池保證相同的字串直接量只有一個,不會產生多餘副本。
- 使用new String()建立的字串物件是執行時建立出來的,它被儲存在執行時記憶體區,即堆記憶體,不會放入常量池中。
類成員
- 建立的物件例項中根本不會擁有對應類的類變數。
- Null物件可以訪問他所屬類的類成員。
- 類成員不能訪問例項成員,包括成員變數,方法,初始化塊,內部類和列舉類。因為類成員是屬於類的,類成員的作用域比例項成員的作用域更大,完全可能出現類成員已將初始化完成,但例項成員還不曾初始化的情況,如果允許類成員訪問例項成員將會引起大量錯誤。
- 如果一個類始終只能建立一個例項,那就被成為單例類。
- 為了避免其他類自由建立該類的例項,應該把構造器private修飾,從而把該類的所有構造器隱藏起來。
-
一旦把該類的構造器隱藏起來,就需要提供一個public方法作為該類的訪問點,用於建立該類的物件,且該方法必須使用static修飾。除此之外,該類還必須快取已經建立的物件,否則該類無法知道是否曾經建立過物件。
- 程式碼例項
public class Singleton {
//單例類
public static Singleton singleton;//用於儲存建立好的物件。
private Singleton() {}//private修飾,對外隱藏構造器
public static Singleton getInstance() {
if (singleton == null) {//如果為空那麼就是未曾建立物件
singleton = new Singleton();//建立物件
}
return singleton;//返回
}
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);//true
}
}
Final修飾符
- 當類初始化時,系統會為該類的類變數分配記憶體,並分配預設值,當建立物件時,系統會為該物件的例項變數分配記憶體,並分配預設值,也就是說:當執行靜態初始化快時可以對類變數附初始值,當執行初始化程式碼塊,構造器時可以對例項變數附初始值。
- Final修飾的成員變數而言,一旦有了初始值,就不能二次賦值,如果既沒有初始值,也沒有在構造器,初始化塊中指定初始值,那麼就為系統預設的初始值,也就失去了意義,因此final修飾的成員變數必須由程式設計師顯示的指定初始值。
-
Final修飾的類變數,例項變數能指定初始值的地方
- 類變數:必須在靜態初始化塊中指定初始值或宣告該類變數時指定初始值。
- 例項變數:非靜態初始化塊,宣告變數時和構造器中。
- 例項變數不能在靜態初始化塊中指定初始值,因為靜態初始化塊是靜態成員,不可訪問例項變數-非靜態成員。類變數不能在普通初始化塊中指定初始值,因為類變數在類初始化階段已經被初始化了。
- Final成員變數在顯示初始化之前不能直接訪問,但可以通過方法來訪問,基本可以算是java的一個設計缺陷。
- 系統不會對區域性變數初始化,區域性變數必須由程式設計師顯示初始化,因此使用final修飾區域性變數時。既可以在定義時指定預設值,也可以不指定預設值。
- 當使用final修飾基本型別變數時,不能對基本型別變數重新賦值,因此基本型別變數不能被改變。但對於引用變數而言,他僅僅儲存的是一個引用,final只保證這個引用型別變數所引用的地址不會改變,即一直引用同一個物件,但是在堆記憶體中的這個物件完全可以發生改變。
-
巨集變數的確定條件
- 使用final修飾。
- 在定義該final變數時就指定了初始值。
- 該初始值可以在編譯時被確定下來。
- 巨集變數一旦確定下來,編譯器會把程式中所有用到該變數的地方直接替換成該變數的值。
- 巨集變數與非巨集變數不相等
- 不希望子類衝重寫父類的某個方法,則可以使用final修飾該方法。
- 為了保證某個類不被繼承,則可以使用final修飾這個類。
- 不可變類:建立例項後,例項的例項變數是不可改變的。如Integer in = new Integer(8);
-
建立自定義的不可變類規則如下
- 使用private和final修飾符來修飾該類中的成員。
- 提供帶引數的構造器,用於傳入引數以便初始化成員變數。
- 僅為該類的成員變數提供getter方法,不要為該類提供setter方法,因為普通方法無法修改final修飾的成員變數。
- 如果有必要重寫Object中的equals 和 hashCode方法
- 與可變類相比,不可變類的例項在整個生命週期中要永遠處於初始化狀態,他的例項變數不可以改變,因此對不可變類的例項的控制更加簡單。
public class NoChangeClass {
private final ClassNames names;
public NoChangeClass() {
this.names = new ClassNames("1","2");//預設值
}
public NoChangeClass(ClassNames names) {
//如果利用有參構造,那麼直接new一個新例項,這樣避免了舊例項的更改造成當前類的變化。
this.names = new ClassNames(names.getName(),names.getTime());
}
public ClassNames getNames() {
return new ClassNames(names.getName(),names.getTime());
}
public static void main(String[] args) {
ClassNames classNames = new ClassNames("?","??");
NoChangeClass noChangeClass = new NoChangeClass(classNames);
System.out.println(classNames.getName());
classNames.setName("21e1231");
System.out.println(classNames.getName());
System.out.println(noChangeClass.names.getName());
}
}
Output:
?
21e1231
?
抽象類
- 某個父類只是知道其子類應該包含怎麼樣的方法,但是無法準確的知道這些子類是如何實現這些方法的。
- 抽象方法只有方法簽名,沒有方法實現的方法。
- 有抽象方法的類只能被定義為抽象類,抽象類可以沒有抽象方法。
-
抽象方法和抽象類的規則
- 抽象類抽象方法必須使用abstract修飾符修飾,抽象方法不能有方法體。
- 抽象類不能被例項化,無法使用new呼叫抽象類的構造器建立抽象類的例項,即使抽象類裡不包含抽象方法。
- 抽象類可以包含成員變數,方法,構造器,初始化塊,內部類。抽象類的構造器不能用於建立例項,主要是用於被其子類呼叫。
- 含有抽象方法的類,包括直接定義了一個抽象方法;或者整合了一個抽象父類,但沒有完全實現父類包含的抽象方法;或實現了一個藉口,但沒有完全實現介面包含的抽象方法,只能被定義為抽象類.
- 在建立其子類例項的時候呼叫抽象父類的構造器,初始化塊。
- 抽象類不能用於建立例項,只能當做父類被其他子類繼承。
- Abstract修飾類,表示只能被繼承,修飾方法,表明必須由子類實現重寫。而final修飾的類不能被繼承,final修飾的方法不能被重寫,所以final和abstract不能同時修飾類和方法。
- Static與abstract不能同時修飾某個方法,即沒有所謂的類抽象方法。但是可以同時修飾內部類。
- Abstract關鍵字修飾的方法必須被其子類重寫才有意義,否則就永遠不會擁有方法體,所以abstract方法不能是private修飾。Private與abstract不能同時修飾方法。
- 抽象類是從多個具體類中抽象出來的父類,它具有更高層次的抽象。
-
抽象類别範本模式的簡單規則:
- 抽象父類可以只定義需要使用的某些方法,把不能實現的部分抽象成抽象方法,留給其子類去實現。
- 父類中可能包含需要呼叫其他系列方法的方法,這些被呼叫方法即可以由父類實現,也可以由其子類實現。父類裡提供的方法只是定義了一個通用演算法,其實現也許並不完全由自身實現,而必須依賴於其子類的輔助。
Java 9 改進的介面
- Java9對介面進行了改進,允許在介面中定義預設方法和類方法。
- 介面定義的是多個類共同的公共行為規範,這些行為是與外部交流的通道,這就意味著介面裡通常是定義一組公共方法。
-
介面定義的基本語法說明:
- 修飾符可以是public或者省略,如果省略了public,則預設採用包許可權訪問控制符。
- 一個介面可以有多個直接父介面,但介面只能繼承介面,不能繼承類。
- 由於介面定義的是一種規範,因此介面裡不能包含構造器和初始化塊定義,可以包含常量(靜態常量),方法(只能是抽象例項方法,類方法,預設和私有方法),內部類定義。
- 介面中的私有方法可以擁有方法體,但私有方法不能使用default修飾。私有方法可以使用static修飾,所以就是說私有方法可以是類方法也可以是例項方法。
- 介面中的靜態常量是介面相關的。因此預設增加static和final修飾符。所以在介面中的成員變數總是使用public static final修飾,並且只能在定義時指定初始值。
- 介面中如果不是定義預設方法類方法或私有方法,系統自動為普通方法增加abstract修飾符。介面中的普通方法總是使用public abstract修飾。
- 介面中定義的內部類,內部介面,內部列舉預設都採用public static兩個修飾符,不管定義時是否指定,系統會為其自動修飾。
- 預設方法都是採用default修飾,不能使用static修飾,並且總是public修飾。需要使用介面的實現類例項來呼叫這些方法。
- 類方法總是public修飾,可以直接使用介面名來呼叫。
- 介面中的預設方法:當兩個預設方法中包含一段相同的實現邏輯時,就有必要把這段程式碼抽取成工具方法,而且工具方法一般是被隱藏的,所以這就是java9中增加私有方法的原因。
- 一個介面可以由多個直接父介面,和類繼承相似,子介面擴充套件某個父介面時,將會獲得父介面裡定義的所有抽象方法,常量。
- 介面不能用於建立例項,但是可以用於宣告引用型變數。
-
介面的主要用途就是被實現類實現:
- 定義變數,也可以用於強制型別轉換。
- 呼叫介面中定義的常量、
- 被其他類實現。
- 實現介面與繼承父類相似,一樣可以獲得所實現介面裡定義的常量,方法,
- 一個類實現了一個或者多個介面之後,必須完全實現介面中定義的全部抽象方法,否則該類就將保留從父介面整合得到的抽象方法,該類也必須定義成抽象類。
- 一個類實現某個介面時,該類將會獲得介面中定義的常量,方法等。因此可以把實現介面理解為一種特殊的繼承。相當於實現類繼承了一個徹底抽象的類。
-
為什麼實現類實現介面裡的方法只能使用public?
- 因為介面裡的方法都是public的,而子類重寫父類方法時訪問許可權只能更高或者相等。
- 抽象類即只對事物本身的抽象,介面則是對事物本身擁有的行為的抽象。
-
介面與抽象類的在用法上的差別。:
- 介面裡只能包含抽象方法,靜態方法,預設方法和私有方法。不能為普通方法提供實現,抽象類完全可以。
- 介面裡只能定義靜態常量,不能定義普通成員變數,抽象類都可以。
- 介面裡不包含構造器;抽象類裡可以包含構造器,抽象類裡的構造器並不是用於建立物件。而是讓其子類呼叫這些構造器來完成屬於抽象類的初始化操作。
- 介面裡不能包含初始化塊,但抽象類則完全可以包含。
- 一個類最多隻能有一個直接父類,包括抽象類。但一個類可以實現多個介面,通過實現多個介面可以彌補java單繼承的不足。