static final 和final的區別


學習java的時候常常會被修飾符搞糊塗,這里總結下static final和final的區別。

static是靜態修飾關鍵字,可以修飾變量和程序塊以及類方法:

  當定義一個static的變量的時候jvm會將將其分配在內存堆上,所有程序對它的引用都會指向這一個地址而不會重新分配內存;

  當修飾一個程序塊的時候(也就是直接將代碼寫在static{...}中)時候,虛擬機就會優先加載靜態塊中代碼,這主要用於系統初始化;

  當修飾一個類方法時候你就可以直接通過類來調用而不需要新建對象。

final可以修飾變量、方法及類:

  當定義一個final變量時,jvm會將其分配到常量池中,程序不可改變其值;

  當修飾一個方法時,該方法在子類中將不能被重寫;

  當修飾一個類時,該類不能被繼承。

static變量

  按照是否靜態的對類成員變量進行分類可分兩種:一種是被static修飾的變量,叫靜態變量或類變量;另一種是沒有被static修飾的變量,叫實例變量。

  兩者的區別是:

    對於靜態變量在內存中只有一個拷貝(節省內存),JVM只為靜態分配一次內存,在加載類的過程中完成靜態變量的內存分配,可用類名直接訪問(方便),當然也可以通過對象來訪問(但是這是不推薦的)。

    對於實例變量,沒創建一個實例,就會為實例變量分配一次內存,實例變量可以在內存中有多個拷貝,互不影響(靈活)。

static方法

  靜態方法可以直接通過類名調用,任何的實例也都可以調用,因此靜態方法中不能用this和super關鍵字,不能直接訪問所屬類的實例變量和實例方法(就是不帶static的成員變量和成員成員方法),只能訪問所屬類的靜態成員變量和成員方法。

  因為static方法獨立於任何實例,因此static方法必須被實現,而不能是抽象的abstract。

 

1、static 強調只有一份,final 說明是一個常量,final定義的基本類型的值是不可改變的,但是fianl定義的引用對象的值是可以改變的,下面舉個例子來說明:

package DifStaticFinalAndFinal;

class SelfCounter {
    private static int counter;
    private int id=counter++;
    
    public String toString(){
        return "SelfCounter: "+id;
    }
}
package DifStaticFinalAndFinal;

class WithFinalFields {
    static final SelfCounter wffs=new SelfCounter();
    final SelfCounter wff=new SelfCounter();
    
    public String toString(){
        return "wff= "+wff+",\n wffs= "+wffs;
    }
}

主函數:

package DifStaticFinalAndFinal;

public class StaticFinal {
    public static void main(String[] args) {
        System.out.println("First Object:");
        System.out.println(new WithFinalFields());
        System.out.println("Second Object:");
        System.out.println(new WithFinalFields());
    }
}

運行結果:

First Object:
wff= SelfCounter: 1,
wffs= SelfCounter: 0
Second Object:
wff= SelfCounter: 2,
wffs= SelfCounter: 0

 

  分析為什么wff兩次的運行結果不同,而wffs兩次的運行結果相同?

    因為wffs這個容器是用static final來定義的,static 強調只有一份,因此只有一個值,而final修飾的引用是可以變化的,因此wff的值是可以變化的,這也是final修飾基本類型和引用的不同。

2、在方法中將參數指明為final時,在使用該方法時,可以讀參數但是無法使用該參數。

package cn.qdu.chapter7_example;

public class FinalArguments {
    void with(final Gizmo g){
        
    }
    
    void without(Gizmo g){
        g=new Gizmo();
        g.spin();
    }
    
    int g(final int i){return i+1;}
    public static void main(String[] args) {
        FinalArguments bf=new FinalArguments();
        bf.with(null);//不管這里的參數換成什么,都是執行void(final Gizmo g)方法,無法更改參數
        bf.without(null);
        
    }
}

3、為什么使用final方法呢?

  原因有兩個。

    其一是把方法鎖定,確保在繼承中使用方法行為不變,並且不會被覆蓋;

    其二是效率,如果一個方法指明為final,就是同意編譯器將針對該方法的所有調用都轉為內嵌調用。 轉為內嵌調用的目的是節省開銷,因為編譯器發現一個final方法調用命令時,會跳過程序代碼這種正常方式而執行方法調用機制(將參數壓入棧,跳至方法代碼處並執行,然后跳回並清理棧中的參數,處理返回值),並且以方法體中的實際代碼的副本來替代方法調用。但是如果一個方法很大,程序很膨脹,就會看不到內嵌帶來的任何性能的提高。

4、final和private關鍵字

  類中所有private方法都隱式地指定為是final的,因為private關鍵字只能被本類調用,其他類中的方法也無法覆蓋private修飾的方法,因此和加上final效果是一樣的。

5、當某個類的整體定義為final時,表明該類不能被繼承,方法不能被覆蓋,且final類中的所有方法都隱式指定為是final的,方法聲明為final后還可以有效地“關閉”動態綁定。

6、static加載類的動作只發生一次。

     最后,說了這么多,也要對final做一個客觀的評價。若方法或類不想讓別人來繼承和修改,設定為final是明智的,但是在團體中這樣會阻礙其他程序員通過你想不到的合理的途徑來復用類,這樣的話final方法就顯得過於嚴苛了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM