您当前的位置: 首页 >  Java

Kevin-Dev

暂无认证

  • 0浏览

    0关注

    544博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【Java -- 虚拟机】类加载器(含双亲委派模型)

Kevin-Dev 发布时间:2020-03-20 11:21:08 ,浏览量:0

前言

作用:实现类加载的功能,确定被加载类 在 Java虚拟机中 的 唯一性。

类加载器的类型数量分别从 Java虚拟机 & Java开发者的角度来看,如下图: 在这里插入图片描述

一、启动类加载器(Bootstrap ClassLoader)

1. 作用 负责加载以下类:

  • 存放在\lib目录中的类
  • -Xbootclasspath参数所指定路径中、并且是被虚拟机识别的类库

仅按文件名识别,如:rt.jar,名字不符合的类库即使放在lib目录中也不会被加载

2. 特别注意

  • 启动类加载器 无法 被Java程序直接引用
  • 用户在编写自定义类加载器时,若需把 加载请求 委派 给 引导类加载器,直接使用null代替即可,如java.lang.ClassLoader.getClassLoader()方法所示:
@CallerSensitive 
public ClassLoader getClassLoader() { 
    ClassLoader cl = getClassLoader0(); 
    if (cl == null) 
        return null; 
    SecurityManager sm = System.getSecurityManager(); 
    if (sm != null) { 
        ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass()); 
    } 
    return cl; 
}
二、扩展类加载器(Extension ClassLoader)

1. 作用 负责加载以下类:

  • \lib\ext目录中的类库
  • java.ext.dirs系统变量所指定的路径中的所有类库

2. 特别注意

  • sum.misc.Launcher$ExtClassLoader类实现
  • 开发者可以直接使用扩展类加载器
三、应用程序类加载器(Application ClassLoader)

1. 作用 负责加载 用户类路径(ClassPath)上所指定的类库

2. 特别注意

  • 也称为系统类加载器,因为该类加载器是ClassLoader中的getSystemClassLoader()方法的返回值
  • 由sum.misc.Launcher$AppClassLoader类实现
  • 开发者可以直接使用该类加载器
  • 若开发者 没 自定义类加载器,程序默认使用该类加载器

在 Java 虚拟机中,各种类加载器 配合使用 的 模型(关系)是 双亲委派模型

四、双亲委派模型

1. 模型说明 在这里插入图片描述 2. 工作流程

双亲委派模型的工作流程代码实现在 java.lang.ClassLoaderloadClass()中,具体如下:

@Override 
protected Class loadClass(String name, boolean resolve) 
        throws ClassNotFoundException { 
    Class c = findLoadedClass(name); 

  // 检查需要加载的类是否已经被加载过
    if (c == null) { 
        try { 
             // 若没有加载,则调用父加载器的loadClass()方法
            if (parent != null) { 
                c = parent.loadClass(name, false); 
            }else{ 
                // 若父类加载器为空,则默认使用启动类加载器作为父加载器
                c=findBootstrapClassOrNull(name); 
            } 
        } catch (ClassNotFoundException e) { 
            // 若父类加载器加载失败会抛出ClassNotFoundException, 
            //说明父类加载器无法完成加载请求 
        } 
        if(c==null){ 
            // 在父类加载器无法加载时 
            // 再调用本身的findClass方法进行类加载 
            c=findClass(name); 
        } 
    } 
    if(resolve){ 
        resolveClass(c); 
    } 
    return c; 
}

若一个类加载器收到了类加载请求。 1、把 该类加载请求 委派给 父类加载器去完成,而不会自己去加载该类; 2、只有当 父类加载器 反馈 自己无法完成该加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会自己去加载

3. 优点 Java类随着它的类加载器一起具备了一种带优先级的层次关系

1. 如:类 java.lang.Object(存放在rt.jar中)在加载过程中,无论哪一个类加载器要加载这个类,
最终需委派给模型顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。
2. 若没有使用双亲委派模型(即由各个类加载器自行去加载)、用户编写了一个java.lang.Object的类(放在ClassPath中),
那系统中将出现多个不同的Object类,Java体系中最基础的行为就无法保证
五、自定义类加载器

主要是通过继承自 ClassLoader 类 从而自定义一个类加载器

步骤1:自定义类加载器 MyClassLoader.java

// 继承自ClassLoader类
public class MyClassLoader extends ClassLoader { 
    // 类加载器的名称 
    private String name; 
    // 类存放的路径 
    private String classpath = "E:/"; 
 
    MyClassLoader(String name) { 
        this.name = name; 
    } 
 
    MyClassLoader(ClassLoader parent, String name) { 
        super(parent); 
        this.name = name; 
    } 
 
    @Override 
    public Class findClass(String name) {  
        byte[] data = loadClassData(name); 
        return this.defineClass(name, data, 0, data.length); 
    } 
 
    public byte[] loadClassData(String name) { 
        try { 
            name = name.replace(".", "//"); 
            System.out.println(name); 
            FileInputStream is = new FileInputStream(new File(classpath + name 
                    + ".class")); 
            byte[] data = new byte[is.available()]; 
            is.read(data); 
            is.close(); 
            return data; 
 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
        return null; 
    } 
}

步骤2:定义待加载的类

public class TestObject { 
    public void print() { 
        System.out.println("hello DiyClassLoader"); 
 
    } 
}

步骤3:定义测试类

public class Test { 
 
    public static void main(String[] args) throws InstantiationException, 
            IllegalAccessException, ClassNotFoundException { 
       
        MyClassLoader cl = new MyClassLoader("myClassLoader"); 
        // 步骤1:创建自定义类加载器对象

        Class clazz = cl.loadClass("com.carson.TestObject"); 
        // 步骤2:加载定义的测试类:myClassLoader类

        TestObject test= (TestObject) clazz.newInstance(); 
        // 步骤3:获得该类的对象
        test.print(); 
        // 输出
    } 
 
}

// 输出结果
hello DiyClassLoader
关注
打赏
1658837700
查看更多评论
立即登录/注册

微信扫码登录

0.0391s