java類的載入
⑴ java的類載入後什麼時候會被釋放
首先,"java的類在第一次需要創建類的實例(對象)時被載入"這個說的不對x0dx0ax0dx0ajava中類被使用就就會時就會被載入到內存(比如反射等)x0dx0a然後回答你的問題。x0dx0ax0dx0a首先要介紹下相關知識(基礎知識純屬拷貝):x0dx0ax0dx0a首先來了解一下jvm(java虛擬機)中的幾個比較重要的內存區域x0dx0ax0dx0a方法區:在java的虛擬機中有一塊專門用來存放已經載入的類信息、常量、靜態變數以及方法代碼的內存區域,叫做方法區。x0dx0a常量池:常量池是方法區的一部分,主要用來存放常量和類中的符號引用等信息。x0dx0a堆區:用於存放類的對象實例。x0dx0a棧區:也叫java虛擬機棧,是由一個一個的棧幀組成的後進先出的棧式結構,棧楨中存放方法運行時產生的局部變數、方法出口等信息。當調用一個方法時,虛擬機棧中就會創建一個棧幀存放這些數據,當方法調用完成時,棧幀消失,如果方法中調用了其他方法,則繼續在棧頂創建新的棧楨。x0dx0a 除了以上四個內存區域之外,jvm中的運行時內存區域還包括本地方法棧和程序計數器,這兩個區域與java類的生命周期關系不是很大,在這里就不說了,感興趣可以自己網路一下。x0dx0ax0dx0a其實類在JVM裡面有以下幾個階段:x0dx0ax0dx0a載入 -- 連接 -- 初始化 -- 使用 -- 卸載 x0dx0ax0dx0a主要給你說明卸載:x0dx0a在類使用完之後可能會被卸載,可能性如下:x0dx0a如果有下面的情況,類就會被卸載:x0dx0a該類所有的實例都已經被回收,也就是java堆中不存在該類的任何實例。x0dx0a載入該類的ClassLoader已經被回收。x0dx0a該類對應的java.lang.Class對象沒有任何地方被引用,無法在任何地方通過反射訪問該類的方法。x0dx0a 如果以上三個條件全部滿足,jvm就會在方法區垃圾回收的時候對類進行卸載,類的卸載過程其實就是在方法區中清空類信息,java類的整個生命周期就結束了。
⑵ java中反射實例類裝載的步驟及簡要闡述
java反射和類裝載
反射機制:
Person p=new Person();
這是什麼?當然是實例化一個對象了.可是這種實例化對象的方法存在一個問題,那就是必須要知道類名才可以實例化它的對象,這樣我們在應用方面就會受到限制.那麼有沒有這樣一種方式,讓我們不知道這個類的類名就可以實例化它的對象呢?Thank Goodness!幸虧我們用的是java, java就提供了這樣的機制.
1).java程序在運行時可以獲得任何一個類的位元組碼信息,包括類的修飾符(public,static等),基類(超類,父類),實現的介面,欄位和方法等信息.
2).java程序在運行時可以根據位元組碼信息來創建該類的實例對象,改變對象的欄位內容和調用對象方法.
這樣的機制就叫反射技術.可以想像光學中的反射,就像我們照鏡子,鏡子中又出現一個自己(比喻可能不太恰當,但是足以表達清楚意思了).反射技術提供了一種通用的動態連接程序組件的方法,不必要把程序所需要的目標類硬編碼到源程序中,從而使得我們可以創建靈活的程序.
反射的實現步驟( 不問不需要答) ,
1、獲取類的常用方式有三種: a) Class.forName("包名.類名"),最常用、推薦;b) 包名.類名.class 最簡捷;c) 對象.getClass 的方式獲得。
2、對象的實例化,上面已經獲取了類,只需要調用類的實例化方法,類.newInstance()便可。
3、獲取屬性和構造等,可以參考 JavaApi 的調用,類. getDeclaredFields,類. getConstructor(..)等。
Java的反射機制是通過反射API來實現的,它允許程序在運行過程中取得任何一個已知名稱的類的內部信息.反射API位於java.lang.reflect包中.主要包括以下幾類:
1).Constructor類:用來描述一個類的構造方法
2).Field類:用來描述一個類的成員變數
3).Method類:用來描述一個類的方法.
4).Modifer類:用來描述類內各元素的修飾符
5).Array:用來對數組進行操作.
Constructor,Field,Method這三個類都是JVM(虛擬機)在程序運行時創建的,用來表示載入類中相應的成員.這三個類都實現了java.lang.reflect.Member介面,Member介面定義了獲取類成員或構造方法等信息的方法.要使用這些反射API,必須先得到要操作的對象或類的Class類的實例.通過調用Class類的newInstance方法(只能調用類的默認構造方法)可以創建類的實例.這樣有局限性,我們可以先沖類的Class實例獲取類需要的構造方法,然後在利用反射來創建類的一個實例.
類載入機制:
類的載入機制可以分為載入-鏈接-初始化三個階段,鏈接又可以分為驗證、准備、解析三個過程。
載入:通過類的載入器查找並載入二進制位元組流的過程,在堆內存中的方法區生成 一個代表這個類的 java.lang.Class 對象,作為這個類的數據請求入口。(這里可以把上面類載入器載入文件的過程描述一下(參考版本一,不作重復))。
驗證:主要是對一些詞法、語法進行規范性校驗,避免對 JVM 本身安全造成危害; 比如對文件格式,位元組碼驗證,無數據驗證等。但驗證階段是非必須的,可以通過參數 設置來進行關閉,以提高載入的時效。
准備:對類變數分配內存,並且對類變數預初始化,初始化成數據類型的原始值, 比如 static int a=11,會被初始化成成 a=0;如果是 static double a =11,則會被初始化成 a=0.0; 而成員變數只會成實例化後的堆中初始化。
解析:把常量池中的符號引用轉換為直接引用的過程。
初始化:對類的靜態變數和靜態塊中的變數進行初始化。(上面的准備階段可以作為 預初始化,初始到變數類型的原值,但如果被 final 修飾會進行真正初始化)
上面載入、鏈接、初始化的各個階段並不是彼此獨立,而是交叉進行,這點很重要 。
***class.forName和 classloader的區別
Class.forName 和 ClassLoader 都是用來裝載類的,對於類的裝載一般為分三個階段載入、鏈接、編譯,它們裝載類的方式是有區別。
首先看一下 Class.forName(..),forName(..)方法有一個重載方法 forName(className,boolean,ClassLoader),它有三個參數,第一個參數是類的包路徑,第二個參數是 boolean
類型,為 true 地表示 Loading 時會進行初始化,第三個就是指定一個載入器;當你調用class.forName(..)時,默認調用的是有三個參數的重載方法,第二個參數默認傳入 true,第三個參數默認使用的是當前類載入時用的載入器。
ClassLoader.loadClass()也有一個重載方法,從源碼中可以看出它默認調的是它的重載 方法 loadClass(name, false),當第二參數為 false 時,說明類載入時不會被鏈接。這也是兩者之間最大區別,前者在載入的時候已經初始化,後者在載入的時候還沒有鏈接。如果你需要在載入時初始化一些東西,就要用 Class.forName 了,比如我們常用的驅動載入, 實際上它的注冊動作就是在載入時的一個靜態塊中完成的。所以它不能被 ClassLoader 載入代替。
⑶ java 類載入機制有什麼用
AVA類載入機制詳解
「代碼編譯的結果從本地機器碼轉變為位元組碼,是存儲格式發展的一小步,卻是變成語言發展的一大步」,這句話出自《深入理解JAVA虛擬機》一書,後面關於jvm的系列文章主要都是參考這本書。
JAVA源碼編譯由三個過程組成:
1、源碼編譯機制。
2、類載入機制
3、類執行機制
我們這里主要介紹編譯和類載入這兩種機制。
一、源碼編譯
代碼編譯由JAVA源碼編譯器來完成。主要是將源碼編譯成位元組碼文件(class文件)。位元組碼文件格式主要分為兩部分:常量池和方法位元組碼。
二、類載入
類的生命周期是從被載入到虛擬機內存中開始,到卸載出內存結束。過程共有七個階段,其中到初始化之前的都是屬於類載入的部分
載入----驗證----准備----解析-----初始化----使用-----卸載
系統可能在第一次使用某個類時載入該類,也可能採用預載入機制來載入某個類,當運行某個java程序時,會啟動一個java虛擬機進程,兩次運行的java程序處於兩個不同的JVM進程中,兩個jvm之間並不會共享數據。
1、載入階段
這個流程中的載入是類載入機制中的一個階段,這兩個概念不要混淆,這個階段需要完成的事情有:
1)通過一個類的全限定名來獲取定義此類的二進制位元組流。
2)將這個位元組流所代表的靜態存儲結構轉化為方法區的運行時數據結構。
3)在java堆中生成一個代表這個類的Class對象,作為訪問方法區中這些數據的入口。
由於第一點沒有指明從哪裡獲取以及怎樣獲取類的二進制位元組流,所以這一塊區域留給我開發者很大的發揮空間。這個我在後面的類載入器中在進行介紹。
2、准備階段
這個階段正式為類變數(被static修飾的變數)分配內存並設置類變數初始值,這個內存分配是發生在方法區中。
1、注意這里並沒有對實例變數進行內存分配,實例變數將會在對象實例化時隨著對象一起分配在JAVA堆中。
2、這里設置的初始值,通常是指數據類型的零值。
private static int a = 3;
這個類變數a在准備階段後的值是0,將3賦值給變數a是發生在初始化階段。
3、初始化階段
初始化是類載入機制的最後一步,這個時候才正真開始執行類中定義的JAVA程序代碼。在前面准備階段,類變數已經賦過一次系統要求的初始值,在初始化階段最重要的事情就是對類變數進行初始化,關注的重點是父子類之間各類資源初始化的順序。
java類中對類變數指定初始值有兩種方式:1、聲明類變數時指定初始值;2、使用靜態初始化塊為類變數指定初始值。
初始化的時機
1)創建類實例的時候,分別有:1、使用new關鍵字創建實例;2、通過反射創建實例;3、通過反序列化方式創建實例。
new Test();
Class.forName(「com.mengdd.Test」);
2)調用某個類的類方法(靜態方法)
Test.doSomething();
3)訪問某個類或介面的類變數,或為該類變數賦值。
int b=Test.a;
Test.a=b;
4)初始化某個類的子類。當初始化子類的時候,該子類的所有父類都會被初始化。
5)直接使用java.exe命令來運行某個主類。
除了上面幾種方式會自動初始化一個類,其他訪問類的方式都稱不會觸發類的初始化,稱為被動引用。
1、子類引用父類的靜態變數,不會導致子類初始化。
執行結果:
MIGU
用final修飾某個類變數時,它的值在編譯時就已經確定好放入常量池了,所以在訪問該類變數時,等於直接從常量池中獲取,並沒有初始化該類。
初始化的步驟
1、如果該類還沒有載入和連接,則程序先載入該類並連接。
2、如果該類的直接父類沒有載入,則先初始化其直接父類。
3、如果類中有初始化語句,則系統依次執行這些初始化語句。
在第二個步驟中,如果直接父類又有直接父類,則系統會再次重復這三個步驟來初始化這個父類,依次類推,JVM最先初始化的總是java.lang.Object類。當程序主動使用任何一個類時,系統會保證該類以及所有的父類都會被初始化。
⑷ java 類載入先後順序
父類態代碼塊
父類靜態屬性
//以上根據代碼順序
子類靜態屬性
子類靜態代碼塊
//以上根據代碼順序
父類屬性
父類代碼塊
//以上根據代碼順序
父類構造器
子類屬性
子類代碼塊
//以上根據代碼順序
子類構造方法
子類方法
父類方法
父類靜態方法
子類靜態方法
package com.zz;
/**
* Created by @摩西摩西 2020/3/21 22:29
**/
public class Father {
public String name=Son.getmetod("父類屬性");
public Father(){
System.out.println("父類構造器");
}
{
System.out.println("父類代碼塊");
}
public void fatherMethod(){
System.out.println("父類方法");
}
static {
System.out.println("父類態代碼塊");
}
static String name1=Son.getmetod("父類靜態屬性");
public static void fatherStaticMethod(){
System.out.println("父類靜態方法");
}
}
package com.zz;
/**
* Created by @摩西摩西 2020/3/21 22:32
**/
public class Son extends Father {
public String name = getmetod("子類屬性");
public Son() {
System.out.println("子類構造方法");
}
{
System.out.println("子類代碼塊");
}
public void sonMethod(){
System.out.println("子類方法");
}
public static String name1 = getmetod("子類靜態屬性");
static {
System.out.println("子類靜態代碼塊");
}
public static void sonStaticMethod() {
System.out.println("子類靜態方法");
}
public static String getmetod(String s) {
System.out.println(s);
return s;
}
public static void main(String[] args) {
Son son= new Son();
son.sonMethod();
son.fatherMethod();
fatherStaticMethod();
sonStaticMethod();
}
}
⑸ java類載入順序
記住 3 條原則:
1、父類優先於子類
2、屬性優先於代碼塊優先於構造方法
3、靜態優先於非靜態
因此,類載入順序為:
父類靜態變數->父類靜態語句塊->子類靜態變數->子類靜態語句塊->父類普通成員變數->父類動態語句塊->父類構造器->子類普通成員變數->子類動態語句塊->子類構造器