java中的.java文件經過編譯今後,號碼大全就會生成類文件.class文件關鍵詞挖掘工具。class文件是以二進制字節碼寄存在硬盤中的。當咱們需求運用或加載Java文件到JVM中的時分,會從硬盤中讀取字節碼的class文件,然後經過類加載器將class文件加載到JVM中。也即是說,一切的Java文件都是經過類加載器加載到JVM中的。當然類加載器也是一個Java文件。那麼第一個類加載器又是怎麼加載到JVM中的呢?在發動JVM的時分,會調運一個本地辦法findBootStrapClass辦法加載最初始的那個ClassLoader,private native Class findBootstrapClass(String name),這個本地辦法運用C++編寫的。
1.體系已有3品種加載器
1.1 BooststrapClassLoader(boot) 加載rt.jar下面的類(此加載器採用C++編寫,通常開發中是看不到的)
1.2 ExtClassLoader 加載ExtClassLoader下面的類(ext文件夾下面的jar)
1.3 AppClassLoader 加載classpaht下面的類
咱們寫的類簡直都是經過AppClassLoader這個加載器加載到JVM中的。
2.類加載器的加載機制(雙親託付機制)
每一個類加載器都有一個對應的parentClassLoader。
2.1 體系類加載器的父子關係
自界說類加載器的爸爸是AppClassLoader
AppClassLoader的爸爸是ExtClassLoader
ExtClassLoader的爸爸是BooststrapClassLoader
Java代碼 保藏代碼
public class TestClassLoader {
public static void main(String[] args) {
// 當時目標的類加載器
ClassLoader loader = new TestClassLoader().getClass().getClassLoader();
// 從當時目標的類加載器想上找他的各個祖先
while (loader != null) {
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
// 曉得找到最終的祖先是null
System.out.println(loader);
}
}
輸出:
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
2.2 類加載器加載的次序
類加載器是從根向下加載的
也即是boot-》ExiClassLoader -》AppClassLoader
當一個類需求被加載的時分,首要是由AppClassLoader加載器將其傳遞給其爸爸ExtClassLoader,然後ExtClassLoader再將其傳遞給他的爸爸boot。
當boot發現他的加載規模內有對應的class,就加載到JVM中,否則交給兒子ExtClassLoader處置。
ExtClassLoader再在他的加載規模類找有沒有對應的class,有就加載到JVM中,沒有就交給AppClassLoader處置。
AppClassLoader再在classpath途徑下找對應的class,找到就加載,沒有就報反常。
緣由:這樣能夠確保JVM中某一個className對應的是同一個class,由於都是從根向下加載的。
避免重複加載,當爸爸現已加載了該類的時分,就沒有必要子ClassLoader再加載一次。
要是從下向上加載,能夠致使某一個className在JVM中對應好幾個class。能夠咱們會界說自個的類加載器和自個的加載規模。
當自個界說的類加載在他們各自的規模都發現需求加載的類,那麼他們能夠都會加載,致使JVM中一個className對應好幾個不一樣的class
2.3 比方咱們自個界說一個類加載器去加載java.lang.String這個類,那麼咱們是不能到達咱們目的的。
由於加載機制是從上到下加載,當傳遞到上面的boot的時分,現已被加載到JVM中,輪不到咱們自界說的類加載器去加載它。
但是,咱們肯定是能夠自個界說一個類加載器去加載咱們指定的類的。
3.怎麼自界說一個類加載
首要,咱們需求承繼ClassLoadar
然後,咱們不能破壞本來的類加載機制(雙親託付機制),所以咱們不能掩蓋loadClass辦法,咱們需求掩蓋findclass辦法。
最終,在findClass辦法中寫入咱們的類加載器的代碼。
查看源碼解說:
Java代碼 保藏代碼
protected Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首要查看name對應的Class是不是現已被加載
Class c = findLoadedClass(name);
//假如沒有被加載
if (c == null) {
long t0 = System.nanoTime();
//測驗讓parentClassLoader去加載
try {
if (parent != null) {
//當parent不爲null的時分,讓parent去loadClass
c = parent.loadClass(name, false);
} else {
//當parent爲null的時分,就調運本地辦法
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
//當parentClassLoader沒有加載的時分
if (c == null) {
long t1 = System.nanoTime();
//調運findClass辦法去加載
c = findClass(name);
}
}
- indexRead arguments from command-line "http://www.shoudashou.com"
- indexRead arguments from command-line "http://www.4lunwen.cn"
- indexRead arguments from command-line "http://www.zx1234.cn"
- indexRead arguments from command-line "http://www.penbar.cn"
- indexRead arguments from command-line "http://www.whathappy.cn"
- indexRead arguments from command-line "http://www.lunjin.net"
- indexRead arguments from command-line "http://www.ssstyle.cn"
- indexRead arguments from command-line "http://www.91fish.cn"
- indexRead arguments from command-line "http://www.fanselang.com"
if (resolve) {
resolveClass(c);
}
return c;
}
}
4.舉例
目標:自界說一個類加載器加咱們指定途徑下,經過我麼加密的class。
進程:找到指定途徑下的class文件,解密,加載到JVM中。
4.1先界說一個需求被加密編譯的類,一起運用它進行測驗
Java代碼 保藏代碼
public class MyClass extends Date {
@Override
public String toString() {
return "hello world";
}
}
4.2加密本來的class文件
Java代碼 保藏代碼
public static void main(String[] args) throws Exception {
String inputSrc = args[0];//本來的class文件的途徑和文件名
String outputSrc = args[1];//加密今後寄存的文件途徑和文件名
FileInputStream fis = new FileInputStream(inputSrc);
FileOutputStream fos = new FileOutputStream(outputSrc);
//調用加密算法
cypher(fis, fos);
fis.close();
fos.close();
}
/**
* 加密解密函數辦法
*
* @param is
* @param os
* @throws IOException
*/
private static void cypher(InputStream is, OutputStream os) throws IOException {
int b = -1;
while ((b = is.read()) != -1) {
// 將0成爲1,將1成爲0
os.write(b ^ 0xff);
}
}
4.3編寫咱們自個的類加載器
Java代碼 保藏代碼
public class MyClassLoader extends ClassLoader {
//要加載的類的途徑
private String clas***c;
public MyClassLoader() {
}
public MyClassLoader(String clas***c) {
this.clas***c = clas***c;
}
@Override
protected Class findClass(String name) throws ClassNotFoundException {
//先找到自個的加密的class文件的方位
String classFielName = clas***c + "\\" + name + ".class";
try {
FileInputStream fis = new FileInputStream(classFielName);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//調運解密算法
cypher(fis, baos);
fis.close();
byte[] bytes = baos.toByteArray();
//將讀出來的二進制轉換爲Class字節碼
return defineClass(null, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
}
4.4運用咱們自個的類加載加載咱們加密的類到JVM中
Java代碼 保藏代碼
//首要運用自個的類加載器去加載咱們加密的class文件
//留意,這個當地的加載類的途徑下的class應該是咱們加密今後文件的方位
Class clazz = new MyClassLoader("E:/AllWorkspace/workspace1/classLoaderTest/bin/com/gusi/test").loadClass("MyClass");
//經過反射,測驗咱們的classLoader
Date date = (Date) clazz.newInstance();
System.out.println(date.toString());