這篇文章運用簡單易懂的例子給大家介紹java中類加載器的使用方法,代碼非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設、高性價比崇州網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式崇州網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設找我們,業(yè)務覆蓋崇州地區(qū)。費用合理售后完善,十多年實體公司更值得信賴。
概述
Java類加載器( 英語:Java Classloader)是Java運行時環(huán)境(Java Runtime Environment)的一部分,負責動態(tài)加載Java類到 Java虛擬機的內存空間中。類通常是按需加載,即第一次使用該類時才加載。由于有了類加載器,Java運行時系統(tǒng)不需要知道文件與文件系統(tǒng)。學習類加載器時,掌握Java的委派概念很重要。
獲得ClassLoader的途徑
1. 獲得當前類的ClassLoader
clazz.getClassLoader()
2. 獲得當前線程上下文的ClassLoader
Thread.currentThread().getContextClassLoader();
3. 獲得系統(tǒng)的ClassLoader
ClassLoader.getSystemClassLoader()
4. 獲得調用者的ClassLoader
DriverManager.getCallerClassLoader
ClassLoader源碼解析
代碼一:
public class Test12 {
public static void main(String[] args) {
String[] strings = new String[6];
System.out.println(strings.getClass().getClassLoader());
// 運行結果:null
Test12[] test12s = new Test12[1];
System.out.println(test12s.getClass().getClassLoader());
// 運行結果:sun.misc.Launcher$AppClassLoader@18b4aac2
int[] ints = new int[2];
System.out.println(ints.getClass().getClassLoader());
// 運行結果:null
}
}loadClass方法
loadClass的源碼如下, loadClass方法加載擁有指定的二進制名稱的Class,默認按照如下順序尋找類:
a)調用findLoadedClass(String)檢查這個類是否被加載
b)調用父類加載器的loadClass方法,如果父類加載器為null,就會調用啟動類加載器
c)調用findClass(String)方法尋找
使用上述步驟如果類被找到且resolve為true,就會去調用resolveClass(Class)方法
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}findClass方法
findClass的源碼如下,findClass尋找擁有指定二進制名稱的類,JVM鼓勵我們重寫此方法,需要自定義加載器遵循雙親委托機制,該方法會在檢查完父類加載器之后被loadClass方法調用,默認返回ClassNotFoundException異常。
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}defineClass方法
defineClass的源碼如下,defineClass方法將一個字節(jié)數(shù)組轉換為Class的實例。
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}自定義類加載器
/**
* 繼承了ClassLoader,這是一個自定義的類加載器
* @author 夜的那種黑丶
*/
public class ClassLoaderTest extends ClassLoader {
public static void main(String[] args) throws Exception {
ClassLoaderTest loader = new ClassLoaderTest("loader");
Class<?> clazz = loader.loadClass("classloader.Test01");
Object object = clazz.newInstance();
System.out.println(object);
System.out.println(object.getClass().getClassLoader());
}
//------------------------------以上為測試代碼---------------------------------
/**
* 類加載器名稱,標識作用
*/
private String classLoaderName;
/**
* 從磁盤讀物字節(jié)碼文件的擴展名
*/
private String fileExtension = ".class";
/**
* 創(chuàng)建一個類加載器對象,將系統(tǒng)類加載器當做該類加載器的父加載器
* @param classLoaderName 類加載器名稱
*/
private ClassLoaderTest(String classLoaderName) {
// 將系統(tǒng)類加載器當做該類加載器的父加載器
super();
this.classLoaderName = classLoaderName;
}
/**
* 創(chuàng)建一個類加載器對象,顯示指定該類加載器的父加載器
* 前提是需要有一個類加載器作為父加載器
* @param parent 父加載器
* @param classLoaderName 類加載器名稱
*/
private ClassLoaderTest(ClassLoader parent, String classLoaderName) {
// 顯示指定該類加載器的父加載器
super(parent);
this.classLoaderName = classLoaderName;
}
/**
* 尋找擁有指定二進制名稱的類,重寫ClassLoader類的同名方法,需要自定義加載器遵循雙親委托機制
* 該方法會在檢查完父類加載器之后被loadClass方法調用
* 默認返回ClassNotFoundException異常
* @param className 類名
* @return Class的實例
* @throws ClassNotFoundException 如果類不能被找到,拋出此異常
*/
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
byte[] data = this.loadClassData(className);
/*
* 通過defineClass方法將字節(jié)數(shù)組轉換為Class
* defineClass:將一個字節(jié)數(shù)組轉換為Class的實例,在使用這個Class之前必須要被解析
*/
return this.defineClass(className, data, 0 , data.length);
}
/**
* io操作,根據(jù)類名找到對應文件,返回class文件的二進制信息
* @param className 類名
* @return class文件的二進制信息
* @throws ClassNotFoundException 如果類不能被找到,拋出此異常
*/
private byte[] loadClassData(String className) throws ClassNotFoundException {
InputStream inputStream = null;
byte[] data;
ByteArrayOutputStream byteArrayOutputStream = null;
try {
this.classLoaderName = this.classLoaderName.replace(".", "/");
inputStream = new FileInputStream(new File(className + this.fileExtension));
byteArrayOutputStream = new ByteArrayOutputStream();
int ch;
while (-1 != (ch = inputStream.read())) {
byteArrayOutputStream.write(ch);
}
data = byteArrayOutputStream.toByteArray();
} catch (Exception e) {
throw new ClassNotFoundException();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (byteArrayOutputStream != null) {
byteArrayOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return data;
}
}以上是一段自定義類加載器的代碼,我們執(zhí)行這段代碼
classloader.Test01@7f31245a sun.misc.Launcher$AppClassLoader@18b4aac2
可以看見,這段代碼中進行類加載的類加載器還是系統(tǒng)類加載器(AppClassLoader)。這是因為jvm的雙親委托機制造成的,private ClassLoaderTest(String classLoaderName)將系統(tǒng)類加載器當做我們自定義類加載器的父加載器,jvm的雙親委托機制使自定義類加載器委托系統(tǒng)類加載器完成加載。
改造以下代碼,添加一個path屬性用來指定類加載位置:
public class ClassLoaderTest extends ClassLoader {
public static void main(String[] args) throws Exception {
ClassLoaderTest loader = new ClassLoaderTest("loader");
loader.setPath("/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/");
Class<?> clazz = loader.loadClass("classloader.Test01");
System.out.println("class:" + clazz);
Object object = clazz.newInstance();
System.out.println(object);
System.out.println(object.getClass().getClassLoader());
}
//------------------------------以上為測試代碼---------------------------------
/**
* 從指定路徑加載
*/
private String path;
......
/**
* io操作,根據(jù)類名找到對應文件,返回class文件的二進制信息
* @param className 類名
* @return class文件的二進制信息
* @throws ClassNotFoundException 如果類不能被找到,拋出此異常
*/
private byte[] loadClassData(String className) throws ClassNotFoundException {
InputStream inputStream = null;
byte[] data;
ByteArrayOutputStream byteArrayOutputStream = null;
className = className.replace(".", "/");
try {
this.classLoaderName = this.classLoaderName.replace(".", "/");
inputStream = new FileInputStream(new File(this.path + className + this.fileExtension));
byteArrayOutputStream = new ByteArrayOutputStream();
int ch;
while (-1 != (ch = inputStream.read())) {
byteArrayOutputStream.write(ch);
}
data = byteArrayOutputStream.toByteArray();
} catch (Exception e) {
throw new ClassNotFoundException();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (byteArrayOutputStream != null) {
byteArrayOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return data;
}
public void setPath(String path) {
this.path = path;
}
}運行一下
class:class classloader.Test01 classloader.Test01@7f31245a sun.misc.Launcher$AppClassLoader@18b4aac2
修改一下測試代碼,并刪除工程下的Test01.class文件
public static void main(String[] args) throws Exception {
ClassLoaderTest loader = new ClassLoaderTest("loader");
loader.setPath("/home/fanxuan/桌面/");
Class<?> clazz = loader.loadClass("classloader.Test01");
System.out.println("class:" + clazz);
Object object = clazz.newInstance();
System.out.println(object);
System.out.println(object.getClass().getClassLoader());
}運行一下
class:class classloader.Test01 classloader.Test01@135fbaa4 classloader.ClassLoaderTest@7f31245a
分析
改造后的兩塊代碼,第一塊代碼中加載類的是系統(tǒng)類加載器AppClassLoader,第二塊代碼中加載類的是自定義類加載器ClassLoaderTest。是因為ClassLoaderTest會委托他的父加載器AppClassLoader加載class,第一塊代碼的path直接是工程下,AppClassLoader可以加載到,而第二塊代碼的path在桌面目錄下,所以AppClassLoader無法加載到,然后ClassLoaderTest自身嘗試加載并成功加載到。如果第二塊代碼工程目錄下的Test01.class文件沒有被刪除,那么依然是AppClassLoader加載。
再來測試一塊代碼
public static void main(String[] args) throws Exception {
ClassLoaderTest loader = new ClassLoaderTest("loader");
loader.setPath("/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/");
Class<?> clazz = loader.loadClass("classloader.Test01");
System.out.println("class:" + clazz.hashCode());
Object object = clazz.newInstance();
System.out.println(object.getClass().getClassLoader());
ClassLoaderTest loader2 = new ClassLoaderTest("loader");
loader2.setPath("/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/");
Class<?> clazz2 = loader2.loadClass("classloader.Test01");
System.out.println("class:" + clazz2.hashCode());
Object object2 = clazz2.newInstance();
System.out.println(object2.getClass().getClassLoader());
}結果顯而易見,類由系統(tǒng)類加載器加載,并且clazz和clazz2是相同的。
class:2133927002 sun.misc.Launcher$AppClassLoader@18b4aac2 class:2133927002 sun.misc.Launcher$AppClassLoader@18b4aac2
再改造一下
public static void main(String[] args) throws Exception {
ClassLoaderTest loader = new ClassLoaderTest("loader");
loader.setPath("/home/fanxuan/桌面/");
Class<?> clazz = loader.loadClass("classloader.Test01");
System.out.println("class:" + clazz.hashCode());
Object object = clazz.newInstance();
System.out.println(object.getClass().getClassLoader());
ClassLoaderTest loader2 = new ClassLoaderTest("loader2");
loader2.setPath("/home/fanxuan/桌面/");
Class<?> clazz2 = loader2.loadClass("classloader.Test01");
System.out.println("class:" + clazz2.hashCode());
Object object2 = clazz2.newInstance();
System.out.println(object2.getClass().getClassLoader());
}運行結果
class:325040804 classloader.ClassLoaderTest@7f31245a class:621009875 classloader.ClassLoaderTest@45ee12a7
ClassLoaderTest是顯而易見,但是clazz和clazz2是不同的,這是因為類加載器的命名空間的原因。
我們可以通過設置父類加載器來讓loader和loader2處于同一命名空間
public static void main(String[] args) throws Exception {
ClassLoaderTest loader = new ClassLoaderTest("loader");
loader.setPath("/home/fanxuan/桌面/");
Class<?> clazz = loader.loadClass("classloader.Test01");
System.out.println("class:" + clazz.hashCode());
Object object = clazz.newInstance();
System.out.println(object.getClass().getClassLoader());
ClassLoaderTest loader2 = new ClassLoaderTest(loader, "loader2");
loader2.setPath("/home/fanxuan/桌面/");
Class<?> clazz2 = loader2.loadClass("classloader.Test01");
System.out.println("class:" + clazz2.hashCode());
Object object2 = clazz2.newInstance();
System.out.println(object2.getClass().getClassLoader());
}運行結果
class:325040804 classloader.ClassLoaderTest@7f31245a class:325040804 classloader.ClassLoaderTest@7f31245a
擴展:命名空間
1. 每個類加載器都有自己的命名空間,命名空間由該加載器及所有的父加載器所加載的類組成
2. 在同一命名空間中,不會出現(xiàn)類的完整名字(包括類的包名)相同的兩個類
3. 在不同的命名空間中,有可能會出現(xiàn)類的完整名字(包括類的包名)相同的兩個類
關于java中類加載器的使用方法就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
分享標題:java中類加載器的使用方法
本文路徑:http://www.chinadenli.net/article0/iiooio.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站排名、關鍵詞優(yōu)化、微信小程序、網(wǎng)站制作、服務器托管、網(wǎng)站內鏈
聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)