类加载机制​

 

​1、类加载器

定义一个Person类:

package cn.yishijie.config;
​
public class Person {
    private int age;
    private String name;
}

 

测试所有的类加载器:

package cn.yishijie.config;
​
public class Client {
​
    public static void main(String[] args) {
        ClassLoader classLoader = Person.class.getClassLoader();
        System.out.println(classLoader);
        System.out.println(classLoader.getParent());
        System.out.println(classLoader.getParent().getParent());
    }
}

返回的结果分别为:

sun.misc.Launcher$AppClassLoader@58644d46

sun.misc.Launcher$ExtClassLoader@3b9a45b3

null

 

这个三个加载器是自带的加载器,分别是app类加载器,扩展类加载器,启动类加载器(是jvm的一部分,用c++写的,所以为null,没有对应的对象引用)

其中app类加载器,扩展类加载器这个两个类加载器都是Launcher类的内部类。这三个类加载都有自己负责加载的目录,分别有对应的系统变量设置。这里只是截取部分的源码:

App类加载器
static class AppClassLoader extends URLClassLoader {
        public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
            final String var1 = System.getProperty("java.class.path");
....
    
扩展类加载器
static class ExtClassLoader extends URLClassLoader {
 ...
​
        private static File[] getExtDirs() {
            String var0 = System.getProperty("java.ext.dirs");
            File[] var1;
...
​
启动类加载器
private static String bootClassPath = System.getProperty("sun.boot.class.path");

 

通过下面的属性获取来获取对应的加载路径:

System.getProperty("java.class.path");
System.getProperty("sun.boot.class.path");
System.getProperty("java.ext.dirs");
​
上述的信息都是用分号隔开的,可以通过下面打印:
private  void print(String path){
    String[] split = path.split(";");
    for (String s : split) {
      System.out.println(s);
    }
}

 

2、类型的加载

对类的首次主动使用,会触发我们类的初始化,主动方式包括下面:

访问静态属性:getStaticField

调用静态方法:invokeStaticMethod

反射调用:Class.forName

使用main方法入口:main

创建对象:new

初始化子类主动使用,会触发父类初始化

 

双亲委托机制:

将加载的动作优先交给父加载器加载,当父加载器不能加载的时候,子加载器才去加载,子加载器也加载不到的话,就抛出ClasssNotFoundException。

 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            // 查看是否已经加载
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        // 调用父类加载器
                        c = parent.loadClass(name, false);
                    } else {
                        // 查看启动类加载器是否已经加载,没有就尝试加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
​
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    // 加载类
                    c = findClass(name);
​
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

 

 

首先App类加载器查看是否已经加载,没有就扩展类加载查看是否已经加载,没有就查看启动类加载器是否已经加载,没有的话,启动类加载器就尝试在自己加载的路径下尝试加载,加载不到的话,就扩展类尝试加载,再加载不到的话,就App类加载器尝试加载,如果都找不到,那么就抛出ClassNotFoundException。

 

直接将对应的class文件删除掉,假设编译能通过,通过new触发加载的动作,如果加载不到class文件的话,会抛出:Exception in thread "main" java.lang.NoClassDefFoundError

这个异常是因为在需要加载的路径下,找不到需要的class文件或者找的class文件是异常的导致的。class文件异常的情况下,一般是因为静态属性初始化的时候出问题了,ExceptionInInitializerError异常会出现一次,以后再次触发加载动作的时候,就会抛出:Exception in thread "main" java.lang.NoClassDefFoundError异常。

如果通过:Class.forName("cn.yishijie.config.Person")方式:会抛出:ClassNotFoundException异常

 

自定义类加载器:

官方给的模板方式:

class NetworkClassLoader extends ClassLoader {
      String host;
      int port;
 
      public Class findClass(String name) {
           byte[] b = loadClassData(name);
           return defineClass(name, b, 0, b.length);
      }
 
      private byte[] loadClassData(String name) {
              // load the class data from the connection
     }
}

可以看出,重写findClass即可

package cn.yishijie.config;
​
import java.io.*;
​
public class MyClassLoader extends ClassLoader {
    // 对应的class文件的路径
    String path = "D:\\jeffchan\\workspace\\hello\\target\\classes\\";
    // 用于存对应的class文件的大小
    private int size;
    @Override
    public Class findClass(String name) {
        byte[] b = loadClassData(path,name);
        return defineClass(name, b, 0, size);
    }
​
    private byte[] loadClassData(String path, String name){
        String s = name.replaceAll("\\.", "/");
        File file = new File(path+s+".class");
        try {
            InputStream inputStream = new FileInputStream(file);
            byte[] b = new byte[1024 * 1024 * 10];
            this.size = inputStream.read(b);
            return b;
        }catch (Exception e){
            ;
        }
        return null;
    }
}

 

测试:

package cn.yishijie.config;
​
​
import java.lang.reflect.Field;
​
public class Client {
​
    public static void main(String[] args) throws Exception{
        MyClassLoader myClassLoader = new MyClassLoader();
        Class aClass = myClassLoader.findClass("cn.yishijie.config.Person");
        Object o = aClass.newInstance();
        Field field = aClass.getDeclaredField("name");
        field.setAccessible(true);
        field.set(o, "jeffchan");
        System.out.println(o);
        System.out.println(aClass.getClassLoader());
    }
}
​

打印出结果:

Person{age=0, name='jeffchan'}

cn.yishijie.config.MyClassLoader@7699a589

 

自定义的类加载器,可以对class文件进行一些加密等操作,还可以从自己指定的路径下获取class,比如存在任意一个磁盘,或者存到数据库等位置,还有官方给的通过网络的形式将class文件读入进来,都可以通过自定义的类加载器将类加载进来。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值