2018-4-8 Java(57)-Java高级开发 -ClassLoader类加载器

2019-07-04 00:00:00

Class类描述的是整个类的信息,在Class类中提供的forName()方法,它所能处理的只是通过CLASSPATH配置的路径进行类的加载,如果现在类的加载路径可能是网络、文件、数据库,此时就必须实现类加载器,也即是ClassLoader类的主要作用。

  • 认识类加载器ClassLoader-从CLASSPATH加载类
  • 自定义类加载器-从指定的路径加载类

【认识类加载器ClassLoader】

首先通过Class类观察如下方法:public ClassLoader getClassLoader()

例 编写一个简单的反射程序,来观察ClassLoader的存在

class Member{// 自定义类,这个类一定在CLASSPATH之中
	
}
public class TestDemo1 {
	public static void main(String[] args) {
		Class<?> cls=Member.class;
		System.out.println(cls.getClassLoader());
		System.out.println(cls.getClassLoader().getParent());
		System.out.println(cls.getClassLoader().getParent().getParent());
	}
}

结果

jdk.internal.loader.ClassLoaders$AppClassLoader@224aed64
jdk.internal.loader.ClassLoaders$PlatformClassLoader@c39f790
null

此时出现了2个类加载器:PlatformClassLoader(平台类加载器Java9版本出现,之前版本是ExtClassLoader扩展类加载器)、AppClassLoader(应用程序类加载器)

《2018-4-8 Java(57)-Java高级开发 -ClassLoader类加载器》
《2018-4-8 Java(57)-Java高级开发 -ClassLoader类加载器》

对于第三方程序类库(Ext加载器)除了CLASSPATH之外,实际上在Java中还有一个加载目录:d:\jdk\jre\lib\ext

《2018-4-8 Java(57)-Java高级开发 -ClassLoader类加载器》
《2018-4-8 Java(57)-Java高级开发 -ClassLoader类加载器》

实际上ClassLoader类中提供有这样一个方法:

  • 进行类的加载处理:public Class<?> loadClass(String name) throws ClassNotFoundException

例 观察默认类加载器(只是演示,没有意义)

package cn.mytest.demo;

class Member{// 自定义类,这个类一定在CLASSPATH之中
	public String toString() {
		return "Class Member";
	}
}
public class TestDemo1 {
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		System.out.println(Class.forName("cn.mytest.demo.Member").getClassLoader().loadClass("cn.mytest.demo.Member").newInstance()); ;
	}
}

结果

Class Member

不过这种类加载默认还是从CLASSPATH中加载的

【自定义类加载器】

希望可以通过程序来实现文件的类加载器

例 在D盘上建议一个Mebmer.java文件,

package cn.mytest.vo;

public class Member{
	public String toString() {
		return "New Member";
	}
}

随后对此程序文件进行编译,但是不打包:javac Mebmer.java,会产生Member.class文件。

现在希望通过自定义的类加载器实现d:\Member.class数据的加载。

例 实现自定义类加载器

  • ClassLoader类定义的加载:protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError
package cn.mytest.demo;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

class MyClassLoader extends ClassLoader{
	//实现一个自定义类加载器,传入类名称后,要通过指定的文件路径加载,返回类的Class对象
	public Class<?> loadData(String className) throws Exception{
		byte classData[]=this.loadClassData();//加载类文件的数据信息
		return super.defineClass(className, classData, 0,classData.length);
	}
	//通过指定的文件路径进行类的文件加载,就是二进制读取,返回类文件数据
	private byte [] loadClassData() throws Exception{
		InputStream input=new FileInputStream(new File("D:"+File.separator+"Member.class"));
		ByteArrayOutputStream bos=new ByteArrayOutputStream();//取得所有字节内容
		byte data []=new byte[20];//定义读取的缓冲区
		int temp=0;
		while((temp=input.read(data))!=-1) {
			bos.write(data,0,temp);
		}
		byte ret[]=bos.toByteArray();
		input.close();
		bos.close();
		return ret;
	}
}
public class TestDemo1 {
	@SuppressWarnings("deprecation")
	public static void main(String[] args) throws Exception {
		Class<?> cls=new MyClassLoader().loadData("cn.mytest.vo.Member");
		System.out.println(cls.newInstance()); 
		System.out.println(cls.getClassLoader());//cn.mytest.demo.MyClassLoader
		System.out.println(cls.getClassLoader().getParent());//jdk.internal.loader.ClassLoaders
		System.out.println(cls.getClassLoader().getParent().getParent());//jdk.internal.loader.ClassLoaders	
	}
}

结果

New Member
cn.mytest.demo.MyClassLoader@1c53fd30
jdk.internal.loader.ClassLoaders$AppClassLoader@50cbc42f
jdk.internal.loader.ClassLoaders$PlatformClassLoader@75412c2f

类加载器给用户最大的帮助在于可以通过动态的路径实现类的加载处理操作。

总结

大部分情况下我们可能不需要去实现类的加载器,但是请至少清楚Java中类的加载流程。

    原文作者:没人不认识我
    原文地址: https://zhuanlan.zhihu.com/p/35398570
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。

相关文章