Java中final的進一步理解

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修飾符。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章