Java基础-知识点总结-Java类加载器

Java类加载器

 

      类加载器(classloader)用来加载 Java 类到 Java虚拟机中。一般来说,Java虚拟机使用 Java类的方式如下:Java源程序(.java文件)在经过 Java编译器编译之后就被转换成 Java字节代码(.class文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。

 

  1. Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader。

  2. 类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap。

  3. Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。

    类加载器之间的父子关系和管辖范围图

Java基础-知识点总结-Java类加载器_第1张图片

 

 

演示类加载器的树状组织结构

 

public class ClassLoaderTree { 
    public static void main(String[] args) { 
    	//获取加载该类的类加载器
        ClassLoader loader = ClassLoaderTree.class.getClassLoader(); 
        while (loader != null) { 
            System.out.println(loader.toString());
            //获取该类加载器的父加载器
            loader = loader.getParent(); 
        } 
    } 
 }

 

 

 

 

 

 

 

 

      打印结果为:

 

      sun.misc.Launcher$AppClassLoader@addbf1

 

sun.misc.Launcher$ExtClassLoader@42e816

 

类加载器的委托机制

 

  1. 当Java虚拟机要加载一个类时:

    1. 当前线程的类加载器去加载线程中的第一个类。

    2. 如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。

    3. 还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。

  2. 每个类加载器加载类时,又先委托给其上级类加载器。

    1. 当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?

      java.lang.ClassLoader

             java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java类,即java.lang.Class类的一个实例。除此之外,ClassLoader还负责加载 Java应用所需的资源,如图像文件和配置文件等。

      ClassLoader 中与加载类相关的方法

 

方法

说明

getParent()

返回该类加载器的父类加载器。

loadClass(String name)

加载名称为name的类,返回的结果是java.lang.Class类的实例。

findClass(String name)

查找名称为name的类,返回的结果是java.lang.Class类的实例。

findLoadedClass(String name)

查找名称为name的已经被加载过的类,返回的结果是java.lang.Class类的实例。

defineClass(String name, byte[] b, int off, int len)

把字节数组b中的内容转换成 Java类,返回的结果是 java.lang.Class类的实例。这个方法被声明为final的。

resolveClass(Class c)

链接指定的 Java类。

 

【编写自己的类加载器】

 

创建自定义加载器类

 

package cn.itcast.day2;

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

//自定义加载器类必须继承ClassLoader
public class MyClassLoader extends ClassLoader{
	//定义变量存储要加载的.Class文件所在目录
	private String classDir;
	
	//构造方法
	public MyClassLoader(){
		
	}
	public MyClassLoader(String classDir){
		this.classDir = classDir;
	}
	
	//主函数用于将指定.calss文件加密并存放到指定位置
	public static void main(String[] args) {
		//获取要加密的源
		String srcPath = args[0];
		//获取加密后文件存放的路径
		String destDir = args[1];
		//设置目的
		String destPath = destDir + "\\"+ srcPath.substring(srcPath.lastIndexOf('\\')+1);
		
		//创建输入流和输出流对象
		FileInputStream fis = null;
		FileOutputStream fos = null;
		
		try{
			fis = new FileInputStream(srcPath);
			fos = new FileOutputStream(destPath);
			//调用加密方法
			cepher(fis,fos);
		}catch(IOException e){
			throw new RuntimeException();
		}finally{
			try{
				if(fos != null)
					fos.close();
			}catch(IOException e){
				e.printStackTrace();
			}
			try{
				if(fis != null)
					fis.close();
			}catch(IOException e){
				e.printStackTrace();
			}
		}

	}
	
	//定义加密解密方法
	public static void cepher(InputStream ips,OutputStream ops){
		int b = -1;
		try{
			while((b = ips.read())!= -1){
				ops.write(b ^ 0xff);
			}
		}catch(IOException e){
			throw new RuntimeException("加密失败!");
		}finally{
			try{
				if(ops != null)
					ops.close();
			}catch(IOException e){
				e.printStackTrace();
			}
			try{
				if(ips != null)
					ips.close();
			}catch(IOException e){
				e.printStackTrace();
			}
		}
	}
	
	@Override
	//覆盖findClass方法,只让本加载器加载
	protected Class findClass(String arg0) throws ClassNotFoundException {
		System.out.println(arg0);
		
		//获取指定位置的.class文件
		String classFileName = classDir + "\\" + arg0.substring(arg0.lastIndexOf('.')+1) + ".class";
		
		//创建输入流,字符数组输出流
		FileInputStream fis = null;
		ByteArrayOutputStream bos = null;
		
		//将.class文件进行解密,并返回解密后的.class文件
		try{
			fis = new FileInputStream(classFileName);
			bos = new ByteArrayOutputStream();
			//解密
			cepher(fis,bos);
			byte[] bytes = bos.toByteArray();
			//defineClass()方法将字符数组转换为.class文件
			return defineClass(bytes, 0, bytes.length);
			
		}catch(IOException e){
			throw new RuntimeException();
		}finally{
			try{
				if(bos != null)
					bos.close();
			}catch(IOException e){
				e.printStackTrace();
			}
			try{
				if(fis != null)
					fis.close();
			}catch(IOException e){
				e.printStackTrace();
			}
		}
	}
}

创建类,并用MyClassLoader进行加密

 

package cn.itcast.day2;

import java.util.Date;

public class ClassLoaderAtt extends Date {
	public String toString(){
		return "hello java";
	}
}

 

 

 

 

 

 

 

 

运行MyClassLoader参数为E:\test\javaenhance\bin\cn\itcast\day2\ClassLoaderAtt.class

 

itcastlib(该目录是相对路径)

 

运行完程序,会在itcastlib目录中生成加密的ClassLoaderAttachment.class,然后删除E:\test\javaenhance\bin\cn\itcast\day2目录中的ClassLoaderAttachment.class,因为不删除的话自定义加载器的loadClass方法会把此目录下的ClassLoaderAttachment.class加载,而不是调用findClass方法查找被自定义加载器加密的ClassLoaderAttachment.class

 

测试类

 

package cn.itcast.day2;
import java.util.Date;
public class ClassLoaderTest {
	public static void main(String[] args) {
		try {
			//用自定义加载器加载.class文件
			Class clazz = new MyClassLoader("itcastlib").loadClass("cn.itcast.day2.ClassLoaderAtt");
			//实例化对象
			Date d = (Date)clazz.newInstance();
			System.out.println(d);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

 

 

 

 

你可能感兴趣的:(Java基础,类加载器)