黑马程序员_类加载器

----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------

1、定义:简单说,类加载器就是加载类的工具。

当出现一个类,用到此类的时候,Java虚拟机首先将类字节码加载进内存,通常字节码的原始信息放在硬盘上的classpath指定的目录下。

2、类加载器作用:将.class文件中的内容加载进内存进行处理,处理完后的结果就是字节码。

3、默认类加载器:

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

2)BootStrap--顶级类加载器:

       类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这正是BootStrap。。它是嵌套在Java虚拟机内核中的,已启动即出现在虚拟机中,是用c++写的一段二进制代码。所以不能通过java程序获取其名字,获得的只能是null。

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

代码示例:

package cn.itcast.text2;
import java.util.Date;
public class ClassLoadTest{
	public static void main(String[] args) throws Exception{
		System.out.println(
				ClassLoadTest.class.getClassLoader().
				getClass().getName());//为AppClassLoader
		System.out.println(
				System.class.getClassLoader());//为null
	}
}

二、类加载器的委托机制:

1、加载类的方式

当Java虚拟机要加载一个类时,到底要用哪个类加载器加载呢?

1)首先,当前线程的类加载器去加载线程中的第一个类。

2)若A引用类B(继承或者使用了B),Java虚拟机将使用加载类的类加载器来加载类B。

3)还可直接调用ClassLoader的LoaderClass()方法,来制定某个类加载器去加载某个类。

2、加载器的委托机制:每个类加载器加载类时,又先委托给上级类加载器。

       当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,运行结果为ExtClassLoader的原因。

3、委托机制的优点:可以集中管理,不会产生多字节码重复的现象。

补充:面试题

可不可以自己写个类为:java.lang.System呢?

回答:第一、有一道面试,能不能自己写个类叫java.lang.System,为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,也就是总是使用爸爸们能找到的类,这样总是使用java系统提供的System

第二、但是还是有办法加载这个自定义的System类的,此时就不能交给上级加载了,需要用自定义的类加载器加载,这就需要有特殊的写法才能去加载这个自定义的System类的。

三、自定义类加载器

1、自定义的类加载器必须继承抽象类ClassLoader,要覆写其中的findClass(String name)方法,而不用覆写loadClass()方法。

2、覆写findClass(String name)方法的原因:

1)是要保留loadClass()方法中的流程,因为loadClass()中调用了findClass(String name)这个方法,此方法返回的就是去寻找父级的类加载器。

2)在loadClass()内部是会先委托给父级,当父级找到后就会调用findClass(String name)方法,而找不到时就会用子级的类加载器,再找不到就报异常了,所以只需要覆写findClass方法,那么就具有了实现用自定义的类加载器加载类的目的。

流程:

父级-->loadClass-->findClass-->得到Class文件后转化成字节码-->defind()。

3、编程步骤:

1)编写一个对文件内容进行简单加盟的程序

2)编写好了一个自己的类加载器,可实现对加密过来的类进行装载和解密。

3)编写一个程序,调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,程序中除了可使用ClassLoader的load方法外,还能使用放置线程的上线文类加载器加载或系统类加载器,然后在使用forName得到字节码文件。

import java.util.Date;

public class ClassLoaderAttachment extends Date {
	//对此类进行加密
		public String toString(){
			return "hello world";
		}
		public static void main(String [] args){
			
		}
}

//自定义类加载器
package cn.itcast.text2;

import java.io.*;
//继承抽象类ClassLoader
public class MyClassLoader  extends ClassLoader {
	public static void main(String[] args) throws Exception {
		//传入两个参数,源和目标
		String scrPath = args[0];
		String destDir = args[1];
		//将数据读取到输入流中,并写入到输出流中
		FileInputStream fis = new FileInputStream(scrPath);
		String destFileName = 
				scrPath.substring(scrPath.lastIndexOf('\\')+1);
		String destPath = destDir + "\\" + destFileName;
		FileOutputStream fos = new FileOutputStream(destPath);
		//加密数据
		cypher(fis,fos);
		fis.close();
		fos.close();
	}
	//定义加密数据的方法
	private static void cypher(InputStream ips,OutputStream ops)throws Exception{
		int b = 0;
		while((b=ips.read())!=-1){
			ops.write(b ^ 0xff);
		}
	}
	//定义全局变量
	private String classDir;
	@Override//覆写findClass方法,自定义类加载器
	protected Class findClass(String name) throws ClassNotFoundException {
		String classFileName = classDir + "\\" + name + ".class"; 
		try {
			//将要加载的文件读取到流中,并写入字节流中
			FileInputStream fis = new FileInputStream(classFileName);
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			cypher(fis,bos);
			fis.close();
			byte[] bytes = bos.toByteArray();
			return defineClass(bytes, 0, bytes.length);
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//如果没找到类,则用父级类加载器加载
		return super.findClass(name);
	}
	//构造函数
	public MyClassLoader(){}
	public MyClassLoader(String classDir){
		this.classDir = classDir;
	}
}

----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------

你可能感兴趣的:(j2se基础,基础加强)