java类加载过程

classLoader

JVM将类的加载过程分为三个步骤:装载(Load)链接(Link)初始化(Initialize)

1、装载

通过一个类的全限定类名获取类的二进制字节流

将在这个字节流代表的静态存储结果转化为方法区的运行时内存

2、链接

验证:确保被加载类的正确性

​ 主要包括四种验证:文件格式验证、元数据验证、字节码验证、符号引用验证

准备:为类的静态变量分配内存,并将其初始化为默认值

(不包含被final修饰的static,因为final在编译的时候就会分配,准备阶段会显示的初始化)

解析:把常量池中的符号引用转换为直接引用

3、初始化

为类的静态变量赋予正确的初始值

实例查看类加载器

public class ClassLoaderDemo {
    public static void main(String[] args) {
        // 双亲委派模型
        // 父子关系  Bootstrap ClassLoader -> ExtClassLoader  -> AppClassLoader
        ClassLoader classLoader1 = ClassLoaderDemo.class.getClassLoader();
        System.out.println("classLoader1 -> " + classLoader1);
        System.out.println("parent of classLoader1 -> " + classLoader1.getParent());
        // Bootstrap ClassLoader 由 C++ 开发,是 jvm 的一部分,本身并不是java类
        System.out.println("Grandfather of classLoader1 -> " + classLoader1.getParent().getParent());

        // String 、Int 等基础类型由 Bootstrap ClassLoader 加载
        ClassLoader classLoader2 = String.class.getClassLoader();
        System.out.println("classLoader2 -> " + classLoader2);
        try {
            System.out.println(classLoader1.loadClass("java.util.List").getClass().getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        // JDK 类加载对象
        // classLoader -> SecureClassLoader -> URLClassLoader -> ExtClassLoader,AppClassLoader
    }
}
// 运行结果
classLoader1 -> sun.misc.Launcher$AppClassLoader@18b4aac2
parent of classLoader1 -> sun.misc.Launcher$ExtClassLoader@39a054a5
Grandfather of classLoader1 -> null
classLoader2 -> null
null

Java类加载的过程使用的是双亲委派模型(Parents Delegation model)(如果一个类加载器收到了类加载请求,它并不会自己先去加载。而是会把这个请求委托给父类的加载器去执行,如果父类加载器还存在父类加载器,则进一步向上委托,以此递归。如果父类加载器可以完成类加载任务,就成功返回,如果父类加载器无法完成此加载任务,子加载才会尝试自己去加载)

Parents Delegation model

即:向上委托查找,向下委托加载

  1. Bootstrap ClassLoader(启动类加载器)负责装载最核心的Java

    使用C++语言实现,嵌套在JVM 内部,并不集成java.lang.ClassLoader没有父加载器

    加载拓展类和应用程序类加载器,并指定他们为父类加载器

    启动类加载器只加载包名为javajavax和``sun`等开头的类

  2. Extension ClassLoader(扩展类加载器)

    派生于ClassLoader类 ,父类加载器为Bootstrap ClassLoader(启动类加载器)

  3. 应用程序类加载器(Application ClassLoader)

自定义类加载器

在什么情况下 需要自定义的加载器?

1、隔离加载器

2、修改类加载方式

3、拓展加载类

4、防止源码泄露

自定义实现类加载器

// JDK 类加载对象
// classLoader -> SecureClassLoader -> URLClassLoader -> ExtClassLoader,AppClassLoader

将下面的放法打成jar,目的是模拟防止源码泄露。

public String url(String url) {
    String login = "/login" + url;
	return login;
}

idea如何将Java文件打成Jar?

jar -cvf 【打包后的文件名】.jar 【要打包的目录】

URL url = null;
try {
    // 读取需要加载的文件
    url = new URL("file:D:\\work\\design-mode\\ClassLoaderDemo.jar");
    // 类加载器用于从引用JAR文件和目录的URL搜索路径中加载类和资源。假定所有以''结尾的URL均指目录。
    // 否则,假定该URL指向将根据需要打开的JAR文件。 <p>在随后加载类和资源时将使用创建URLClassLoader实例的线程的AccessControlContext。 
    // 默认情况下,仅向加载的类授予权限,以访问创建URLClassLoader时指定的URL。
    URLClassLoader loader = new URLClassLoader(new URL[]{url});
    Class<?> aClass = loader.loadClass("top.lzmvlog.top.jvm.ClassLoaderDemo");
    Object o = aClass.newInstance();
    String newUrl = (String) aClass.getMethod("url", String.class).invoke(o, "/submit");
    System.out.println(newUrl);
} catch (Exception e) {
    e.printStackTrace();
} 
/login/submit

JVM注定将是一条不归路,硬着头皮往前冲吧!