Java基础加强之类加载器,类的加载和加载机制,自定义类加载器
http://www.zftb.cn  发布时间:2015-04-05 12:45 来源:一起学编程网 浏览:加载中

学习概述:本模块深入讲解了Java类加载方面的知识,Java类加载器和类加载机制以及类加载原理

  学习目标:掌握类加载机制和原理,能够独立开发自己的类加载器。


  1.类的加载
 

  什么是类加载? 类加载是指将类的class文件读入内存,并为之创建一个Java.lang.Class对象,也就是说当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。

  类加载器负责加载所有类,系统为所有被载入内存中的类生成一个java.lang.Class实例。一旦一个类被载入JVM中,同一个类不会再被再次载入。

  思考问题:怎么样才算同一个类?

  当JVM启动时,会形成三个类加载器组成的原始类加载器层次结构:

  【BootStrap ClassLoader】根类加载器 这是一个特殊的加载器,他并不是有Java编写,而是JVM自身实现的

  【Extension Classloader】扩展类加载器

  【System Classloader】系统类加载器
 

  类加载器的父子关系:


  实验获得类加载器以及了解类加载器的层次结构:
 

 

public  class ClassloaderDemo{

public static  void main(String[] args){

System.out.printlb(ClassLoaderDemo.class.getClassLoader().getName());

System.out.println(System.class.getClassloader());

ClassLoader classloader = ClassLoaderDemo.class.getClassLoader());

while(loader!=null){

System.out.println(loader.getClass().getName());

loader=loader.getParent();

}

}

 


  注意:程序会抛出异常,因为JVM根类加载器不是Java类。


  2.类的加载机制,如图所示:

  根类加载器——》扩展类加载器——》系统类加载器——》用户类加载器
 

  <1>全盘负责:所谓全盘负责,就是说当一个类加载器负责加载某个Class的时候,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显式使用另外一个类加载器来实现载入。
 

  <2>父类委托:意思是先让父类加载器试图加载该Class,只有父类加载器无法加载该类是才尝试从自己的路径中加载该类。


  <3>缓存机制:缓存机制将会保证所有被加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存中搜索该Class,只有当缓存中不存在该Class对象 时,系统才重新读取该类对应的二进制数据。这就是为什么我们修改Class后,JVM必须重新启动,修改才生效的原因。


  类加载器的父子关系:用户类加载器—>系统类加载器—>扩展类加载器—>根类加载器


  类加载机制:


  <1>当JVM需要加载一个类是,到底指派哪个类加载器去加载呢?


  首先当前线程的类加载器去加载线程中的第一个类,如果A类中引用了B类,JVM将使用加载A类的加载器来加载B类,最后还可以调用ClassLoader。loadeClass方法指定某个类加载器去加载某个类。


  <2>每个类加载器在加载类时,先委托给其上级加载器。


  注意两点:


  当所有的祖宗类加载器都没有加载到类,回到发起类加载器,还加载不了,那么程序将抛出ClassNotFoundExcetpion,而不是去找发起类加载器的儿子,因为没有getChild ()方法,即使有,那么选择哪一个儿子加载器呢?


  面试题:能不能自己写一个类叫Java.lang.System?


  答案:可以写,但是因为JVM委托机制的存在,会先找到JVM根类加载器,我自己写也可以,那么我要抛开委托加载机制,我自己指定一个ClassLoader。

 

  3.自定义类加载器
 

  JVM中除了根类加载器之外的所有类加载器都是classloader的子类实例,我们完全可以通过扩展ClassLoader的子类,并重写ClassLoader所包含的的方法来实现自定义类       加载器,ClassLoader有两个关键的方法:loadClass(),findClass()。

  不过我们一般推荐重写findClass()方法,而不是loadClass()方法,因为重写findClass()可以避免覆盖默认类加载器的父类委托,缓存机制两种策略。

  下面是我自己编写的一个类加载器:

 

package snippet;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.InputStream;

/**

*

* @author Administrator

*自定义类加载器

*/

public class MyClassLoader extends ClassLoader {

// 获取java源文件的二进制码

public byte[] getBytes(String filename){

File file = new File(filename);

InputStream ips=null;

byte[] b = new byte[(int) file.length()];

try {

ips = new FileInputStream(file);

int raw =ips.read(b);

if(raw!=file.length()){

throw new IOException("无法完整读取文件");

}

} catch (FileNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

finally{

if(ips!=null){

try {

ips.close();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

return b;

}

public boolean compile(String javaFile){

System.out.println("正在编译");

Process p=null;

try {

//调用系统javac命令

p=Runtime.getRuntime().exec("javac" + javaFile);

try {

p.waitFor();

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

int ret = p.exitValue();

return ret==0;

}

@Override

protected Class<?> findClass(String name) {

Class<?> clazz=null;

String fileStub = name.replace(".", "/");

String javaFileName = fileStub + ".java";

String classFileName = fileStub + ".class";

File javaFile = new File(javaFileName);

File classFile = new File(classFileName);

//如果java源文件存在并且class文件不存在,或者java源文件比class文件修改的时间晚

if(javaFile.exists()&&(!classFile.exists()||javaFile.lastModified()>classFile.lastModified())){

if(!compile(javaFileName)||!classFile.exists()){

try {

throw new ClassNotFoundException("未发现class文件");

} catch (ClassNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

//如果class文件已经存在,那么直接生成字节码

if(classFile.exists()){

byte[] b = getBytes(classFileName);

clazz = defineClass(name, b, 0, b.length);

}

//如果为空,标明加载失败

if(clazz==null){

try {

throw new ClassNotFoundException(name);

} catch (ClassNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

return clazz;

}

}

 

  上面代码重写了findClass方法,通过重写该方法就可以实现自定义的类加载机制。

  学习总结:1.了解了JVM三种类加载器(根类加载器,系统类加载器,扩展类加载器),明白了三种类加载器的作用和范围

  2.学习了JVM三种类加载机制(父类委托,缓存,全盘负责)

  3.学习了如何自定义类加载器,通过继承ClassLoader类,特别要注意两个关键方法:loadClass()和findClass()两种方法的机制和不同。

 

如果你有好的win10资讯或者win10教程,以及win10相关的问题想要获得win10系统下载的关注与报道。
欢迎加入发送邮件到657025171#qq.com(#替换为@)。期待你的好消息!