1. 程式人生 > >深入java static關鍵字 淺析java類載入機制(解答java靜態方法或變數無法訪問非靜態資料)

深入java static關鍵字 淺析java類載入機制(解答java靜態方法或變數無法訪問非靜態資料)

想要清晰理解java語法,不瞭解java和jvm的機制是不行的,以前不理解java中用static修飾方法和變數為什麼不可以訪問非靜態方法和資料,現在明瞭,如果你也有相同的困惑,這篇部落格足以解惑,原創不易,轉載請宣告出處。
本文分為3大部分

  • static的用法和例子
  • 簡析java類載入機制
  • 為何java中static靜態資料無法訪問非static資料,但是反過來卻可以

    1.static用法和解析

    類中靜態資料是在類被載入(ClassLoader類的loadClass方法,大致的情況我們待會在第二部分說明)時初始化的。為什麼會載入這個類,因為我們嘗試使用這個類的時候,類就會被載入進記憶體裡。
    舉例說明:

/**
 1. Created by Yangsheng on 2017/4/12.
 */
public class StaticTest {
    //private int allNum = changeNum *2;
    private int changeNum = 0;
    public static int X;//X放在後面會提醒X非法
    public static int Y = X * 2;
    StaticTest(int changeNum){
        this.changeNum = changeNum;
    }
    int getChangeNum(){
        return
changeNum; } void setChangeNum(int changeNum){ this.changeNum = changeNum; } { X = 10;//非static模組可以訪問static變數 changeNum = 100; } { changeNum = 99; } static { Y = 1; System.out.println("YYY"); } static { X = 30
; //changeNum = 100;static模組不可以訪問非static變數 } int getLength(String s){ return s.length()*2; } public static void main(String[] args) { System.out.println(X); //輸出60 StaticTest staticTest = new StaticTest(1); System.out.println(staticTest.Y); System.out.println(X); System.out.println(staticTest.getChangeNum()); /** * static屬性會在類載入進如虛擬機器的時候宣告,必須按照順序宣告,因為虛擬機器一看到static就會將它對應的屬性和函式塊作為 * * static{}(靜態方法會在JVM將類載入進來的時候按照順序從上往下執行 * {}會在物件生成的時候按照順序從上往下執行 * static{} * */ } }

這裡寫圖片描述
我們先看一下執行的結果:
這裡寫圖片描述
1.程式執行的時候,我們從main方法進入,因為main方法是在StsticTest類中的,JVM會先載入StaticTest這一個類,當程式在載入Static變數的時候,JVM會將類裡面宣告到的靜態變數在棧記憶體裡面開闢空間儲存好,這裡就是X、Y,可是這個時候並沒有初始化。這裡我們還需要注意到:
我們在程式裡面聲明瞭:
public static int X;
public static int Y = X * 2;
若X放在後面會提醒X非法,因為虛擬機器在載入類的時候是將static變數從上到下依次進棧,如果還沒宣告就使用那肯定就非法了。

2.程式會從上到下依次執行static的程式碼塊(因為static的變數和塊是屬於類的),這個時候Y = 1;X = 30;

3.我們在執行new StaticTest( ),在堆記憶體裡面開闢空間生成StaticTest物件的時候,程式執行建構函式 StaticTest(int changeNum),但是在正式構造物件之前,會將非靜態程式碼塊
這裡寫圖片描述
依次執行,執行完畢之後,接著在棧記憶體中初始化int changeNum = 0(初始化變數,如果變數是物件的話,會宣告一個這個物件的控制代碼,但是不會為這個物件分配空間)。接著繼續執行建構函式構造物件,以確保構造出來的物件沒有問題。

2.簡析java類載入機制(Classloader具體的實現以後學完java虛擬機器再寫)

類載入器顧名思義,類載入器(classloader)用來載入 Java 類到 Java 虛擬機器中。一般來說,Java 虛擬機器使用 Java 類的方式如下:

  • Java 源程式(.java 檔案)在經過 Java 編譯器編譯之後就被轉換成 Java 位元組程式碼(.class 檔案)。
  • 類載入器負責讀取 Java 位元組程式碼,並轉換成 java.lang.Class類的一個例項。每個這樣的例項用來表示一個 Java 類。通過此例項的 newInstance()方法就可以創建出該類的一個物件。
  • 實際的情況可能更加複雜,比如 Java 位元組程式碼可能是通過工具動態生成的,也可能是通過網路下載的。
    基本上所有的類載入器都是 java.lang.ClassLoader類的一個例項。下面詳細介紹這個 Java 類。
    java.lang.ClassLoader類介紹java.lang.ClassLoader類的基本職責就是根據一個指定的類的名稱,找到或者生成其對應的位元組程式碼,然後從這些位元組程式碼中定義出一個 Java 類,即 java.lang.Class類的一個例項。除此之外,ClassLoader還負責載入 Java 應用所需的資源,如影象檔案和配置檔案等。不過本文只討論其載入類的功能。為了完成載入類的這個職責,ClassLoader提供了一系列的方法,挑選其中幾個很重要的方法先看一下:
  • 1. getParent() 返回該類載入器的父類載入器。
  • 2. loadClass(String name) 載入名稱為 name的類,返回的結果是java.lang.Class類的例項。
  • 3. findClass(String name)查詢名稱為 name的類,返回的結果是java.lang.Class類的例項。
  • findLoadedClass(String name)查詢名稱為 name的已經被載入過的類,返回的結果是 java.lang.Class類的例項。
  • 4. defineClass(String name, byte[] b, int off, int len)把位元組陣列 b中的內容轉換成 Java 類,返回的結果是 java.lang.Class類的例項。這個方法被宣告為 final的。
  • 5. resolveClass(Class c)連結指定的 Java 類。

類的生命週期:
這裡寫圖片描述

3.為何java中static靜態資料無法訪問非static資料,但是反過來卻可以

終於到了解答自己內心困惑的時候了
1.在類被呼叫的時候,類載入器根據一個指定的類的名稱,找到或者生成其對應的位元組程式碼(.class檔案),然後從這些位元組程式碼中定義出一個 Java 類,即 java.lang.Class類的一個例項。
這裡寫圖片描述
載入完畢後,只要不解除安裝這個類,那這個類就會一直存在記憶體當中,所以我們常常會說“類只會被載入一次”。(
類解除安裝滿足的條件:
- 該類所有的例項都已經被回收,也就是java堆中不存在該類的任何例項。
- 載入該類的ClassLoader已經被回收。
- 該類對應的java.lang.Class物件沒有任何地方被引用,無法在任何地方通過反射訪問該類的方法。
- )
2.在定義類的時候,虛擬機器會將位元組碼對應的static變數從上到下依次初始化,然後執行static程式碼塊。這個時候記憶體中就有了這些Static變數。

3.在類載入完畢,同時我們生成這個類的例項的時候,這個時候類執行建構函式,在構造物件之前,會先執行非靜態程式碼塊,然後初始化非static變數,最後在執行建構函式裡面的構造內容構造出一個物件。

重點:

1.類在載入的時候會初始化static變數,但是沒有對非static變數宣告和初始化,如果我們在static方法中呼叫類非static變數的話,就極有可能出錯,當然java是不允許的。所以在編譯階段,就會報錯。

2.我們是先“生成”類,在“生成”物件(類的例項);所以當我們用非static的類方法去訪問類的static變數的時候(static變數是在類載入的時候初始化的),static變數一定是存在記憶體裡面了,不會出現任何問題。

3.為什麼會有static關鍵字(個人理解):因為針對一些開發場景,我們想要一些關鍵資料在類載入的時候就被初始化,而且這些資料類物件的所有成員都可以訪問;例如遊戲。
對於static方法,我認為是:假如我們想“製造”一些方法,這些方法不想通過生產例項去呼叫,想直接使用,在類載入的時候就硬編碼進去,提升效率,而且不許要複雜的物件生成的過程。天才的設計師們就用static這條規則滿足我們的需要。