關於這兩個關鍵字,應該是在開發工做中比較常見的,使用頻率上來講也比較高。接口中、常量、靜態方法等等。可是,使用頻繁卻不表明必定是可以清晰明白的瞭解,能說出個子醜演卯來。下面,對這兩個關鍵字的常見用法作點總結記錄,方便以後的回顧以及突擊知識點。java
final,一如字面意思 「最終的」,大致在 Java 中表示 「不可變的」。可用來修飾類、方法、方法參數以及變量。ide
一、修飾類性能
final 在修飾類的時候,表明的是此類不能被繼承。也就是說若是一個類肯定不會被繼承使用,則能夠設計成 final類型的。典型的例子就是 String 類。優化
二、修飾方法this
final 修飾的方法,能被繼承,可是不能重寫。能夠重載。spa
三、修飾方法參數設計
final 在修飾方法參數的時候,表示的是在執行方法的內部,不可以去改變參數的值。可是若是是引用對象,是能夠改變應用對象的屬性值。code
四、修飾變量對象
final 在修飾變量,表明的是不可變,也便是常說的 「常量」。 final 在修飾的時候,容許一次賦值,以後在生命週期類,不容許對其進行修改。blog
修飾變量存在兩種狀況:基本類型的數據 和 對象數據。在修飾基本類型數據的時候,值是不可變的。在修飾對象數據的是,對象的引用是不可改變的,可是,能夠修改對象內部的屬性值。
final 修飾的變量必須在使用前進行初始化,一種方式是在聲明的時候就給出默認值。還有一種就是經過構造方法去設置。
五、代碼示例
package com.cfang; import java.util.Calendar; import lombok.Data; import lombok.extern.slf4j.Slf4j; @Slf4j public class T3 { public static void main(String[] args) { // -- 修飾變量 final int b = 0; /** * 編譯報錯:The final local variable b cannot be assigned. It must be blank and not using a compound assignment */ // b = 2; // -- 修飾方法參數 DemoCls cls = new DemoCls(); cls.setAge(10); log.info("democls age : {}", cls.getAge()); int a = 10; sayHello(a, cls); log.info("after call sayHello, democls age : {}", cls.getAge()); } private static void sayHello(final int a, final DemoCls cls) { /** * 編譯報錯:The final local variable a cannot be assigned. It must be blank and not using a compound assignment */ // a = 11; // cls = new DemoCls(); cls.setAge(11); } } @Data class DemoCls{ private int age; } // -- 修飾方法 @Slf4j class Person { public final void saySth() { log.info("i am person"); } } @Slf4j class Male extends Person{ /** * 編譯出錯 : Cannot override the final method from Person * 修飾的方法不可以被重寫 */ // public final void saySth() { // log.info("i am male"); // } public final void saySth(String msg) { log.info("i am male"); } }
其中,修飾方法參數,若是是引用對象,是能夠改變對象的屬性值,這一點也很好理解:cls 是引用變量,final 修飾引用變量,只是限定了此引用變量 cls 的引用不能改變,而實際引用的對象的自己的值是能夠進行修改的。文字語言組織可能不是很清晰,以下圖所示,一目瞭然的就知道說要表述的意思了。
static,靜態的。在 Java 中,static 一般可被用於修飾 變量、方法以及代碼塊。
一、修飾變量
static 修飾的變量,叫作靜態變量。static 變量被全部類對象共享,在內存中僅一份,隨着類的初始化而被加載。與之對應的非靜態變量,是屬於每一個實例對象自己,內存中存在多份,相互間不影響。
二、修飾方法
static 修飾的方法,叫作靜態方法。調用靜態方法,不依賴於實例對象就能夠進行訪問,因此,靜態方法是沒有 this的。因爲此特性以及非靜態方法依賴於實例對象調用,因此靜態方法中是不可以直接使用非靜態的成員屬性和方法。與之相反的是,非靜態方法是能夠直接訪問使用靜態成員變量和方法。一樣的,靜態方法也是沒有 super 的。能夠一句話總結下:因爲 static 和具體的實例對象無關,而 this、super和具體的實例對象息息相關,因此,static 和 this、super 勢如水火,一如白天與黑夜。
三、修飾代碼塊
static 修飾代碼塊,在類初始化加載的時候,會按照 static 塊的順序進行加載,而且,生命週期內,只加載一次。基於此特性,能夠設計優化程序的性能,一些只須要一次性初始化加載的內容,就能夠放在 static 塊中進行。
四、代碼示例
package com.cfang; import lombok.extern.slf4j.Slf4j; @Slf4j public class T4 { private static int a; private static int b; private static int c; static { log.info("init value a"); a = 10; } { /** * 普通代碼塊,依賴於實例對象 */ log.info("init value c"); c = 12; } public static void main(String[] args) { // -- 靜態方法調用非靜態 /** * 不可直接訪問 */ // sayHello1(); new T4().sayHello1(); // -- 靜態方法直接調用 sayHello(); // -- 靜態代碼塊初始化資源 log.info("value a : {}", a); log.info("value b : {}", b); log.info("value c : {}", c); } private static void sayHello() { log.info("say hello"); } private void sayHello1() { log.info("say hello1"); // -- 非靜態方法直接調用 sayHello(); } static { log.info("init value b"); b = 11; } }
五、static 常見誤區
package com.cfang; import lombok.Data; import lombok.extern.slf4j.Slf4j; @Slf4j public class T5 { private static int a = 10; public static void main(String[] args) { new T5().printVal(); //-- The field DemoCls01.b is not visible DemoCls01.b; } private void printVal() { int a = 11; log.info("value a : {}", this.a); } } @Data class DemoCls01{ private static int b; }
5.一、static 的 this
上述代碼中,最終運行 printVal 方法,輸出的結果是 :value a : 10 。 其實這也很好理解: this 指代的當前對象,而經過 new T5().printVal() 調用的話,this 指代的就是當前新建立的實例對象,static 修飾的變量自己是被全部類對象所共享的,而 printVal 方法中 a 屬於局部變量,跟 this 實例對象並無關聯。因此,輸出的就是 10。
5.二、static 與 可見性
static 並不能改變變量或者方法的可見性。上述代碼中,main 方法中,DemoCls01.b 會編譯報錯。
5.三、static 與 局部變量
Java規範中,明確說明了 :static 關鍵字不可修飾局部變量。
4、總結
final 和 static ,聯合使用修飾屬性表示一旦給值,就不可修改,而且能夠經過類名訪問;修飾方法,表示該方法不能重寫,能夠在不new對象的狀況下調用。
忽然想到,接口 interface 中,成員變量的默認修飾符爲 public static final,方法的默認修飾符 public abstract 。