Java中final的進一步理解
final修飾符的作用
在JMM中要求final域(屬性)的初始化動作必須在構造方法return之前完成。換言之,一個對象創建以及將其賦值給一個引用是兩個動作,對象創建還需要經歷分配空間和屬性初始化的過程,普通的屬性初始化允許發生在構造方法return之後(指令重排序)。
普通變量和final變量到底又有什麼區別呢?看看下面這段代碼的輸出結果
public class FinalConstructorTest {
static abstract class A {
public A() {
display();
}
public abstract void display();
}
static class B extends A {
private int INT = 100;
private final int FINAL_INT = 100;
private final Integer FINAL_INTEGER = 100;
private String STR1 = "abc";
private final String FINAL_STR1 = "abc";
private final String FINAL_STR2 = new String("abc");
private final List<String> FINAL_LIST = new ArrayList<>();
private B(){
super();
System.out.println("abc");
}
public void display() {
System.out.println(INT);
System.out.println(FINAL_INT);
System.out.println(FINAL_INTEGER);
System.out.println(STR1);
System.out.println(FINAL_STR1);
System.out.println(FINAL_STR2);
System.out.println(FINAL_LIST);
}
}
public static void main(String[] args) {
new B();
}
}
輸出結果如下:
0
100
null
null
abc
null
null
abc
匿名內部類使用外部變量,這個變量必須用final來聲明纔可以被使用
僞代碼如下:
public void test() {
final int a = 100;
new A() {
public void display() {
System.out.println(a);
}
}
其他操作
}
這其實是對一個語法的疑問,本身沒什麼好解釋的,但如果非要解釋,可以從這個角度來理解:在編譯時,這個地方會自動生成一個匿名內部類,而本地變量a的作用域是在方法test()中,它如何能用到另一個內部類中呢?
其中一種方式是參數傳遞,另一種方式就是作爲一個屬性存在於匿名內部類對象中。無論哪一種方式都會在匿名內部類對象中有一份數據拷貝。
JVM在設計時並不知道我們的代碼會怎麼寫,或者說不明確在所創建的匿名內部類中到底會做什麼,例如在代碼中完全可以在內部創建一個線程來使用這個變量,或者創建一個任務提交給線程池來使用這個變量。如果是這樣,匿名內部類的運行與該方法本身的運行處於兩個線程中,當外部方法可能已經結束時,那麼相應的局部變量的作用域已經結束,自動會被回收,要保證匿名內部類一直可以使用該變量,就只能用拷貝的方法。
如果這個屬性不是final修飾的,在匿名內部類中使用“同名”的變量操作,並且可以對它做任意修改,自然外部也應當能感知到。但是事實上不能感知到,因爲這是一份數據拷貝。這種語法的設計手段是爲了避免誤解,語法上強制約束它爲final修飾符。