final,finalize,finally 的區別

suwu150發表於2016-08-20

final  finalize  finally  的區別

一、性質不同
(1)final為關鍵字;
(2)finalize()為方法;
(3)finally為為區塊標誌,用於try語句中;
二、作用
(1)final為用於標識常量的關鍵字,final標識的關鍵字儲存在常量池中(在這裡final常量的具體用法將在下面進行介紹);

(2)finalize()方法在Object中進行了定義,用於在物件“消失”時,由JVM進行呼叫,用於對物件進行垃圾回收,類似於C++中的解構函式;使用者自定義時,用於釋放物件佔用的資源(比如進行I/0操作)

 (3)finally{}用於標識程式碼塊,與try{}進行配合,不論try中的程式碼執行完或沒有執行完(這裡指有異常),該程式碼塊之中的程式必定會進行;
三、詳解

(一) final詳解
       final 是一種修飾符(關鍵字),final具有"不可改變的"含義,它可以修飾非抽象類、非抽象成員方法和變數。如果一個類被宣告為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個類不能既被宣告為 abstract的(能夠被繼承,而且可能含有抽象方法),又被宣告為final(不能被繼承)的。將變數或方法宣告為 final,可以保證它們在使用中不被改變。被宣告為final的變數必須在宣告時給定初值,而在以後的引 用中只能讀取,不可修改。被宣告為final的方法也同樣只能使用,不能過載
       final定義變數問題
             3.1.1  final定義基本型別變數時,要求變數初始化必須在宣告時或者建構函式中,不能用於其它地方。該關鍵字定義的常量,除了初始化階段,不能更改常量的值。
             3.1.2  final定義物件的引用,該引用的初始化與定義常量時的要求一致;該關鍵字定義的物件內容可以改變,但是引用指向的地址不能改變;
       final定義引數問題

             3.2.1 如果傳入該引數定義的變數時,方法不能對該引數內容進行修改,與定義變數的修改規則相同;java方法中傳遞基本型別時是傳值的,java方法對於物件的傳遞是傳參的;歸根結底,java中方法的傳遞是依靠傳遞“副本”:對於基本型別,首先建立一個Copy,並將傳入的值賦值給Copy,然後對Copy進行操作;對於物件型別,首先建立一個引用Copy,並將傳入的物件引用賦值給Copy;
        如下面所示  final修飾的屬性(成員變數)賦值的位置:
            非靜態的成員變數
            1.宣告的同時
            2.匿名程式碼塊
            3.構造器(類中出現的所有構造器)
            靜態的成員變數
            1.宣告的同時
            2.static程式碼塊          
                  a. final可以修飾靜態變數、例項變數、區域性變數;
                  b. final變數都必須顯示初始化,否則會導致編譯錯誤;
                     1) 靜態變數,定義變數時進行初始化或者static程式碼塊中賦值;
                     2) 例項變數,可以在定義變數時,或者在構造方法中進行初始化;
                  c. final變數只能賦一次值。
eg1:

                     public class Sample {
                            private final int var1 = 1;
                            public Sample() {
                                 var1 = 2;                //編譯出錯,不允許改變var1例項變數的值;
                            }
                            public void method(final int param) {
                                 final int var2 = 1;         
                                 var2++;                  //編譯出錯,不允許改變var2區域性常量的值
                                 param++;                 //編譯出錯,不允許改變final型別引數的值;
                            }
                     }
eg2:
            public class Sample {
                            final int var1;               //定義var1例項常量
                            final int var2 = 0;           //定義並初始化var2例項常量
                            Sample() {
                                  var1 = 1;               //初始化var1例項常量
                            }
                            Sample(int x) {
                                  var1 = x;                //初始化var1例項常量
                            }
                     } 
            3.1.1定義方法

                (1)使用final關鍵字定義的方法,不能被子類繼承,某些情況下,出於安全原因,父類不允許子類覆蓋某個方法, 此時可以把這個方法宣告為final型別。例如在 java.lang.Object類中,getClass()方法為final型別;

                (2)允許編譯器將所有對此方法的呼叫轉化為inline(行內)行為,即可以將此方法直接複製在呼叫處,而不是進行例行的方法呼叫(儲存斷點、壓棧),這樣會使程式的效率升高。但是---------如果過多的話,這樣會造成程式碼膨脹,反而會影響效率,所以該方法要慎用。。

                (3) final不能用來修飾構造方法,因為"方法覆蓋"這一概念僅適用於類的成員方法,而不適用於類的構造方法,父類的構造方法和子類的構造方法之間不存在覆蓋關係. 因此用final修飾構造方法是無意義的。父類中用private修飾的方法不能被子類的方法覆蓋,因此private型別的方法預設是final型別的。

           4.1.1定義類

                      繼承關係的弱點是打破封裝,子類能夠訪問父類的方法,而且能以方法覆蓋的方式修改實現細節。在以下情況下,可以考慮把類定義為final型別,使得這個類不能被繼承。
                  . 子類有可能會錯誤地修改父類的實現細節;
                  . 出於安全,類的實現細節不允許有任何改動;
                  . 在建立物件模型時,確信這個類不會再被擴充套件;

           例如JDK中java.lang.String類被定義為final型別;

                    一個任何final類無法被任何人繼承,這也就意味著此類在一個繼承樹中是一個葉子類,並且此類被認為是很完美的,不需要進行任何修改(總之是不推薦使用)               
(二)、finalize詳解
               finalize 屬於方法名。Java 技術允許使用 finalize() 方法在垃圾收集器將物件從記憶體中清除出去 之前做必要的清理工作。
               這個方法是由垃圾收集器在確定這個物件沒有被引用時對這個物件呼叫的。它 是在 Object 類中定義的,因此所有的類都繼承了它。子類覆蓋 finalize() 方法以整理系統資源或者 執行其他清理工作。finalize() 方法是在垃圾收集器刪除物件之前對這個物件呼叫的。
               屬於java.lang.Object類,檢視API能夠知道它的定義如下:

                                protected void finalize() throws Throwable { }


       finalize()方法的作用是什麼呢?
       finalize()方法是在GC清理它所從屬的物件時被呼叫的,如果執行它的過程中丟擲了無法捕獲的異常(uncaught exception),GC將終止對該物件的清理,並且該異常會被忽略;直到下一次GC開始清理這個物件時,它的finalize()會被再次呼叫。
      請看下面的示例:  

   package sample;      
   public class FinalizeTest
    {
              // 重寫finalize()方法
           protected void finalize() throws Throwable
           {
            System.out.println("執行了finalize()方法");
           }
           public static void main(String[] args)
          {
            FinalizeTest fl = new FinalizeTest();
            fl = null;
            System.gc();//呼叫垃圾回收機制
          }
   }
執行結果如下:即呼叫了重寫的方法,顯示:執行了finalize()方法


程式呼叫了java.lang.System類的gc()方法,引起GC的執行,GC在清理ft物件時呼叫了它的finalize()方法,因此才有了上面的輸出結果。呼叫System.gc()等同於呼叫下面這行程式碼:
           Runtime.getRuntime().gc();
呼叫它們的作用只是建議垃圾收集器(GC)啟動,清理無用的物件釋放記憶體空間,但是GC的啟動並不是一定的,這由JAVA虛擬機器來決定。直到 JAVA虛擬機器停止執行,有些物件的finalize()可能都沒有被執行過,那麼怎樣保證所有物件的這個方法在JAVA虛擬機器停止執行之前一定被呼叫呢?答案是我們可以呼叫System類的另一個方法:
         public static void runFinalizersOnExit(boolean value) {
         //other code
         }
給這個方法傳入true就可以保證物件的finalize()方法在JAVA虛擬機器停止執行前一定被執行了,不過遺憾的是這個方法是不安全的,它會導致有用的物件finalize()被誤呼叫,因此已經不被贊成使用了。由於finalize()屬於Object類,因此所有類都有這個方法,Object的任意子類都可以重寫(override)該方法,在其中釋放系統資源或者做其它的清理工作,如關閉輸入輸出流。
(三)、finally詳解

    finally 在異常處理時提供 finally 塊來執行必須進行的操作。finally{}用於標識程式碼塊,與try{}進行配合,不論try中的程式碼執行完或沒有執行完(這裡指有異常),該程式碼塊之中的程式必定會進行;如果丟擲一個異常,那麼相匹配 的 catch 子句就會執行,然後控制就會進入 finally 塊(如果有的話)。如果不丟擲異常,則會從try塊中直接跳轉到finally塊中。如下面程式段:

(1)有異常時:

package sample;
public class TryCatchFinally
{
//用於輸出,定義函式專門用於輸出
   public static void sop(Object o)
   {
      System.out.println(o);
   }
   //定義函式用於表示出錯資訊
   static public int  div(int a,int b)
   {
      int res=0;
      try
      {
              res=a/b;
      }
      catch(ArithmeticException e)
      {
      sop("in catch");
      }
      finally
      {
        sop("in Finally");
      }
return res;
   }
   //主函式
   public static void main(String[] args)
   {
    sop("Main0:"+div(10,0));
   }
}
結果如下所示:

如下程式碼,不存在異常時

package sample;
public class FinallyTest
{
//用於輸出,定義函式專門用於輸出
   public static void sop(Object o)
   {
      System.out.println(o);
   }
   //定義函式用於表示出錯資訊
   static public int  div(int a,int b)
   {
      int res=0;
      try
      {
              res=a/b;
      }
      catch(ArithmeticException e)
      {
      sop("in catch");
      }
      finally
      {
        sop("in Finally");
      }
return res;
   }
   //主函式
   public static void main(String[] args)
   {
    sop("Main0:"+div(10,2));
   }
}
執行結果如下所示:沒有執行catch塊,但和有異常時一樣,同樣執行finally塊




以上就是三者的區別

   

相關文章