类加载浅析和自定义类加载器

来源:互联网 发布:健体冠军杰瑞米数据 编辑:程序博客网 时间:2024/05/01 02:15



一、类加载器初识

1、概述
        类加载机制不只是使用一个单一的类加载器,每个Java程序至少有三个类加载器,它们都是系统类加载器。
        用户也可以通过实现java.lang.ClassLoader实现自定义类加载器。
2、JVM自带类加载器
 (1)根类加载器(Bootstrap):使用c++编写,该加载器没有父加载器。它负责加载虚拟机的核心类库(典型地,来自rt.jar包下的类库)。
        根类加载器依赖于底层操作系统,属于虚拟机实现的一部分,它并没有实现java.lang.ClassLoader类。
 (2)扩展类加载器(Extension):纯java代码编写,ClassLoader的子类,它的父加载器为根类加载器。
        它从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre\lib\ext子目录下加载类库。
        你可以将你要引进的jar文件放在该目录下,加载器不需要任何class path就可以找到它们的类库。
 (3)系统类加载器或应用加载器(System):纯java代码编写,ClassLoader的子类,它的父加载器为扩展类加载器,
        它从环境变量classpath或系统属性java.class.path所指定的目录中加载类,它是用户自定义的类加 载器的默认父加载器。
3、类的加载过程
  类的加载采用父类委托机制,这种机制能够更好的保障Java平台的安全性。在此委托机制中,除Java自带的根加载器外,
 其余的加载器都有且只有一个父加载器,如下层次图所示,当Java程序请求(your custom)loader加载(your custom)class时,
loader首先委托自己的父加载器去加载class,若父加载器能完成,则由父加载器加载,否则由loader加载。
(这种关系并非是继承关系,而是一种包装机制)。
4、命名空间
        一旦一个类被载入JVM中,同一个类就不会被再次载入了(切记,同一个类)。那什么是同一个类?
        在Java中一个类通过认证的类全名来唯一的标识。认证的类全名包括包名和类名两部分组成。
        但是一个被加载到JVM中的类则通过类的全名和加载这个类的类记载器来唯一的标识。每个类加载器都有自己的命名空间,
        命名空间由该加载器和所有的父加载器所加载的类组成。在同一个命名空间不会出现类的完整名字(包括包名)相同的两个类;
        在不同的命名空间有可能出现完整的名字包括包名相同的两个类。
5、运行时包
        由同一个类加载器加载的属于相同包的类组成了运行时包,决定两个类是不是属于同一个运行时包,
        不仅要看它们的包名是否相同,还要看定义类加载器是否相同,只有属于同一个运行时包的类才能互相访问包可见的类和类成员,
        这样的限制能避免用户自定义的类冒充核心类库的类,去访问包可见成员。
6、自定义类加载器
        如上所述,系统类加载器是自定义类加载器的父加载器。若想实现自定义类加载器,需实现java.lang.ClassLoader类。
 7.Java源代码经过编译器编译之后便会生成一个字节码文件,字节码是一种二进制的类文件,它的内容是 JVM 的指令,而不像 C、C++经由编译器直接生成机器码

二、 自定义类加载器程序演示

/* (1 )
 * 测试类,然后自定义的类加载器去 加载该类
 */
public class ClassTest implements InterfaceTest{
    
     @Override
    public void name() {
     System.out.println("tao");
     }
    
    @Override
    public void age() {
    System.out.println("21");
    }
}

/* (2)
 *  要加载类的接口,加载该接口的子类时,可以用接口引用,而不需要利用反射来实现。
 */
public interface InterfaceTest {
    
    public void name();
    public void age();
}

(3) 加密处理

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class ClassEncrypt {
     public static void main(String[] args) throws IOException {
             //要加密的字节码.class文件
             String srcPath="D:\\Program Files\\MyEclipse 8.5 Workspace_UTF-8\\JavaSE\\bin\\com\\hu\\ClassLoader\\ClassTest.class";
             //加密之后输出的字节码.class文件的位置
             String destPath="D:\\IOstream\\ClassTest.class";
             FileInputStream fis=new FileInputStream(srcPath);
             FileOutputStream ofs=new FileOutputStream(destPath);
             cypher(fis, ofs);//加密
             fis.close();
             ofs.close();
         }
             
             //简单的加密,用于测试。将所有二进制位取反,即0变成1,1变成0
    private static void cypher(InputStream in,OutputStream out) throws IOException{
             int b=-1;
             while((b=in.read())!=-1){
                  out.write(b^0xff);
                 }
             }
}

(4)自定义类加载器测试

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class MyClaLoader extends ClassLoader{
    
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
                 //需要加载的.class字节码的位置
                 String classPath="D:\\IOstream\\ClassTest.class";
                 
                 FileInputStream fis=new FileInputStream(classPath);
                 ByteArrayOutputStream bos=new ByteArrayOutputStream();
                 cypher(fis, bos);
                 fis.close();
                 byte[] bytes=bos.toByteArray();
                 return defineClass(bytes, 0, bytes.length);
             } catch (FileNotFoundException e) {
             e.printStackTrace();
             } catch (IOException e) {
                 e.printStackTrace();
             }
             return super.findClass(name);
         }
         
         //相应的字节码解密类,在加载E盘根目录下的被加密过的ClassTest.class字节码的时候,进行相应的解密。
     private static void cypher(InputStream in,OutputStream out) throws IOException{
             int b=-1;
             while((b=in.read())!=-1){
                 out.write(b^0xff);
             }
         }
         
     public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
                  Class clazz=new MyClaLoader().loadClass("a");
                  //这就是我们接口的作用。如果没有接口,就需要利用反射来实现了。
                  InterfaceTest classTest=(InterfaceTest) clazz.newInstance();
                  classTest.name();
                  classTest.age();
              }
}




原创粉丝点击