类加载器ClassLoader

首先认识Class文件

我们平常用文本编辑器或者是IDE编写的程序都是.java格式的文件,这是最基础的源码,但这类文件是不能直接运行的。我们需要在命令行中进行java文件的编译javac HelloWorld.java,生成了.class文件,从命令行中执行命令:java HelloWorld,运行程序。
因为class文件是字节码格式文件,java虚拟机并不能直接识别我们平常编写的.java源文件,所以需要javac这个命令转换成.class文件。

JAVA类加载简单流程

Java语言系统自带有三个类加载器: 
- Bootstrap ClassLoader 最顶层的加载类,主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。我们可以打开我的电脑,在上面的目录下查看,看看这些jar包是不是存在于这个目录。 
- Extention ClassLoader 扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。 
- Appclass Loader也称为SystemAppClass 加载当前应用的classpath的所有类。

我们上面简单介绍了3个ClassLoader。说明了它们加载的路径。并且还提到了-Xbootclasspath-D java.ext.dirs这两个虚拟机参数选项。

下面是ClassLoader中的一段代码

 

 一些重要的方法

类加载全过程

 

package com.bjsxt.test;
  
public class Demo01 {
    static{
    System.out.println("静态初始化Demo01");
    }
    public static void main(String[] args) throws Exception {
        System.out.println("Demo01的main方法!");
        System.out.println(System.getProperty("java.class.path"));
         
        //主动引用
        //  new A();
        //  System.out.println(A.width);
        //  Class.forName("com.bjsxt.test.A");
         
         
        //被动引用
        //  System.out.println(A.MAX);
        //  A[] as = new A[10];
        System.out.println(B.width);
         
        }
    }
      
    class B  extends A {
        static {
            System.out.println("静态初始化B");
        }
    }
      
    class A extends A_Father {
        public static int width=100;   //静态变量,静态域    field
        public static final  int MAX=100; 
         
        static {
            System.out.println("静态初始化类A");
            width=300;
        }
        public A(){
            System.out.println("创建A类的对象");
        }
    }
      
    class A_Father extends Object {
        static {
            System.out.println("静态初始化A_Father");
        }
}

自定义类加载器

package com.bjsxt.test;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
  
/**
 * 自定义文件系统类加载器
 *
 */
public class FileSystemClassLoader extends ClassLoader {
 
    //com.bjsxt.test.User   --> d:/myjava/  com/bjsxt/test/User.class      
    private String rootDir;     
    public FileSystemClassLoader(String rootDir){
    this.rootDir = rootDir;
    }     
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {     
        Class<?> c = findLoadedClass(name); 
        //应该要先查询有没有加载过这个类。如果已经加载,则直接返回加载好的类。如果没有,则加载新的类。
        if(c!=null){
            return c;
        }else{
            ClassLoader parent = this.getParent();
            try {
                c = parent.loadClass(name);    //委派给父类加载
            } catch (Exception e) {
            //    e.printStackTrace();
            }
     
            if(c!=null){
                return c;
            }else{
                byte[] classData = getClassData(name);
                if(classData==null){
                    throw new ClassNotFoundException();
                }else{
                    c = defineClass(name, classData, 0,classData.length);
                }
            } 
        }
        return c;
    }
     
    private byte[] getClassData(String classname){   //com.bjsxt.test.User   d:/myjava/  com/bjsxt/test/User.class
        String path = rootDir +"/"+ classname.replace('.', '/')+".class";
         
        //  IOUtils,可以使用它将流中的数据转成字节数组
        InputStream is = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try{
            is  = new FileInputStream(path);
             
            byte[] buffer = new byte[1024];
            int temp=0;
            while((temp=is.read(buffer))!=-1){
                baos.write(buffer, 0, temp);
            }
            return baos.toByteArray();
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }finally{
            try {
                if(is!=null){
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(baos!=null){
                baos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }  
    }
}

测试类的代码如下:
package com.bjsxt.test;
public class Demo03 {
    public static void main(String[] args) throws Exception{
        FileSystemClassLoader loader = new FileSystemClassLoader("d:/myjava");
        FileSystemClassLoader loader2 = new FileSystemClassLoader("d:/myjava");
         
        Class<?> c = loader.loadClass("com.bjsxt.gaoqi.HelloWorld");
        Class<?> c2 = loader.loadClass("com.bjsxt.gaoqi.HelloWorld");
        Class<?> c3 = loader2.loadClass("com.bjsxt.gaoqi.HelloWorld");
          
        Class<?> c4 = loader2.loadClass("java.lang.String");
        Class<?> c5 = loader2.loadClass("com.bjsxt.test.Demo01");
         
        System.out.println(c.hashCode());
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode()); //同一个类,被不同的加载器加载,JVM认为也是不相同的类
        System.out.println(c4.hashCode());
        System.out.println(c4.getClassLoader()); //引导类加载器
        System.out.println(c3.getClassLoader()); //自定义的类加载器
        System.out.println(c5.getClassLoader()); //系统默认的类加载器
     
    }
}

网络类加载器:
package com.bjsxt.test;
  
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
  
/**
 * 网络类加载器
 */
public class NetClassLoader extends ClassLoader {
    //com.bjsxt.test.User   --> www.sxt.cn/myjava/  com/bjsxt/test/User.class      
    private String rootUrl;
    public NetClassLoader(String rootUrl){
        this.rootUrl = rootUrl;
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {     
        Class<?> c = findLoadedClass(name);         
        //应该要先查询有没有加载过这个类。如果已经加载,则直接返回加载好的类。如果没有,则加载新的类。
        if(c!=null){
            return c;
        }else{
            ClassLoader parent = this.getParent();
            try {
                c = parent.loadClass(name);    //委派给父类加载
            } catch (Exception e) {
            //    e.printStackTrace();
            }         
            if(c!=null){
                return c;
            }else{
                byte[] classData = getClassData(name);
                if(classData==null){
                    throw new ClassNotFoundException();
                }else{
                    c = defineClass(name, classData, 0,classData.length);
                }
            }
 
        }
        return c;
    }
    private byte[] getClassData(String classname){   //com.bjsxt.test.User   d:/myjava/  com/bjsxt/test/User.class
        String path = rootUrl +"/"+ classname.replace('.', '/')+".class";
     
            //  IOUtils,可以使用它将流中的数据转成字节数组
        InputStream is = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try{
            URL url = new URL(path);
            is  = url.openStream();
             
            byte[] buffer = new byte[1024];
            int temp=0;
            while((temp=is.read(buffer))!=-1){
                baos.write(buffer, 0, temp);
            } 
            return baos.toByteArray();
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }finally{
            try {
                if(is!=null){
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(baos!=null){
                    baos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }  
    }
}

类加载器之加密技术初步

自定义加密解密类加载器技术展示的代码如下:
package com.bjsxt.test;
  
/**
 * 测试简单加密解密(取反)操作
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class Demo04 {
    public static void main(String[] args) throws Exception {
        //测试取反操作
        //  int a = 3; //0000011
        //  System.out.println(Integer.toBinaryString(a^0xff));
         
        //加密后的class文件,正常的类加载器无法加载,报classFormatError
        //  FileSystemClassLoader loader = new FileSystemClassLoader("d:/myjava/temp");
        //  Class<?> c = loader.loadClass("HelloWorld");
        //  System.out.println(c);
         
        DecrptClassLoader loader = new DecrptClassLoader("d:/myjava/temp");
        Class<?> c = loader.loadClass("HelloWorld");
        System.out.println(c);
     
    }
}


加密类加载器代码:

package com.bjsxt.test;
  
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
  
/**
 * 加密工具类
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class EncrptUtil {
 
    public static void main(String[] args) {
        encrpt("d:/myjava/HelloWorld.class", "d:/myjava/temp/HelloWorld.class");
    }
 
    public static void encrpt(String src, String dest){
        FileInputStream fis = null;
        FileOutputStream fos = null;
         
        try {
            fis = new FileInputStream(src);
            fos = new FileOutputStream(dest);
             
            int temp = -1;
            while((temp=fis.read())!=-1){
                fos.write(temp^0xff);  //取反操作
            }
             
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                if(fis!=null){
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(fos!=null){
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }  
    }
}


解密的类加载器代码如下:

package com.bjsxt.test;
  
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
  
/**
 * 加载文件系统中加密后的class字节码的类加载器
 * @author 尚学堂高淇 www.sxt.cn
 *
 */
public class DecrptClassLoader  extends ClassLoader {
  
     //com.bjsxt.test.User   --> d:/myjava/  com/bjsxt/test/User.class      
     private String rootDir;
      
     public DecrptClassLoader(String rootDir){
        this.rootDir = rootDir;
     }
      
     private byte[] getClassData(String classname){   //com.bjsxt.test.User   d:/myjava/  com/bjsxt/test/User.class
         String path = rootDir +"/"+ classname.replace('.', '/')+".class";
          
        //  IOUtils,可以使用它将流中的数据转成字节数组
         InputStream is = null;
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         try{
             is  = new FileInputStream(path);
             int temp = -1;
             while((temp=is.read())!=-1){
                baos.write(temp^0xff);  //取反操作,相当于解密
             }
              
             return baos.toByteArray();
         }catch(Exception e){
            e.printStackTrace();
            return null;
         }finally{
             try {
                 if(is!=null){
                    is.close();
                 }
             } catch (IOException e) {
                e.printStackTrace();
             }
             try {
                 if(baos!=null){
                    baos.close();
                 }
             } catch (IOException e) {
                e.printStackTrace();
            }
         }
      
     } 
  
}

 

posted @ 2018-05-18 17:57  邓不利多  阅读(101)  评论(0编辑  收藏  举报