Java反射--ClassLoader类加载器

内容学习于:edu.aliyun.com


1. 类加载器简介

  所有的Java程序的执行流程: JVM进程加载字节码文件,随后进行字节码文件的虚拟机解析,并且得到一个结果。
  经过分析可以发现,所有的“*.class"文件在磁盘上都会以二进制数据的形式保存(反射机制提供的就是二进制文件的解析能力),但是如果要想进行解析,那么首先要做到类的信息加载,于是在JDK之中针对于信息的加载提供有一个ClassLoader的加载器,如果要想观察ClassLoader可以直接利用Class类的方法来完成:

  • public ClassLoader getClassLoader()

  获得了ClassLoader之后就可以依据ClassLoader继续获取它之上的各个类加载器,也可以依据此ClassLoader找到对应的二进制字节码数据的信息,这个类里面重要的方法是获取父加载器: public final ClassL oader getParent()。

代码:

class Ball{}

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        System.out.println(Ball.class.getClassLoader());
        System.out.println(Ball.class.getClassLoader().getParent());
        System.out.println(Ball.class.getClassLoader().getParent().getParent());
    }
}

结果:

jdk.internal.loader.ClassLoaders$AppClassLoader@15db9742
jdk.internal.loader.ClassLoaders$PlatformClassLoader@85ede7b
null

  在Java之中为了防止程序可能出现的漏洞,例如:开发者自己定义了一个“java.lang.String”, 并且在里面安装有一些恶意的代码,那么请问,这个时候如何去防止这个恶性程序类被加载呢?所以在类加载器中专门提供了一个双亲加载机制,双亲加载指的是系统类使用系统类自己的加载器处理,而用户的类使用用户的加载器类进行处理,如果该类在系统类加载器中已经加载过了,那么用户加载器将不再加载重复的类。
  在JDK 1.8以前,不存在有“PlatformClassLoader", 而使用的是“ExtClassLoader”,主要是可以加载-一个第三方目录中的“*.jar” 文件,但是从JDK 1.9之后该加载器不再使用了。

2. 自定义类加载器

  ClassLoader类定义为抽象类,实际上如果有需要的开发者可以自己来定义专属的ClassLoader,假设现在有如下一个程序类。

定义消息处理类:

package com.xzzz.demo;

public class Message {
    public void send(String msg){
        System.out.println("【消息发送】"+msg);
    }
}

  2、为了更加方便的观察出ClassLoader特点,下 面将这个类手工进行编译(不打包),将生成的Message.class 文件保存在D盘。
  3、利用字节流进行二进制数据的加载,并且将加载到的内容转到ClassLoader之中,就是自定义类加载器。

自定义类加载器:

class ClassLoader extends java.lang.ClassLoader{
    private static final String CLASS_FILE = "D:" + File.separator + "Message.class";
    public Class<?> loadData (String classname) throws Exception{
        byte [] data = this.loadClassData();
        if (data!=null){
            return super.defineClass(classname,data,0,data.length);
        }
        return null;
    }


    private byte[] loadClassData() throws Exception{//加载数据
        InputStream input = new FileInputStream(CLASS_FILE);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        input.transferTo(bos);//加载文件到内存流
        byte [] data = bos.toByteArray();
        input.close();
        bos.close();
        return data;
    }
}

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        ClassLoader classLoader = new ClassLoader();
        Class<?> clazz = classLoader.loadData("com.xzzz.demo.Message");
        Object object = clazz.getDeclaredConstructor().newInstance();
        Method method = clazz.getDeclaredMethod("send",String.class);
        method.invoke(object,"www.mldn.cn");
        System.out.println("【类加载器】"+clazz.getClassLoader());
        System.out.println("【类加载器】"+clazz.getClassLoader().getParent());
        System.out.println("【类加载器】"+clazz.getClassLoader().getParent().getParent());
        System.out.println("【类加载器】"+clazz.getClassLoader().getParent().getParent().getParent());
    }
}

结果:

【消息发送】www.mldn.cn
【类加载器】com.xzzz.demo.ClassLoader@4c873330
【类加载器】jdk.internal.loader.ClassLoaders A p p C l a s s L o a d e r @ 15 d b 9742 j d k . i n t e r n a l . l o a d e r . C l a s s L o a d e r s AppClassLoader@15db9742 【类加载器】jdk.internal.loader.ClassLoaders PlatformClassLoader@41629346
【类加载器】null

  这个最主要可以去操作远程的类,比如在加载网路服务器上的。
  在现实的对发之中,99%的情况下不需要开发者自定义类加载器,因为一旦定义多了,整个项目容易产生混乱。

发布了43 篇原创文章 · 获赞 13 · 访问量 2631

猜你喜欢

转载自blog.csdn.net/qq_43040688/article/details/104202400