對修飾符final和static的淺析

通常在進行分析的時候,會從三個方面進行分析:類、方法(構造方法、成員方法)、變量(成員變量(靜態變量、實例變量)、局部變量)。java

1、static修飾符:

被static修飾的變量和方法,被類的全部實例所共享。加載類時只分配一次內存。dom

  1. 靜態變量:能夠直接經過類名來訪問 Person.a;
  2. 靜態方法:能夠直接經過類名來訪問 Person.say();靜態方法內,能夠訪問靜態變量,可是不能使用this關鍵字不能訪問實例變量,由於被全部實例所共有,沒法判斷屬於哪一個實例對象
  3. 靜態代碼塊:java虛擬機在加載類時就執行代碼塊

2、final修飾符:

有final修飾表示最終(不可再修改)的意思:函數

  1. final類:不能被繼承(即沒有子類);
  2. final方法:不能被子類的方法覆蓋(final不用來修飾構造方法,父類與子類之間的構造方法不存在覆蓋關係,final修飾是沒有意義的);
  3. final修飾的變量:即常量,常量只能被賦值一次,以後不能改變。
    靜態常量屬於編譯時常量(final static int a=2*3;在編譯的時就能計算出具體的值),在編譯的時候將這個值就放入到常量池中,a被訪問時類是不會被初始化的(這是類的被動使用)。訪問類的靜態變量或靜態方法的時候類會被初始化(這是類的主動使用)。而靜態變量只會在類初始化的時候纔會被賦值)。具體細節請參考「類的生命週期」---加載(堆區、方法區)--鏈接(驗證、準備(靜態變量分配內存、設置默認初始值爲0)、解析(符號引用替換爲直接引用,即指針指向方法區的內存位置))--初始化(程序對類或接口主動使用的時候纔會被初始化。

3、類的初始化時機:

類的初始化即java虛擬機爲類的static靜態變量賦予初始值(這和準備階段設置默認初始值爲0是不同的)。只有類的主動使用纔會初始化類this

1.類的主動使用(6種):

  1. 建立類的實例:用new語句建立實例 Person ps=new Person();
  2. 調用類的靜態變量或對靜態變量賦值:指針

    public class Person{ 
    static int a=2*3;  //這和final static int a=2*3;是有區別的
    static{  //Java在定義一個類的時候裏面只能放方法和屬性,這是規定死了的。System.out.println()是在調用一個叫println的函數,這裏是函數的調用而不是類裏面定義一個函數。因此須要用static代碼塊
        System.out.println("init Person"); //static聲明的靜態代碼塊,使得類在初始化的時候會被調用而不須要建立實例對象。它這時候就不在任何一個方法中。
    } 
    }

    調用的時候寫:code

    System.out.println("a="+Person.a); //這樣就能夠在不new一個Person實例的狀況下,來初始化Person類了。
  3. 調用類的靜態方法
  4. 調用java API中的反射方法:Class.forName("Person");
  5. 初始化子類的時候會先初始化父類(但"父類"是接口的時候,不會先初始化它所實現的接口的,只有在程序在使用接口的靜態變量時纔會使靜態接口初始化)
  6. java虛擬機啓動時被標明爲啓動類的類

2.類的被動使用:

  1. final類型的靜態變量在編譯的時候能計算出值(即編譯時常量,在編譯的時候將這個值就放入到常量池中了):
    注: final類型的靜態變量在編譯的時候不能計算出變量的值(即運行時常量)的時候是會被初始化的對象

    final static int a=2*3;                 //變量a是編譯時常量
      final static int a=(int)Math.random();  //變量a不是是編譯時常量(即運行時常量)
  2. "父類"是接口的時候,不會先初始化它所實現的接口的,只有在程序在使用接口的靜態變量時纔會使靜態接口初始化
  3. ClassLoader類的loadClass("Person")方法的時候,只是對類的加載,不是初始化。Class.forName("Person");纔會初始化
相關文章
相關標籤/搜索