final能夠用來修飾 類、成員變量、方法app
一、修飾類jvm
修飾類時,表示該類不能夠被繼承,同時,該類的方法也被默認爲final修飾的(變量沒有)優化
二、修飾方法ui
修飾方法時,表示這個方法不能夠被重寫,可是能夠被繼承(子類不能隨便修改父類的方法了)對象
三、修飾成員變量繼承
這個是最重要的一點。字符串
當成員變量爲基本數據類型時,則該值在初始化以後,則不能夠被改變。以下代碼string
當成員變量爲引用類型時,則該引用在初始化後,則不能夠指向別的對象了,可是這個對象裏屬性的值仍是能夠改變的。編譯
而且,在final修飾引用類型的成員變量時,必須顯式的初始化(由於若是不顯式的初始化,在代碼中也不可能經過set方法來給它賦值,和基本數據類型同樣,用set方法賦值時,會報錯)test
再說一個比較經典的問題,
public static void main(String[] args) { String s1 = "a"; String s2 = "b"; final String fs2 = "b"; String s3 = "ab"; String s4 = s1 + s2; String s5 = "a" + fs2; String s6 = "a" + "b"; System.out.println(s3 == s6);//true System.out.println(s3 == s4);//false System.out.println(s3 == s5);//true }
這是爲何呢
s3 == s6,
在jvm計算字符串相加時,若是是至關於「a」 + "b",則在編譯時就自動優化爲「ab」。因此s3和s6相等
s3 != s4,
s4是兩個變量的值相加,因此在編譯時沒法確認他們(s1和s2)的值,在執行時,會從新new 一個StringBuilder()來append他們的值,而最終s4指向的是堆中的一個對象,非String Pool中
s3 == s5
這個就是fianl的做用了,fs2被fianl修飾了,那麼就是常量,在編譯期就能夠肯定其值,因此,
s5 = "a"+fs2 其實就至關於 s5 = "a" + "b"
經過上面的闡述,也就知道,字符串在相加時的一些「規則」
如
String str1 = 「c」;
String str2 = 「a」 + str1 + "b"; 這樣的寫法,在jvm編譯時是沒法優化的,由於a和b之間夾了一個變量str1
String str2 = 「a」 + "b" + str1; 這樣的寫法,在jvm編譯時自動優化爲 「ab」 + str1
在字符串相加時,加號直接相連的常量之間能夠被jvm自動編譯時就優化,一旦中間夾了變量,則會從新建立一個StringBuilder對象來append,因此,以下代碼:
public static void test1(){ String str1 = "a"; String str2 = ""; long l1 = System.currentTimeMillis(); for (int i=0;i<1000000;i++){ //這樣效率很低 str2 += str1; } long l2 = System.currentTimeMillis(); System.out.println(str2); System.out.println(l2-l1); }
效率是極其低下的,由於一直在不停地建立StringBuilder對象。通常咱們用以下代碼代替
public static void test2(){ StringBuilder stringBuilder = new StringBuilder(); String str2 = ""; long l1 = System.currentTimeMillis(); for (int i=0;i<1000000;i++){ stringBuilder.append("a"); } str2 = stringBuilder.toString(); System.out.println(str2); long l2 = System.currentTimeMillis(); System.out.println(l2-l1); }
這個代碼只建立了一次StirngBuilder,在時間消耗上,比第一段代碼強了不止十倍百倍。