Java final關鍵字詳解

java - final關鍵字

final是java的保留關鍵字,字面意思是”最終的、不可更改的”,對應到java的使用場景完全適用。

java的final關鍵字可以修飾類、方法、成員變量、局部變量、方法參數。接下來將會分別說明這些用法。

用法

修飾類

final修飾類,表示該類是不可被繼承的,該類已經足夠完整了。jdk源碼中的StringInteger等包裝類都是final的。如String類的聲明


public final class String implements java.io.Serializable, Comparable<String>, CharSequence {

}

Integer類的聲明

public final class Integer extends Number implements Comparable<Integer> {

}

聲明爲final的類如果被子類繼承,編譯期即會報錯。

修飾方法

final修飾方法,表示該方法不可被重寫。如果確認方法功能已經足夠完整,不需要子類重寫,可以在方法前加上該修飾符。如下例

public class A {
  //該方法被聲明爲final
  public final void aMethod() {
  }
}

public class B extends A{
  //該方法試圖重寫類A的方法,編譯期將會報錯,提示final方法不可被重寫
  public final void Method() {
  }
}

修飾成員變量

final修飾成員變量,表示該變量一旦被賦值就不能改變,final變量也稱爲java常量。

對於基本數據類型,表示該值本身不可被改變,對於指向對象的引用,表示該引用不可改變,但是引用指向的對象確是可以改變的。

final變量和其他普通的成員變量不太一樣,普通成員變量會初始化爲該類型的”零”值,例如:

public class Test {
    //int型的"零值"是0
    private int x;
    private long y;
    //對象引用的"零值"是null
    private Long l;
    private Object obj;

    public void print() {
        System.out.println(x);
        System.out.println(y);
        System.out.println(l);
        System.out.println(obj);
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.print();
    }
}

將會打印出:

0
0
null
null

可以看出,int型默認爲0,long型默認也是0,對象引用類型的默認爲null。

如果成員變量用final來修飾,則必須對其進行初始化,否則編譯期即報錯,final變量有3種初始化方式:

直接賦值、構造函數、靜態初始化塊

  • 直接賦值

    public class Test {
    //直接給final成員變量賦值
    private final int x = 0;
    }
  • 構建函數

    public class Test {
    private final int x;
    public Test() {
      //構造函數初始化
      x = 0;
    }
    }
  • 靜態初始化塊

    public class Test {
    private static final int x;
    static {
      //static final修飾的成員變量只能通過直接賦值或者靜態初始化塊賦值
      x = 0;
    }
    }

    final修飾的成員變量存儲在常量池中

修飾局部變量

final還可以修飾局部變量,final修飾的局部變量在使用前必須顯示初始化,如果該變量未被使用則不需要初始化。

同樣地,final修飾的局部變量一經賦值,就不能更改。

另外,方法中的局部內部類(包括匿名內部類)訪問方法的局部變量必須是final修飾的,例如:

public class Outer {
    public Object method() {
        //被局部內部類訪問的局部變量必須是final修飾的
        final int k = 0;

        class Inner {
            void innerMethod() {
                //此處引用了方法的局部變量
                System.out.println(k);
            }
        }
        return new Inner();
    }
}

方法中的普通變量在線程退出該方法之後,局部變量k就消失了,但是方法method的返回值對該變量有引用,可能會訪問到不存在的變量。所以該變量在方法退出之後不能消失。那麼java是如何解決的呢?

我們知道內部類和外部類是如何通信的,內部類其實是持有外部類的引用Outer.this,通過這個引用可以訪問外部類的成員變量。方法中的局部內部類也是如此(方法中的內部類也有自己獨立的類文件)。

局部內部類如果想訪問方法的局部變量按理來說是沒法辦到的,但是爲了解決這個問題,java通過將方法中的局部變量傳遞給(通過局部類的構造函數)局部內部類並變成局部內部類的成員變量來實現的,以後局部內部類訪問方法的局部變量其實是在訪問自己的成員變量,也就是說局部內部類維護了方法局部變量的副本,既然是副本,那就有一致性的問題,如何保證局部變量的值和局部內部類中該變量副本的值是一樣的?利用final來保證,將變量用final修飾,禁止修改。

此時方法退出後,局部內部類仍然可以訪問到自己的成員變量(該變量是方法局部變量的副本),造成了局部內部類仍然可以訪問方法局部變量的假象。

修飾方法參數

方法參數其實也是局部變量,不再說明。

注意事項

java引入final關鍵字其中一個重要原因是效率,那麼爲何用final修飾後會提高效率呢?

編譯器遇到final方法時會採用內聯機制,節省了方法調用的開銷,但是如果方法體過大,編譯器可能不會採用內聯機制。

參考:

  1. Thinking in java
  2. http://blog.csdn.net/zhangjg_blog/article/details/19996629 (這篇分析內部類的文章非常好,分析的很透徹)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章