基础加强_类加载器,类的加载机制

  ------- android培训、java培训、期待与您交流! ----------

* 类的加载机制

* 1.1、类的加载

* 类的加载是指将类的class文件读入内存,并为之创建一个java.lang.Class对象。
* 类的加载由类加载器完成,JVM提供的类加载器称为系统类加载器,另外可以通过继承ClassLoader基类创建类加载器。
* (1)从本地文件系统加载class文件
* (2)从JAR包加载class文件
* (3)通过网络加载class文件
* (4)把一个java源文件动态编译,并执行加载。

* 1.2、类的连接

* 类被加载以后,系统生成该类对应的Class对象,进入链接阶段。连接阶段负责把类的二进制数据合并到JRE中。
* 类连接分为3步:
* (1)验证:检验被加载的类是否有正确的内部结构,并和其他类协调一致。
* (2)准备:为类的静态Field分配内存,并设置默认初始值。
* (3)解析:将类的二进制数据中的符号引用替换成直接引用。

* 1.3、类的初始化

* 虚拟机负责对类进行初始化,主要是对类的静态Field进行初始化。
* 静态Field初始化有两种方式:声明时指定初始值;在静态代码块为静态Field指定初始值。
* 类的初始化步骤:
* (1)、如果该类还没有加载和连接,则先加载并连接该类;
* (2)、如果该类的直接父类还没有被初始化,则先初始化其直接父类;
* (3)、如果有初始化语句,则依次执行初始化语句。

* 1.4、类的初始化时机

* 在以下实际进行类的初始化
* (1)、创建类的实例;
* (2)、调用某个类的静态方法;
* (3)、访问某个类或接口的静态Field,或为该静态Field赋值;
* 对于一个final类型的静态Field,如果在编译时可以确定,则该Field相当于“宏变量”,使用该Field不会造成该类的初始化。
* 如果不能在编译时确定,则使用该Field会导致该类被初始化。
* (4)、使用反射强制创建对象;
* (5)、初始化某类的子类;
* (6)、使用java运行某个主类;
* 当使用ClassLoader类的loadClass()方法加载某个类时,只是加载,不会对类进行初始化。

* 2.1、类加载器

* 前面讲到类加载器负责将.class文件加载到内存中,并生成java.lang.Class对象。
* java中类的标识:包名、类名
* JVM中类的标识:包名、类名、类加载器
* JVM启动时,形成由3个类加载器组成的初始类加载器层次结构。
* ---根类加载器(引导类加载器)  Bootstrap
* 加载Java的核心类,由JVM自身实现。System.class.getClassLoader()  --> null
* ---扩展类加载器  ExtClassLoader 
* 加载JRE的扩展目录中的JAR包的类。%JAVA_HOME%/jre/lib/ext
* ---系统类加载器  AppClassLoader
* 加载classpath下的类,用户自定义的类的加载器都是系统类加载器

        自定义的类加载器必须继承ClassLoader,并以AppClassLoader作为父加载器。
* 可以通过ClassLoader的静态方法:getSystemClassLoader()获取系统类加载器。

* 2.2、类加载机制

* (1)、全盘负责,当一个类加载器加载某个Class时,该Class所依赖和引用的其他Class也由该类加载器加载。
* (2)、父类委托,先让父类加载器加载该Class,只有父类加载器无法加载才从本类加载路径加载。拂过本类也无法加载,则报异常。
* (3)、缓存机制,加载过的Class会被缓存,使用时,从缓存区调用。

通过代码学习自定义类加载器

public class MyClassLoader extends ClassLoader{


	/*
	 * 编写了一个自己的类装载器
	 * 自定义的类加载器的必须继承ClassLoader
	 * loadClass方法与findClass方法  defineClass方法
	 * 
	 * 步骤:
	 * 1、编写一个对文件内容进行简单加密的程序。
	 * 2、编写一个类装载器,可实现对加密过的类进行装载和解密。
	 * 3、编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,
	 * 因为编译器无法识别这个类。程序中可以除了使用ClassLoader.load方法之外,
	 * 还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName。
	 * 实验步骤:
	 * 对不带包名的class文件进行加密,加密结果存放到另外一个目录,
	 * 例如: java MyClassLoader MyTest.class F:\itcast
	 * 运行加载类的程序,结果能够被正常加载,但打印出来的类装载器名称为AppClassLoader;
	 * 用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,错误说明是AppClassLoader类装载器装载失败。
	 * 删除CLASSPATH环境下的类文件,再执行上一步操作就没问题了。
	 * 
	 * 
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		// main函数参数中传入源文件路径,加密后的目标路径
		String srcPath = args[0];
		String destDir = args[1];
		FileInputStream fis = new FileInputStream(srcPath);
		String destFileName = srcPath.substring(srcPath.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 = -1;
		while((b=ips.read())!=-1){
			ops.write(b ^ 0xff);
		}
	}
	
	private String classDir;
	//实现findClass方法
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		// TODO Auto-generated method stub
		String classFileName = classDir + "\\"  + name.substring(name.lastIndexOf('.')+1) + ".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) {
			e.printStackTrace();
		}
		return null;
	}
	
	public MyClassLoader(){
		
	}
	//定义类加载器的构造方法,传入可以加载的类文件路径
	public MyClassLoader(String classDir){
		this.classDir = classDir;
	}
}



类加载器的高级问题

编写一个能打印出自己的类加载器名称和当前类加载器的父子结构关系链的MyServlet,正常发布后,看到打印结果为WebAppClassloader。
把MyServlet.class文件打jar包,放到ext目录中,重启tomcat,发现找不到HttpServlet的错误。
把servlet.jar也放到ext目录中,问题解决了,打印的结果是ExtclassLoader 。
父级类加载器加载的类无法引用只能被子级类加载器加载的类。

基础加强_类加载器,类的加载机制_第1张图片


  ------- android培训、java培训、期待与您交流! ----------

你可能感兴趣的:(基础加强_类加载器,类的加载机制)