一、获取 ClassLoader 对象
获取某个类的类加载对象需要两步:
① 获取某个类的 Class 对象;
② 通过 Class 对象调用 getClassLoader() 方法获取类加载器对象
二、java.lang.ClassLoader 对象
ClassLoader 类是一个抽象类,学习一下 ClassLoader 的相关方法:
public final ClassLoader getParent():返回委托的父类加载器。一些实现可能使用 null 来表示引导类加载器;
public static ClassLoader getSystemClassLoader():返回委托的系统类加载器;
public Class<?> loadClass(String name):使用指定的二进制名称(类的全限定名)来加载类。
例如:java.lang.String,注意内部类的名称:匿名内部类(外部类的全限定名$编号)、局部内部类(外部类的全限定名$编号+类名)、成员/静态内部类(外部类的全限定名$+类名);
protected Class<?> findClass(String name):使用指定的二进制名称(类的全限定名)来查找类。此方法应该被类加载器的实现重写,该实现按照委托模型来加载类。
在通过父类加载器检查所请求的类后,此方法将被 loadClass 方法调用;
protected final Class<?> findLoadedClass(String name):返回Class 对象,如果类没有被加载,则返回 null;
protected final Class<?> defineClass(String name,byte[] b,int off,int len):将一个 byte 数组转换为 Class 类的实例;
Demo:自定义类加载器示例
1 import java.io.ByteArrayOutputStream;
2 import java.io.File;
3 import java.io.FileInputStream;
4 import java.io.FileNotFoundException;
5 import java.io.IOException;
6 import java.io.InputStream;
7
8 public class FileClassLoader extends ClassLoader{
9 private String rootDir;//指定加载路径
10
11 public FileClassLoader(String rootDir){
12 this.rootDir = rootDir;
13 }
14
15 @Override
16 protected Class<?> findClass(String name) throws ClassNotFoundException {
17 //首先检查请求的类型是否已经被这个类装载器装载到命名空间中了,如果已经被装载,直接返回;
18 Class<?> c = findLoadedClass(name);
19
20 if(c ==null){
21 //委派类加载器请求给父类加载器,如果父类加载器能够完成,则返回父类加载器加载的Class实例;
22 ClassLoader parent = this.getParent();
23 try {
24 c = parent.loadClass(name);
25 //加异常处理,父类加载不到,然后自己加载
26 } catch (Exception e) {
27 }
28
29 //调用本类加载器的findClass()方法,试图获取对应的字节码,如果获取的到,则调用defineClass()导入类型到方法区;
30 //如果获取不到对应的字节码或其他原因失败,则异常,终止加载过程
31 if(c == null){
32 byte[] classData = getClassData(name);
33 if(classData == null){
34 throw new ClassNotFoundException();
35 }else{
36 c = defineClass(name, classData, 0, classData.length);
37 }
38 }
39 }
40 return c;
41 }
42
43 //把.class文件的内容读取到一个字节数组中
44 //为什么要读取的字节数组中,因为protected final Class<?> defineClass(String name,byte[] b,int off,int len)
45 private byte[] getClassData(String name) {
46 String path = rootDir + File.separator + name.replace(".", File.separator)+".class";
47 InputStream is = null;
48 ByteArrayOutputStream baos = null;
49 try {
50 is = new FileInputStream(path);
51 baos =new ByteArrayOutputStream();
52 byte[] buffer = new byte[1024];
53 int len;
54 while((len = is.read(buffer))!=-1){
55 baos.write(buffer, 0, len);
56 }
57 return baos.toByteArray();
58 } catch (FileNotFoundException e) {
59 e.printStackTrace();
60 } catch (IOException e) {
61 e.printStackTrace();
62 }finally{
63 try {
64 if(is!=null){
65 is.close();
66 }
67 } catch (IOException e) {
68 e.printStackTrace();
69 }
70 }
71 return null;
72 }
73 }
74
75 public class TestFileClassLoader {
76
77 public static void main(String[] args) throws ClassNotFoundException {
78 FileClassLoader fsc = new FileClassLoader("D:/java/code");
79 Class<?> uc = fsc.loadClass("com.ks.UserManager");
80 System.out.println(uc);
81
82 Class<?> sc = fsc.loadClass("java.lang.String");
83 System.out.println(sc);
84 System.out.println(sc.getClassLoader());//null,因为委托给父类加载器...一直到引导类加载器
85 }
86
87 }
三、类加载器加载资源文件
ClassLoader 类的职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java类,即 java.lang.Class 类的一个实例。除此之外,ClassLoader 还负责加载 Java 应用所需的资源,如图像文件和配置文件等。
案例一:加载类路径(如:src 下)jdbc.properties 资源文件
扩展:SourceFolder:源代码文件夹,一般会单独用一个config这种SourceFolder来装配置文件、等价于src,不同于普通的 Folder
代码示例:
1 username=root
2 password=123456
3 url=jdbc:mysql://localhost:3306/test
4
5 package com.atguigu.loader;
6
7 import java.io.IOException;
8 import java.util.Properties;
9
10 public class TestLoaderProperties {
11 public static void main(String[] args) {
12 Properties pro = new Properties();
13 try {
14 pro.load(ClassLoader.getSystemResourceAsStream("jdbc.properties"));
15 System.out.println(pro);
16 } catch (IOException e) {
17 e.printStackTrace();
18 }
19 System.out.println(pro);
20 System.out.println(pro.getProperty("username"));
21 }
22 }
23
案例二:获取存放在“com.ks.reflect” 包下面的 demo.properties 文件
代码示例:
1 @Test
2 public void test() throws IOException{
3 Properties pro = new Properties();//集合,map,key=value
4
5 Class clazz = Test.class; //获取本类的 Class 对象
6 ClassLoader loader = clazz.getClassLoader(); //获取类加载器
7 InputStream in = loader.getResourceAsStream("com/ks/reflect/demo.properties");
8
9 pro.load(in);
10
11 System.out.println(pro);
12 System.out.println(pro.getProperty("name"));
13 }
案例三:获取当前项目下的 out.properties 文件,不在 src 下面(这个文件没有在编译目录下,不需要使用类加载器)
代码示例:
1 @Test
2 public void test() throws IOException{
3 Properties pro = new Properties(); 4
5 //在项目的根路径下,不在src里面
6 pro.load(new FileInputStream("out.properties"));
7
8 System.out.println(pro);
9 System.out.println(pro.getProperty("out"));
10 }