類在執行之前會執行三個步驟:加載 -> 連接 -> 初始化
1.java中類的加載
java 類的加載指的是將類的.class文件中的二進制數據讀入到內存中,將其放在運行時數據區的方法區內,然後在堆區創建一個Class對象,用來封裝類在方法區的數據結構。可以把堆區的Class理解爲方法區的一面鏡子,對應方法區的類的數據結構,通過這面鏡子實現類的調用。
加載.class文件的多種方式:
1.從本地系統中直接加載
2.通過網絡下載.class文件
3.從zip,jar里加載.class文件
4.從專有的數據庫中提取.class文件
5.將java源文件動態編譯爲.class文件
類加載的最終結果是生成位於堆中的Class對象,Class對象封裝了類在方法區內的數據結構,並且向java程序員提供了訪問方法區內的數據結構的接口
查看源碼得到Class對象只能由java虛擬機來創建,其構造函數私有化
2.連接
1. 驗證:確保被加載的類的正確性(主要防止噁心的class文件被加載)
2. 準備:爲類的靜態變量分配內存,並將其初始化爲默認值
3. 解析:把類中的符合引用轉換爲直接引用
3.初始化
爲類的靜態變量賦予正確的初始值
所有的java虛擬機實現必須在每一個類或接口被java程序“首次主動使用”時才初始化
java對類的使用方式分爲:主動使用,被動使用
主動使用有六種:(除這6種外,其他都是被動使用)
1。創建類的實例
2。訪問某個類或接口的靜態變量或對該靜態變量賦值
3。調用類的靜態方法
4。反射
5。初始化類的子類
6。java虛擬機啓動時被標註位啓動類的類
4.類加載器Classloader
類加載器類型
1.java虛擬機自帶的類加載器:
-----根類加載器Bootstrap
該類加載器沒有父類加載器,它負責加載虛擬機的核心類庫,如java.lang.String等,根類加載器用於在啓動JVM時加載類,以使JVM能正常工作,因而它是用Native(c++)代碼實現的,最早被創建出來,處於最底層。它並沒有繼承java.lang.ClassLoader類。
-----擴展類加載器Extension
該類加載器的父類加載器是根類加載器。它從java.ext.dirs系統屬性所指定的目錄獲取加載類庫或從JDK的安裝目錄的jre\lib\ext子目錄下加載類庫。如果把jar放到這個目錄下,也會自動用擴展類加載器加載。擴展類加載器是java類,是java.lang.ClassLoader類的子類
-----系統類加載器System
也成爲應用類加載器,它的父類加載器是擴展類加載器,它將加載CLASSPATH中配置的目錄和jar文件,它是用戶自定義類加載器的默認父類加載器,系統類加載器是java類,是java.lang.ClassLoader類的子類
2.用戶自定義類加載器:是java.lang.ClassLoader的子類,可以定義類加載器
類加載器機制
類加載器用來把類加載到java虛擬機中,類加載過程採用的是父親委託機制,這種機制能很好的保證java平臺的安全,因爲在這種機制下用戶定義的類加載器不可能加載應由父加載器加載的可靠類,如java.lang.String總是由根類加載器加載。在此委託機制中,除了java的根類加載器以外,其餘的加載器都有且只有一個父加載器。
自定義類加載器
用戶自定義類加載器需要滿足父委託機制,默認系統類加載器爲其父加載器。要實現自定義加載器,只需要集成ClassLoader類,然後覆蓋findClass(String name)方法,該方法根據參數指定加載類的名字,返回對應的Class對象的引用
自定義類加載器類: MyClassLoader
-
package com.longpo.classloader;
-
-
import java.io.File;
-
import java.io.FileInputStream;
-
import java.io.FileNotFoundException;
-
import java.io.IOException;
-
import java.io.InputStream;
-
-
public class MyClassLoader extends ClassLoader {
-
-
private String name; //類加載器的名字
-
-
private String path="D:\\"; //加載類的路徑
-
-
private final String type=".class"; //class文件的擴展名
-
-
public MyClassLoader(String name)
-
{
-
super();
-
this.name=name;
-
}
-
-
//指定父類加載器
-
public MyClassLoader(ClassLoader parent ,String name)
-
{
-
super(parent);//顯示指定該加載器的父加載器
-
this.name=name;
-
}
-
-
//設置加載class的路徑
-
public String getPath() {
-
return path;
-
}
-
-
public void setPath(String path) {
-
this.path = path;
-
}
-
-
@Override
-
public String toString() {
-
return "MyClassLoader :" + name ;
-
}
-
-
-
//讀取對應class文件的二進制數據---這裏簡單讀取
-
private byte[] loadClassData(String name)
-
{
-
//根據要加載的類名找到對應的文件
-
name=name.replace(".", "\\");//com.longpo.test目錄結構是\\
-
-
File file=new File(path+name+type);
-
//根據文件大小來創建字節數組
-
byte[]bytes=new byte[(int)file.length()] ;
-
-
try {
-
InputStream is = new FileInputStream(file);
-
int len=is.read(bytes);//返回讀取字節的長度
-
-
is.close();
-
} catch (FileNotFoundException e) {
-
e.printStackTrace();
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
-
return bytes;
-
}
-
-
//一定要重寫該方法
-
@Override
-
public Class<?> findClass(String name) throws ClassNotFoundException {
-
//得到class文件的二進制數據
-
byte[]data=loadClassData(name);
-
-
return this.defineClass(name, data, 0,data.length);//返回class對象的引用
-
}
-
-
-
}
-
定義一個測試加載類:Simple
-
package com.longpo.classloader;
-
-
public class Simple {
-
-
public Simple( )
-
{
-
System.out.println("Simple的類加載器的名字是:"+this.getClass().getClassLoader());
-
}
-
}
測試自定義加載類:Test
-
package com.longpo.classloader;
-
-
public class Test {
-
-
public static void main(String[] args) throws Exception{
-
-
//loader1的父加載器默認是系統加載器--上面還有系統加載器,擴展加載器,根加載器
-
MyClassLoader loader1=new MyClassLoader("loader1");
-
-
loader1.setPath("D:\\lib\\loader1\\");//loader1加載路徑
-
-
//loader2的父加載器爲loader1--上面有loader1加載器,系統加載器,擴展加載器,根加載器
-
MyClassLoader loader2=new MyClassLoader(loader1,"loader2");
-
-
loader2.setPath("D:\\lib\\loader2\\");//loader2加載路徑
-
-
//loader3的父加載器爲null,即根加載器--上面只有根加載器
-
MyClassLoader loader3=new MyClassLoader(null,"loader3");
-
loader3.setPath("D:\\lib\\loader3\\");//loader3加載路徑
-
-
test(loader2);
-
test(loader3);
-
}
-
-
-
public static void test(ClassLoader loader)throws Exception{
-
-
Class clazz=loader.loadClass("com.longpo.classloader.Simple");//loadClas會自動調用findClass方法
-
-
Object object=clazz.newInstance();
-
-
}
-
}
此時把編譯好的class文件發到對應加載器目錄的文件夾裏面,我用的是Eclipse,可在項目的目錄的bin目錄下找到.class文件
在Eclipse運行Test,得結果:
各個加載器的關係爲
使用loader2加載Simple時,根據父親委託機制,會從根加載器開始嘗試加載,一直往下加載,之道系統類加載器加載成功。使用loader3加載Simple,根據父親委託機制,先由根加載器加載,加載失敗後有loader3自己加載
原文地址 http://techfoxbbs.com/blog-1-2.html