类加载器

一.三大类加载

        Java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器是:BootStrapExtClassLoardAppClassLoard,其中BottStrap不是java类,他同虚拟器一样是跨平台的,也就是随着操作系统的不同而改变,他是第一个进行内存开辟的类加载器,就是他把其他两个类加载器加载到虚拟机当中的,而其他两个都是java类,这三个类加载器都负责加载特定位置上的类。

二.类加载器之间的关系

        类加载器之间是一个继承树的关系,如果我们需要自定义类加载器就要让他成为这个体系中的一员。下面把每个类加载器与其对应的需加载的类进行说明:

三.委托机制

      如果当前线程的类加载器去加载线程中的第一个类A,当类A引用了类B时,java虚拟机将使用类A的类加载器来加载类B,除去这种默认方法,我们还可以通过直接调用ClassLoader.loadClass()方法来指定类加载器。

      当我们要使用到一个类时,首先当前的类加载器会委托上级加载器先去加载该类,当上级收到请求后不是直接去他指定的目录下寻找该类,而是再去找上级,一直到最顶层,这时最顶层的类加载器不能再往上抛了,他就去他的指定目录下寻找该类,如果没有找到,就返回给他的下一级,以此类推,最后返回到发起者手中,如果发起者都没有加载到该类,就会报出类不存在异常:ClassNoFoundExeption

      委托机制的好处在于当上级加载器已经把某Class文件加载到内存当中时,如果再调用该类,就无需再次加载,这就避免了多份字节码同时存在的情况发生。

四.自定义类加载器

      自定义类加载器总共需要三步

      ①自定义类加载器首先要有一个前提,就是继承ClassLoard类(抽象类),这样我们的自定义类才能成为类加载器体系中的一员

      ②然后就需要复写ClassLoard中的方法,在自定义类加载器时一般都会复写findClassString name)方法,用于当上级加载器找不到所需类时,自己去加载该类,还有一个方法比较重要:loardClass(String name),此方法大多数情况不用复写,他也是用来根据类名获取该类字节码,他不用复写的原因在于这个方法会使用委托机制,当上级没找到返回来之后,此方法会启用findClass方法,所以说我们要想保留委托机制,就不要去复写该方法,而只改动findClass方法就可以。

      ③使用ClassLoard中的defineClass()方法将找到的Class文件加载到内存当中转换成字节码文件。

五.练习

      编写一个程序,首先自定义一个类,然后自定义类加载器加载该类,我们希望生成的Class文件是经过加密的,而我们自定义的类加载器可以具有解密的功能。

      首先自定义一个类,这个类需要继承一个可以被编译器加载的类,因为在后期使用时,我们不能使用这个类名,那个时候我们的自定义类加载器还没有加载该类。

//这个类就是要被我们自定义类加载器加载的类
public class ClassLoadAcce extends Date{
	public String toString()
	{
		return "你好!自定义类加载器!";
	}
}

      自定义类加载器,在该加载器中定义加密和解密的方法,为了方便使用异或,这样该方法既可以加密又可以解密。在main函数中,我们在运行时期动态的把原Class文件的绝对路径和我们自定义类指定的目录(此处为classLoard)作为参数传递给main函数,因为Eclipse会实时编译把ClassLoadAcceClass文件存放到bin目录下,我们利用IO流和加密方法读取bin目录下的class文件,加密后把他放到自定义目录下。复写findClass方法,加载加密后的class文件,并进行解密。

/*
 * 这是我们自己定义的一个类加载器
 * ①首先定义一个附件类用来产生Class文件,这个类叫做ClassLoadAcce,他生成的Class文件是放在
 * bin目录下,我们自定义一个目录放在该工程下名字叫做classLoard。
 * 
 * ②经过args(main方法参数)读取ClassLoadAcce.class的路径名然后经过IO流的加密功能把加密过的class文件放到
 * 自定义的目录classLoad下面
 * 
 * ③复写ClassLoad类的findClass方法,当委托机制找不到该class文件时,返回到发起者手中
 * 也就是我们的自定义加载器这里,他就会执行findClass方法来 拯救世界了
 * 	findClass方法中从指定的文件中读取出Class文件,并返回给调用者
 * 
 * */
public class MyClassLoard extends ClassLoader{

	public static void main(String[] args) throws IOException{
		//从javac中接收两个参数,分别是源文件和目标目录
		String source = args[0];
		String destPath = args[1];

		//构建文件读取流,获取该文件
		FileInputStream fis = new FileInputStream(source);
		System.out.println(source);
		
		//从源文件中截取文件名
		String fileName = source.substring(source.lastIndexOf("\\")+1);
		
		//将截取好的文件名写入到当前项目中
		String fileDest = destPath+File.separator+fileName;
		
		FileOutputStream fos = new FileOutputStream(fileDest);
		
		encrypt(fis,fos);
		
		fis.close();
		fos.close();

	}
	//定义一个简单的加密算法,使用异或
	private static void encrypt(InputStream is,OutputStream os)throws IOException
	{
		int len=-1;
		while((len = is.read())!=-1)
		{
			//异或255进行加密
			os.write(len^255);
			os.flush();
		}
	}
	
	//定义成员变量,用来接收指定的class路径
	private String classPath;
	
	public MyClassLoard(){}
	
	public MyClassLoard(String classPath)
	{
		this.classPath = classPath;
	}
	
	
	//复写findClass方法让他去指定目录classLoad中寻找class类
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		
		//根据使用自定义类加载器时传进来的文件目录classPath和文件名name,获取该class的全部名称
		StringBuilder className = new StringBuilder(classPath);
		className.append(File.separator+name+".class");
		System.out.println("自定义类加载器所要获取的文件路径:"+className);
		//读取该文件
		try
		{
			FileInputStream fis = new FileInputStream(className.toString());
			//把读取到的class文件写到内存中
			ByteArrayOutputStream bao = new ByteArrayOutputStream();
			//对读取到文件进行解密
			encrypt(fis, bao);
			fis.close();
			byte[] buf = bao.toByteArray();
			//返回读取到的class文件
			return defineClass(buf, 0, buf.length);	
			
		}catch(IOException e)
		{
			e.printStackTrace();
		}
	
		//否则返回委托机制找父类去
		return super.findClass(name);
	}
	

}

在该类中使用自定义类加载器加载该文件

public class ClassLoadTest {

	public static void main(String[] args) throws Exception{

		/*获取我们要读取的那个Class文件字节码,此处类加载器的发起者是AppClassLoader,
		如果我们已经把bin目录下的class文件删掉了,此处报错,如果没删,此处读取的是bin目录下的class文件*/
//		System.out.println(new ClassLoadAcce().toString());
//		System.out.println(new ClassLoadAcce().getClass().getClassLoader());
		
		
		/*使用我们自定义的类加载器,他会先使用委托机制向上找,找不到返回到发起者这里来
		 我们自定义findClass方法在classLoad文件夹下找到加密过得class文件并返回*/
		Class cls = new MyClassLoard("classLoad").loadClass("ClassLoadAcce");
		
		System.out.println(cls);
		//此处不能直接new ClassLoadAcce对象,因为ClassLoadAcce在编译时期还没有吧他的字节码加载到内存中
		Date ca= (Date)cls.newInstance();
		System.out.println(ca);
		
		
	}

}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值