java中类加载机制

在java中的每一个类都会对应一个Class对象,我们通常把这个Class对象称之为字节码对象,那么这个字节码对象是由谁来产生的呢?java中的类是由谁来加载进内存的呢?接下来我介绍的就是负责将java中的字节码文件加载到内存,创建Class对象的类ClassLoader,也就是java中的类加载器。

类加载器一般由系统来提供,不需要我们自己实现,但是通过我们自定义的类加载器可以更加灵活的加载class文件。在java中有三个默认的类加载器分别是Bootstrap ClassLoader(启动类加载器)、Extension ClassLoader(扩展类加载器)、Application ClassLoader(应用程序类加载器),其中启动类加载器负责加载java的基础类,扩展类加载器负责加载java的一下扩展类,应用程序类加载器负责加载应用程序的类也就是在类路径中指定的类。这三个类加载器有父子关系,其中Application ClassLoader的父类是Extension ClassLoader,Extension ClassLoader的父类是Bootstrap ClassLoader。但是它们不是父子的继承关系而是父子的委派关系,在Application ClassLoader中有一个parent变量指向Extension ClassLoader,在Extension ClassLoader中也有一个parent变量指向Bootstrap ClassLoader。一个类在被加载时首先会判断这个类是否被加载过,如果这个类已经被加载过了就会返回Class对象,如果没有被加载过会先让父加载器加载如果父加载器加载成功直接返回Class对象,如果父加载器没有加载成功才自己尝试加载。这个类加载的过程一般我们称之为"双亲委派"模式。java之所以采用这种方式加载类为了防止类的重复加载,防止用户自定义的类型覆盖java原有的类型。

了解了java中的类加载机制后我们要介绍今天的重点类:ClassLoader。类ClassLoader是一个抽象类,每一个Class对象都有一个getClassLoader()方法来获取ClassLoader。ClassLoaer类中有一个getParent()方法来获取这个ClassLoader的父加载器。

public class TestClassLoader {
    public static void main(String[] args) {
         ClassLoader classLoader = TestClassLoader.class.getClassLoader();
         while(classLoader!=null){
             System.out.println(classLoader.getClass().getName());
             classLoader = classLoader.getParent();
         }
         System.out.println(String.class.getClassLoader()); 

    }
}

输出:
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null

这里需要说明的是Bootstrap ClassLoader是用C++写的所以最后一个输出返回了null。在ClassLoader中还有一个静态的方法返回的是默认的系统类加载器 getSystemClassLoader。ClassLoader中还有一个重要的方法loadClass(String name)用来加载类。

public class TestClassLoader {
    public static void main(String[] args) throws ClassNotFoundException {
         ClassLoader classLoader = ClassLoader.getSystemClassLoader();
         Class cls = classLoader.loadClass("com.reflect.generic.TestClassLoader");
         System.out.println(cls.getClassLoader().getClass().getName());
    }
}

这里需要说明一下,在学习反射的时候我学习到使用Class类的静态方法forName也可以加载类,今天我们介绍的ClassLoader的loadClass方法也可以加载类,但是他们两个是有区别的,使用Class的forName方法加载类会初始化类但是使用loadClass方法不会初始化类。

java的类加载机制是允许用户自定义类加载器的。自定义的类加载器可以实现很多强大的功能比如热部署技术和web不用应用的隔离。在java中要自定义类加载器并没有想象中的那么困难。我们只需要继承ClassLoader类然后重写里面的findClass方法即可。findClass中我们主要是写如何通过自己的逻辑找到class文件的字节形式然后调用defineClass(String name,byte[] b,int off,int len)其中name是类名,b是存放字节码的字节数组,off是起始位置,len是长度。

public class MyClassLoader extends ClassLoader {

    private String path;

    public MyClassLoader(String path){
         this.path = path;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] b = getClassByte(name,this.path);
        return defineClass(name, b, 0, b.length);

    }

    private static byte[] getClassByte(String name,String path){
        String classPath = name.replace(".","/");
        String fileName = path+classPath + ".class";
        BufferedInputStream ins = null;
        ByteArrayOutputStream os =null;
        try {
            ins = new BufferedInputStream(new FileInputStream(new File(fileName)));
            os = new ByteArrayOutputStream();
            int i =0;
            while((i=ins.read())!=-1){
                os.write(i);

            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
            if(ins!=null){
                try {
                    ins.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if(os!=null){
                try {
                    os.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return os.toByteArray();
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

}

在调用的时候:

public class TestClassLoader {
    public static void main(String[] args) throws ClassNotFoundException {
        MyClassLoader myclassLoader = new MyClassLoader("E:\\react\\testclassloader\\");
        Class cls = myclassLoader.findClass("com.reflect.generic.User");
        System.out.println(cls.getClassLoader().getClass().getName());
    }
}

输出:
com.reflect.generic.MyClassLoader

这里需要说明一下,不同的类加载器加载同一个类获取的Class对象是不同的。对于同一个类的每个ClassLoader只能加载一次。

原文地址:https://www.cnblogs.com/suyang-java/p/10987891.html

时间: 2024-10-10 09:37:15

java中类加载机制的相关文章

Java中类加载机制和反射技术

我们知道一个对象在运行时有两种类型,一个是编译类型,一个是运行时类型.在程序运行时,往往是需要发现类和对象的真实的信息的.那么如何获的这种信息呢? 其一,如果我们在编译和运行时都知道类型的具体信息,这时是可以手动将一个对象转换为运行时的类型. 其二,如果我们在编译时无法预知对象和类到底是属于哪些类,那么程序只有依靠运行时的信息来发现对象和类的真实的信息了,这时就必须要用到反射技术. 在谈具体的发射技术之前,我想先回顾下,有关类的加载的一些基本的性质和原理,以方便我们更好地理解,反射的作用和特点.

JAVA 初识类加载机制 第13节

JAVA 初识类加载机制 第13节 从这章开始,我们就进入虚拟机类加载机制的学习了.那么什么是类加载呢?当我们写完一个Java类的时候,并不是直接就可以运行的,它还要编译成.class文件,再由虚拟机解释给当前的操作系统去执行.这些过程都是我们看不见的,我们能看见的也就是一个.class文件.既然虚拟机要解释这些.class文件给当前的操作系统听,那么他怎么获得这些.class文件呢?虚拟机获得这些.class文件的过程就是类加载了. 所以,总结来说就是:虚拟机将.class文件从磁盘或者其他地

Java虚拟机类加载机制——案例分析

原文出处: 朱小厮 在<Java虚拟机类加载机制>一文中详细阐述了类加载的过程,并举了几个例子进行了简要分析,在文章的最后留了一个悬念给各位,这里来揭开这个悬念.建议先看完<Java虚拟机类加载机制>这篇再来看这个,印象会比较深刻,如若不然,也没什么关系~~下面是程序代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package jvm.cla

[转]Java虚拟机类加载机制浅谈

Java语言是一种编译后再经过解释器执行的过程, 解释器主要就是如何处理解释Class文件的二进制字节流.JVM主要包含三大核心部分:运行时数据区,类加载器和执行引擎. 虚拟机将描述类的数据从Class文件加载到内存,并对数据进行校验.准备.解析和初始化,最终就会形成可以被虚拟机使用的Java类型,这就是一个虚拟机的类加载机制.Java中的类是动态加载的,只有在运行期间使用到该类的时候,才会将该类加载到内存中,Java依赖于运行期动态加载和动态链接来实现类的动态使用. 一个类的整个生命周期如下:

ClassLoader Java中类加载出现在哪个阶段,编译期和运行期? 类加载和类装载是一样的吗

1.ClassLoader Java中类加载出现在哪个阶段,编译期和运行期? 类加载和类装载是一样的吗? :当然是运行期间啊,我自己有个理解误区,改正后如下:编译期间编译器是不去加载类的,只负责编译而已,去rt.jar拿数据干嘛,不依然是class文件,jvm是只要是class文件就能运行. 2.类加载ClassLoader,各个类加载器执行顺序是什么? :永远是自己写的加载器先去加载,记住并不是真正的加载,而是双亲委派机制,每个加载器都不真正去加载,而是去让父加载器去加载,想一下,自然界亦是如

java 中类加载器

jar 运行过程和类加载机制有关,而类加载机制又和我们自定义的类加载器有关,现在我们先来了解一下双亲委派模式. java 中类加载器分为三个: BootstrapClassLoader 负责加载 ${JAVA_HOME}/jre/lib 部分 jar 包 ExtClassLoader 加载 ${JAVA_HOME}/jre/lib/ext 下面的 jar 包 AppClassLoader 加载用户自定义 -classpath 或者 Jar 包的 Class-Path 定义的第三方包 类的生命周期

学习java虚拟机 - 类加载机制

学习java虚拟机 - 类加载机制  一.是什么 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 在Java语言里面,类型的加载.链接.初始化过程都是在程序运行期间完成的,Java里天生可以动态扩展的语言特性就是依赖运行期间动态加载和动态连接这个特点实现的.例如,如果编写一个面向接口的应用程序,可以等到运行时在制定实际的实现类:用户可以通过Java预定义的和自定义类加载器,让一个本地的应用程序

java虚拟机类加载机制和双亲委派模型

java虚拟机类加载机制:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型. 类的生命周期是从类被加载到虚拟机内存中,到卸载出内存为止: 类的生命周期: 加载 loading . 验证 verification. 准备 preparation. 解析 resolution. 初始化 initialization. 使用 using. 卸载 unloading 类加载器的层次结构: 双亲委派模型过程: 某个特定的类加载器

Java中反射机制(Reflection)研究及源码演示

如下内容内容是关于 Java中反射机制(Reflection)研究及演示的内容. package com.jiangqq.reflection; import java.lang.reflect.Method; public class Reflection1 { public static void main(String[] args) throws Exception { Class<?> tClass = Class.forName("java.lang.Class"