再說final變數

來源:互聯網
上載者:User

從jdk1.0到今天,JAVA技術經過十餘年的發展,技術上已經發生了巨大的變化.但final變數的定義從它

誕生那天起,就沒有發生任何變化,也就是這十多年它就一直表示它原來的意思.

但遺憾的是,經過十多年仍然有90%的人沒有理解它的真實含義,也沒有一篇文章,包括我所見到的所有介紹
JAVA的書籍(包括TKJ)都沒有說清楚,我相信肯定有些作者是理解的,但沒有一個作者向讀者說清楚.而中國網友
大多數人被一篇胡說八道的<<淺談Java中final,finalized,finally>>的文章跑過馬.(臉紅一下:當初我也是).

final變數的定義本身並不複雜,就是變數一經初始化就不能再指向其它對象.在c++中它是一個const指標,而
不是指向const變數的指標,const指標的意思是說它只能一直指向初始化時的那個地址.但那個地址中對象本身
是可以修改的.而指向const變數的指標是說所指對象本身是不能修改的.

如:final StringBuffer sb = new StringBuffer("Axman");
sb = new StringBuffer("Sager");//錯誤,sb不能再指向其它對象.
sb.append(" was changed!"); //sb指向的地象本身可以修改.

先說final變數初始化:

很多文章都這麼說:其初始化可以在兩個地方,一是其定義處,二是在建構函式中,兩者只能選其一。
胡說八道!
final變數可以在任何可以被始化的地方被始化,但只能被初始化一次.一旦被初始化後就不能再次賦
值(重新指向其它對象),作為成員變數一定要顯式初始化,而作為臨時變數則可以只定義不初始化(當然也不能引用)
即使是作為一個類中的成員變數,也還可以在初始化塊中初始化,所以"其初始化可以在兩個地方,一是其定義處,
二是在建構函式中,兩者只能選其一"是錯誤的.

作為成員變數時,final欄位可以設計不變類,是不變類的一個必要條件但不是一個充要條件.至少可以保證欄位不
會以setXXX()這樣的方式來改變.但無法保證欄位本身不被修改(除非欄位本身也是不變類);

對於方法參數的final變數:
對於方法參數的變數定義為final,90%以上的文章都說"當你在方法中不需要改變作為參數的物件變數時,明確使
用final進行聲明,會防止你無意的修改而影響到調用方法外的變數。"
胡說八道!

我不知道這個修改是說重新賦值還是修改對象本身,但無論是哪種情況,上面的說法都是錯誤的.
如果是說重新賦值,那麼:

Java代碼  
  1. public static void test(int[] x){   
  2.  x = new int[]{1,2,3};
      
  3. }   
  4.   
  5.    int[] out = new int[]{4,5,6};
      
  6.    test(out);   
  7.    System.out.println(out[0]);   
  8.    System.out.println(out[1]);   
  9.    System.out.println(out[2]);  
 public static void test(int[] x){  x = new int[]{1,2,3}; }    int[] out = new int[]{4,5,6};    test(out);    System.out.println(out[0]);    System.out.println(out[1]);    System.out.println(out[2]);

    調用test(out);無論如何也不會影響到外面變數out.你加不加final根本沒有意義.final只會強迫方法內

多聲明一個變數名而已,即把x = new int[]{1,2,3};改成int y = new int[]{1,2,3}; 其它沒有任何實際意義.
    如果說是修改對象本身:

Java代碼  
  1. public static void test(final int[] x){
      
  2.   x[0] = 100;   
  3.  }   
  4.     int[] out = new int[]{4,5,6};
      
  5.     test(out);   
  6.     System.out.println(out[0]);  
public static void test(final int[] x){  x[0] = 100; }    int[] out = new int[]{4,5,6};    test(out);    System.out.println(out[0]);

難道你用final修飾就不可以修改了?所以說對於方法參數中final是為了不影響調用方法外的變數那是胡說八道的.

那我們到底為什麼要對參數加上final?其實對方法參數加final和方法內變數加上final的作用是相同的,即為了將它們
傳給內部類回調方法:

Java代碼  
  1. abstract class ABSClass{   
  2.  public abstract void m();   
  3. }  
abstract class ABSClass{ public abstract void m();}

現在我們來看,如果我要實現一個在一個方法中匿名調用ABSClass.應該:

Java代碼  
  1. public static void test(String s){   
  2.      //或String s = "";
      
  3.   ABSClass c = new ABSClass(){   
  4.    public void m(){   
  5.       int x = s.hashCode();   
  6.   
  7.       System.out.println(x);   
  8.   
  9.    }   
  10.   };   
  11.   //其它代碼.
      
  12.  }  
public static void test(String s){     //或String s = "";  ABSClass c = new ABSClass(){   public void m(){      int x = s.hashCode();      System.out.println(x);   }  };  //其它代碼. }

注意這裡,一般而言,回調方法基本上是在其它線程中調用的.如果我們在上面的
  

Java代碼  
  1. ABSClass c = new ABSClass(){   
  2.  public void m(){   
  3.      int x = s.hashCode();   
  4.   
  5.      System.out.println(x);   
  6.   
  7.  }   
  8. };  
  ABSClass c = new ABSClass(){   public void m(){       int x = s.hashCode();       System.out.println(x);   }  };

  後面直接調用c.m();應該是沒有意義的.但這不重要,重要的是只要有可能是在其它線程中調用,那我們就必須

為s儲存引用控制代碼.

我們先來看GC工作原理,JVM中每個進程都會有多個根,每個static變數,方法參數,局部變數,當然這都是指參考型別.
基礎類型是不能作為根的,根其實就是一個儲存地址.

GC在工作時先從根開始遍曆它引用的對象並標記它們,如此遞迴到最末梢,所有根都遍曆後,沒有被標記到的對象說明沒
有被引用,那麼就是可以被回收的對象(有些對象有finalized方法,雖然沒有引用,但JVM中有一個專門的隊列引用它
們直到finalized方法被執行後才從該隊列中移除成為真正沒有引用的對象,可以回收,這個與本主題討論的無關,包括
代的劃分等以後再說明).這看起來很好.

但是在內部類的回調方法中,s既不可能是靜態變數,也不是方法中的臨時變數,也不是方法參數,它不可能作為根,在內部類
中也沒有變數引用它,它的根在內部類外部的那個方法中,如果這時外面變數重指向其它對象,則這個對象就失去了引用,
可能被回收,而由於內部類回調方法大多數在其它線程中執行,可能還要在回收後還會繼續訪問它.這將是什麼結果?

而使用final修飾符不僅會保持對象不會改變,而且編譯器還會持續維護這個對象在回調方法中的生命週期.所以這才是final
變數和final參數的根本意義.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.