内容简介:Java虚拟机中的类加载有三大步骤:,链接,初始化.其中加载是指查找字节流(也就是由Java编译器生成的class文件)并据此创建类的过程,这中间我们需要借助类加载器来查找字节流.Java虚拟机提供了3种类加载器,启动(Bootstrap)类加载器、扩展(Extension)类加载器、应用(Application)类加载器.除了启动类加载器外,其他的类加载器都是java.lang.ClassLoader的子类.启动类加载器由C++语言实现,没有对应的Java对象,它负责将 <JAVA_HOME>/lib路径
Java虚拟机中的类加载有三大步骤:,链接,初始化.其中加载是指查找字节流(也就是由 Java 编译器生成的class文件)并据此创建类的过程,这中间我们需要借助类加载器来查找字节流.
Java虚拟机默认类加载器
Java虚拟机提供了3种类加载器,启动(Bootstrap)类加载器、扩展(Extension)类加载器、应用(Application)类加载器.除了启动类加载器外,其他的类加载器都是java.lang.ClassLoader的子类.启动类加载器由C++语言实现,没有对应的Java对象,它负责将 <JAVA_HOME>/lib路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中.扩展类加载器是指sun.misc.Launcher$ExtClassLoader类,由Java语言实现,是Launcher的静态内部类,它负责加载<JAVA_HOME>/lib/ext目录下或者由系统变量-Djava.ext.dir指定位路径中的类库,他的父类加载器是null.应用类加载器是指sun.misc.Launcher$AppClassLoader类,他负责加载应用程序路径下的类,这里路径指java -classpath或-D java.class.path 指定的路径,他的父类加载器是扩展类加载器.
注意这里面的父子类加载器并不是继承的关系,只是ClassLoader类中的parent属性.我们来看Launcher类中创建扩展类加载器的代码:
public ExtClassLoader(File[] var1) throws IOException {
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}
这里设置了其父加载器为null.
双亲委派机制
Java虚拟机在加载类时默认采用的是双亲委派机制,即当一个类加载器接收到加载请求时,会将请求转发到父类加载器,如果父类加载器在路径下没有找到该类,才会交给子类加载器去加载.我们来看ClassLoader中laodClass方法:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先判断类是否已加载过,加载过就直接返回
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
//有父类加载器,调用父加载器的loadClass
c = parent.loadClass(name, false);
} else {
//调用Bootstrap Classloader
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
long t1 = System.nanoTime();
//到自己指定类加载路径下查找是否有class字节码
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;
}
}
通过这种层级我们可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子类加载器再加载一次。其次也考虑到安全因素,比如我们自己写一个java.lang.String的类,通过双亲委派机制传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载我们新写的java.lang.String,而直接返回已加载过的String.class,这样保证生成的对象是同一种类型.
自定义类加载器
除了jvm自身提供的类加载器,我们还可以自定义类加载器,我们先写一个Person类
public class Person {
private int age;
private String name;
//省略getter/setter方法
}
我们先看他是由哪个类加载器加载的.
public class TestJava {
public static void main(String[] args) throws Exception {
Person person = new Person();
System.out.println("person是由" + person.getClass().getClassLoader() + "加载的");
}
}
运行结果如下:
我们把Person.class放置在其他目录下
再运行会发生什么,在上面的loadClass方法中其实已经有了答案,会抛出ClassNotFoundException,因为在指定路径下查找不到字节码.
我们现在写一个自定义的类加载器,让他能够去加载person类,很简单,我们只需要继承ClassLoader并重写findClass方法,这里面写查找字节码的逻辑.
public class PersonCustomClassLoader extends ClassLoader {
private String classPath;
public PersonCustomClassLoader(String classPath) {
this.classPath = classPath;
}
private byte[] loadByte(String name) throws Exception {
name = name.replaceAll("\\.", "/");
FileInputStream fis = new FileInputStream(classPath + "/" + name
+ ".class");
int len = fis.available();
byte[] data = new byte[len];
fis.read(data);
fis.close();
return data;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
}
我们来测试一下:
public class TestJava {
public static void main(String[] args) throws Exception {
PersonCustomClassLoader classLoader = new PersonCustomClassLoader("/home/shenxinjian");
Class<?> pClass = classLoader.loadClass("me.shenxinjian.algorithm.Person");
System.out.println("person是由" + pClass.getClassLoader() + "类加载器加载的");
}
}
测试结果如下:
编写自定义类加载器的意义
- 当class文件不在classPath路径下,如上面那种情况,默认系统类加载器无法找到该class文件,在这种情况下我们需要实现一个自定义的classLoader来加载特定路径下的class文件来生成class对象。
- 当一个class文件是通过网络传输并且可能会进行相应的加密操作时,需要先对class文件进行相应的解密后再加载到JVM内存中,这种情况下也需要编写自定义的ClassLoader并实现相应的逻辑
以上所述就是小编给大家介绍的《Java类加载器详解》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- mybatis教程--延迟加载详解
- JVM类加载机制详解
- 详解Node模块加载机制
- mybatis教程--延迟加载详解
- SpringBoot 教程之属性加载详解
- 详解PHP文件的自动加载(autoloading)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Persuasive Technology
B.J. Fogg / Morgan Kaufmann / 2002-12 / USD 39.95
Can computers change what you think and do? Can they motivate you to stop smoking, persuade you to buy insurance, or convince you to join the Army? "Yes, they can," says Dr. B.J. Fogg, directo......一起来看看 《Persuasive Technology》 这本书的介绍吧!