首页 > 代码库 > Java中的ClassLoader具体解说
Java中的ClassLoader具体解说
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 = http://www.mamicode.com/args[0];//本来的class文件的途径和文件名
String outputSrc = http://www.mamicode.com/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 classSrc;
public MyClassLoader() {
}
public MyClassLoader(String classSrc) {
this.classSrc = http://www.mamicode.com/classSrc;
}
@Override
protected Class findClass(String name) throws ClassNotFoundException {
//先找到自个的加密的class文件的方位
String classFielName = classSrc + "\\" + 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());