對於一個final變數,不管它是類變數、執行個體變數,還是局部變數,只要定義該變數時使用了final修飾符修飾,並在定義該final類變數時指定了初始值,而且該初始值可以在編譯時間就被確定下來,那麼這個final變數本質上已經不再是變數,而是相當於一個直接量。
public class FinalTest{public static void main(String[] args){<strong>final int a=5+2;final double b=1.2/3;final String str="瘋狂" + "Java";final String book="瘋狂Java講義"+99.0;</strong>//下面的book2變數的值因為調用了方法,所以無法在編譯時間確定下來final String book2="瘋狂Java講義"+String.valueOf(99.0); // 1System.out.println(book=="瘋狂Java講義99.0");System.out.println(book2=="瘋狂Java講義99.0");}}上面程式中的粗體字代碼定義了四個final變數,程式為這四個變數賦予初始值,指定的初始值要麼是算術運算式,要麼是字串串連運算。即使字串串連運算中包含隱式類型(將數值轉換為字串)轉換,編譯器依然可以在編譯時間就確定a、b、str、book這四個變數的值,因此它們都是“宏變數”
“宏變數”:final修飾符的一個重要用途就是定義“宏變數”。當定義final變數時就為該變數指定了初始值,而且該初始值可以在編譯時間就確定下來,那麼這個final變數本質上就是一個“宏變數”,編譯器會把程式所有用到該變數的地方直接替換成該變數的值。
從表面上看,1行代碼定義的book2與book沒有太大的區別,只是定義book2變數時的顯式將數值99.0轉換為字串。但由於該變數的值需要調用String類的方法,因此編譯器無法在編譯時間確定book2的值,book2不會被當成“宏變數”類處理。
例如下列樣本:
public class FinalTest{public static void main(String[] args){String s1="瘋狂Java";String s2="瘋狂"+"Java";System.out.println(s1==s2);String str1="瘋狂";String str2="Java";<span style="color:#ff0000;">String s3=str1+str2;</span>System.out.println(s1==s3);}}對於s3而言,它的值由str1和str2進行串連後得到。由於str1/str2隻是兩個普通變數,編譯器不會執行“宏替換”,因此編譯器無法在編譯時間確定s3的值,不會讓s3指向字串池中緩衝的“瘋狂Java”。由此可見,s1==s3將輸出false
為了讓s1==s3輸出為true也很簡單,只要編譯器可以對str1和str2兩個變數執行“宏替換”這樣編譯器既可以在編譯階段就確定s3的值,就會讓s3指向字串池中緩衝的“瘋狂Java".即把程式改成如下形式:
public class FinalTest{public static void main(String[] args){String s1="瘋狂Java";String s2="瘋狂"+"Java";System.out.println(s1==s2);<strong>final String str1="瘋狂";final String str2="Java";</strong>String s3=str1+str2;System.out.println(s1==s3);}}如下列樣本:
public class FinalTest{final static String str1;final static String str2="Java";static {str1="Java";}public static void main(String[] args){System.out.println(str1+str1=="JavaJava");System.out.println(str2+str2=="JavaJava");}}上面程式定義了兩個final類變數,但是只有str2在定義該變數時指定了初始值,str1則在靜態初始化塊中指定了初始值,因此系統不會對str1進行宏替換,但會對str2執行”宏替換“。