精华内容
下载资源
问答
  • java类加载机制

    2021-02-06 15:34:49
    这就是java类加载机制; 1、加载的生命周期 加载的生命周期主要包括加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸载...

    ​什么是类加载机制呢?

    java虚拟机将编译后的class文件加载到内存中,进行校验、转换、解析和初始化,到最终的使用。这就是java类加载机制;

    1、类加载的生命周期

    类加载的生命周期主要包括加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸载(Unloading)等阶段,其中验证、准备、解析3阶段也可以称为连接(Lingking),如下图:
    在这里插入图片描述

    2、类加载的时机

    在类加载的生命周期中,加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,其加载过程一定是按照这个顺序执行的。而解析阶段有点特殊,在某些特定的情况下,它是在初始化之后开始的。

    那什么情况下需要开始类加载的第一个阶段呢?对此,Java虚拟机规范中并没有进行强制约束,并且交给虚拟机的具体实现来自由把握。但是对于初始化阶段,虚拟机规范则是严格规定了有且只有5种情况(类没有初始化)必须立即对类进行“初始化”(而加载、验证、准备自然需要在此之前开始):1)、遇到new、getstatic、putstatic或invokestaic这四条字节码指令时。

    2)、使用java.lang.reflect包的方法对类进行反射调用的时候。

    3)、当初始化一个类的时候,如果发现父类还没有初始化,则需要先触发其父类的初始化。

    4)、当虚拟机启动时,用户需要指定一个要执行的主类,虚拟机会先初始化这个主类。

    5)、当使用JDK1.7的动态语言支持的时候,如果一个
    java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个句柄所对应的类没有进行过初始化,则需要先触发器初始化。

    3、类加载过程

    接下来详细介绍类加载的几个重要阶段:加载、验证、准备、解析和初始化

    3.1、加载

    在加载阶段,虚拟机主要执行以下三个操作

    1)、通过类的全限定名来获取定义这个类的二进制字节流。

    2)、将这个字节流所代表的静态存储结构转化成方法区的运行时数据结构。

    3)、在内存中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的访问入口。

    这个阶段相比其他阶段来说,是开发人员可控性最强的阶段。因为这个阶段既能使用系统提供的加载器(这个加载器后面会进行介绍)加载,又能通过开发人员自定义的加载器进行加载。

    在加载这个阶段还有一个需要注意的地方,在执行第一个操作时,需要知道可以从哪里获取class文件,例如:

    1)、从压缩文件中读取(JAR,WAR等)

    2)、从本地磁盘中获取

    3)、从网络上获取(Applet)

    4)、运行过程中动态生成(动态代理)

    5)、其他文件生成(jsp生成对应的class文件)

    6)、从数据库中读取

    3.2、验证

    验证阶段主要有4个阶段的验证:文件格式验证、元数据验证、字节码验证和符号验证

    3.2.1、文件格式验证

    这一阶段要验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理,主要包括魔数、版本号、常量池等验证。

    3.2.2、元数据验证

    这个阶段是对字节码描述的信息进行语义分析,以保证其描述的信息符合java语言规范的要求。主要包括是否有父类,类中的字段、方法是否与父类冲突,如果不是抽象类,是否实现了其父类或接口中要求实现的所有方法等;

    3.2.3、字节码验证

    这个阶段是在元数据验证之后,对类的方法体进行校验分析,保证被校验类的方法在运行时不会做出危害虚拟机的安全事件,主要目的是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。也是验证过程最复杂的一个阶段。

    3.2.4、符号引用验证

    这个阶段的校验发生在虚拟机将符号引用转化为直接引用的时候。是对类自身以外的信息进行匹配性校验。主要目的是确保解析动作能正常执行。

    3.3、准备

    准备阶段是为类变量分配内存并设置类变量初始值的阶段,分配这些内存是在方法区里面进行的,这个阶段有两点需要重点介绍:

    1)、只有类变量(被static修饰的变量)会分配内存,不包括实例变量,实例变量是在对象实例化的时候在堆中分配内存的。

    2)、设置类变量的初始值是数量类型对应的默认值,而不是代码中设置的默认值。例如public static int number=111,这类变量number在准备阶段之后的初始值是0而不是111。而给number赋值为111是在初始化阶段。

    基本数据类型默认值如下:
    在这里插入图片描述
    3.4、解析

    解析阶段是虚拟机将常量池里内的符号引用转换为直接引用。这里注意2个概念:

    1)、符号引用:以一组符号来描述所有引用的目标,符号可以是任何形式的字面量,只要使用时能正确定义到目标即可。

    2)、直接引用:可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。

    解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符等7类符号引用进行的。

    3.5、初始化

    这个阶段是类加载过程的最后一步,是代码真正开始执行的时候,在这个阶段,开发人员可以根据自己的需求去给类变量初始化赋值。简单来说就是执行类构造器()方法的过程。

    4、类加载器

    接下来看看是什么是类加载器:

    虚拟机设计团队将加载动作放到了Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称之为“类加载器”。

    4.1、系统提供的3种类加载器

    1)、启动类加载器(Bootstrap ClassLoader):负责将存放在<JAVA_HOME>\lib目录中,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中。(注:仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)

    2)、扩展类加载器(Extension ClassLoader):负责加载<JAVA_HOME>\lib\ext目录中的,或被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。

    3)、应用程序类加载器(Application ClassLoader):负责加载用户路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,一般情况下该类加载是程序中默认的类加载器。

    这三种加载器的加载顺序如下:
    在这里插入图片描述
    4.2、双亲委派模型
    在这里插入图片描述
    如上图展示的类加载器之间的这种层次关系就是双亲委派模型。双亲委派模型要求除了顶层的启动类加载器外,其他的类加载器都应有自己的父类加载器。

    双亲委派原则的好处:

    1)、避免重复加载同一个类;

    2)、防止用户任意修改java中的类;

    双亲委派:如果一个类加载器收到类加载的请求,他首先不会自己去尝试加载这个类,而是把请求委派给父类加载器去完成,每一层次的类加载器都是这样,因此所有的加载请求最终都应该传送到底层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试去加载。

    如下图所示:
    在这里插入图片描述
    4.3、自定义类加载器

    上面讲述的是系统提供的类加载器以及它们之间的关系,还有很多情况需要我们自定义类加载器。那该如何定义呢?有以下两种方式

    1、如果我们自定义的加载器不想破坏双亲委派,继承 java.lang.ClassLoader 类并重写 findClass 方法。

    2、如果使用我们自定义的加载器破坏双亲委派,继承 ava.lang.ClassLoader 类并重写loadClass(java.lang.String) 方法。

    该文章转载自微信公众号:程序猿分享编程

    展开全文
  • Java类加载机制

    2020-12-22 23:42:38
    加载是指将的.class文件读取进内存中,并将其放在JVM运行时数据区的方法区内,然后在堆中创建一个java.lang.Class对象,用于封装在方法区中的数据结构,同时作为方法区数据的访问入口。 2 的生命周期 的...

    1 类的加载

    类的加载是指将类的.class文件读取进内存中,并将其放在JVM运行时数据区的方法区内,然后在堆中创建一个java.lang.Class对象,用于封装类在方法区中的数据结构,同时作为方法区数据的访问入口。

    2 类的生命周期

    类的生命周期指一个class文件从加载到卸载的整个过程
    ​​​​​​​​​​​​​​生命周期

    3 类的加载过程

    JVM将类的加载分为三个阶段:装载(Load)链接(Link)初始化(Initialize)

    3.1 装载:查找并加载类的二进制数据

    在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的Java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

    3.2 链接:验证、准备、解析

    • 验证
      校验字节码文件的正确性、安全性,包括四种验证:文件格式验证、元数据的验证、字节码验证、符号引用验证
    • 准备
      为类变量(static修饰的字段变量)分配内存并且设置该类变量的初始值,这里不包含final修饰的static,因为final在编译的时候就已经分配了。也不会为实例变量分配初始化,实例变量会随着对象分配到堆内存中。
    • 解析
      把常量池中的符号引用转换为直接引用,静态链接过程

    3.3 初始化:初始化静态变量,执行静态代码块

    3.3.1 类的初始化时机

    如果一个类被直接引用,就会触发类的初始化。在Java中,直接引用的情况有:
    ● 通过new关键字实例化对象、读取或设置类的静态变量、调用类的静态方法
    ● 通过反射执行以上三种行为
    ● 初始化子类的时候,会触发父类的初始化
    ● 作为程序的入口直接运行时(main方法)

    # 类初始化时机DEMO
    class InitClass {
        static {
            System.out.println("class init");
        }
        public static String data = null;
        public static void method (){};
    }
    
    class SubInitClass extends InitClass{}
    
    public class TestInitClass{
        // 类的初始化4:main方法运行,触发类初始化
        // 初始化仅会进行一次,所以main方法中只会输出一次 "class init"
        public static void main(String[] args) throws Exception{
            // 类初始化1:new对象、读取或设置类静态变量、调用类的静态方法
            new InitClass();
            InitClass.data = "";
            String data = InitClass.data;
            InitClass.method();
    
            // 类初始化2:反射
            Class<InitClass> clazz = InitClass.class;
            clazz.newInstance();
            Field field = clazz.getDeclaredField("data");
            field.get(null);
            field.set(null, "s");
            Method method = clazz.getDeclaredMethod("method");
            method.invoke(null,null);
    
            // 类的初始化3:实例化子类,引起父类实例化
            new SubInitClass();
        }
    }
    

    3.3.2 类初始化步骤

    • 假如这个类还没有被加载和链接,则程序先加载并连接该类(动态加载:主类在运行过程中如果使用到其他类,会逐步加载这些类 eg:jar包和war包中的类并不是一次性都加载到内存中,而是使用时才加载)
    • 假如该类的直接父类还没有被初始化,则先初始化其直接父类
    • 假如类中有初始化语句,则系统依次执行这些初始化语句
    # 动态加载DEMO
    public class TestDynamicLoad {
        static {
            System.out.println("load TestDynamicLoad");
        }
    
        public static void main(String[] args) {
            // new触发类的加载
            new A();
    
            // 空引用不触发类的加载
            System.out.println("TestDynamicLoad main");
            A a = null;
    
            // 调用静态常量,不触发类的加载
            System.out.println("TestDynamicLoad main2");
            System.out.println(A.NAME);
    
            // 调用静态变量,触发类的加载
            System.out.println("TestDynamicLoad main3");
            System.out.println(A.name);
        }
    }
    
    class A{
        public static final String NAME = "a";
        public static String name = "aName";
        static {
            System.out.println("load A");
        }
        public A(){
            System.out.println("initial A");
        }
    }
    

    4 类加载后方法区存储内容

    类被加载到方法区中后主要包含运行时常量池、类型信息、字段信息、方法信息、类加载器的引用、对应class实例的引用等信息。

    类加载器的引用:
    这个类到类加载器实例的引用

    对应class实例的引用:
    类加载器在加载类信息放到方法区中后,会创建一个对应的Class 类型的对象实例放到堆(Heap)中, 作为开发人员访问方法区中类定义的入口和切入点。

    5 类加载器

    类加载过程主要通过类加载器实现,Java中有以下几种类加载器:

    • 引导类加载器(Bootstrap ClassLoader)
      负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar\charsets.jar等,由C++实现,不是ClassLoader子类,Java程序无法直接引用。
    • 拓展类加载器(Extension ClassLoader)
      负责加载支撑JVM运行的位于JRE的lib目录下的ext拓展目录中的类库
    • 应用类加载器(App ClassLoader)
      负责加载ClassPath路径下的类包,主要加载开发人员写的类
    • 自定义类加载器(Custom ClassLoader)
      负责加载用户自定义路径下的类包

    类加载器之间存在父子层级结构:
    类加载器父子层级

    # classloader父子层级DEMO
    public static void main(String[] args) {
        ClassLoader app = ClassLoader.getSystemClassLoader();
        ClassLoader ext = app.getParent();
        ClassLoader boot = ext.getParent();
        System.out.println("the bootstrapLoader: " + boot);
        System.out.println("the extClassLoader: " + ext);
        System.out.println("the appClassLoader: " + app);
    }
    
    #结果
    the bootstrapLoader: null // c++实现,java获取不到引用
    the extClassLoader: sun.misc.Launcher$ExtClassLoader@6e0be858
    the appClassLoader: sun.misc.Launcher$AppClassLoader@18b4aac2
    

    在这里插入图片描述在这里插入图片描述

    6 双亲委派机制

    6.1 定义

    双亲委派机制是指当一个类加载器收到一个类加载请求时,该类加载器首先会把请求委派给父类加载器。每个类加载器都是如此,只有在父类加载器在自己的加载路径下找不到指定类时,子类才会尝试自己去加载。

    6.2 工作过程

    在这里插入图片描述

    1. 当应用程序类加载器收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器Extension ClassLoader去完成。
    2. 当Extension ClassLoader收到一个类加载请求时,他首先也不会自己去尝试加载这个类,而是将请求委派给父类加载器Bootstrap ClassLoader去完成。
    3. 如果Bootstrap ClassLoader加载失败(在<JAVA_HOME>\lib中未找到所需类),就会让Extension ClassLoader尝试加载。
    4. 如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载。
    5. 如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载。
    6. 如果均加载失败,就会抛出ClassNotFoundException异常。

    6.3 双亲委托机制实现源码(JDK 1.8)

    在这里插入图片描述

    6.4 双亲委托作用

    1. 沙箱安全机制:防止核心类库被篡改,自己写的java.lang.String不会被加载。
    2. 避免类的重复加载:当父加载器已经加载过该类时,子加载器就没必要重新加载一次,保证被加载类的唯一性。
    # 自定义java.lang.String,运行异常,查找不到main方法
    package java.lang;
    public class String {
        public static void main(String[] args) {
            System.out.println("My String Method");
        }
    }
    

    在这里插入图片描述

    7 自定义类加载器

    /**
     * 自定义类加载器
     * 1. 继承ClassLoader父类,实现自定义findClass方法
     * 2. findClass职责:加载class类到内存,并转换为Class类
     **/
    public class MyClassLoader extends ClassLoader{
        private final String classPath;
        public MyClassLoader(String classPath){
            this.classPath = classPath;
        }
    
        /**
         * 通过类全限定名加载类文件byte数组
         * @param name 加载类路径
         * @return class文件字节数组
         * @throws Exception
         */
        private byte[] loadByte(String name) throws Exception {
            name = name.replaceAll("\\.","/");
            FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
            int length = fis.available();
            byte[] data = new byte[length];
            fis.read(data);
            fis.close();
            return data;
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] data = loadByte(name);
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ClassNotFoundException();
            }
        }
    }
    

    8 如何打破双亲委派机制

    核心:继承ClassLoader,重写类加载方法,实现加载逻辑,不委托给双亲加载
    在这里插入图片描述

    /**
     * 自定义类加载器
     * 1. 继承ClassLoader父类,实现自定义findClass方法
     * 2. findClass职责:加载class类到内存,并转换为Class类
     **/
    public class MyClassLoader extends ClassLoader{
        private final String classPath;
        public MyClassLoader(String classPath){
            this.classPath = classPath;
        }
    
        /**
         *  通过类路径加载类文件byte数组
         * @param name 加载类路径
         * @return class文件字节数组
         * @throws Exception
         */
        private byte[] loadByte(String name) throws Exception {
            name = name.replaceAll("\\.","/");
            FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
            int length = fis.available();
            byte[] data = new byte[length];
            fis.read(data);
            fis.close();
            return data;
        }
    
        /**
         * 重写类加载方法,实现自己的加载逻辑,不委托给双亲加载
         * @param name
         * @param resolve
         * @return
         * @throws ClassNotFoundException
         */
        @Override
        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) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    
                    // 定义某些类不走双亲委派机制,直接进行类的加载
                    if (name.startsWith("com.matt.learn")){
                        c = findClass(name);
                    }
                    else {
                        c = this.getParent().loadClass(name);
                    }
    
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte[] data = loadByte(name);
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ClassNotFoundException();
            }
        }
    }
    

    拓展
    Tomcat类加载机制实现:Tomcat如何保证不同的webapp应用中加载不同版本类库?JSP热加载机制?

    参考文档:
    https://www.cnblogs.com/ityouknow/p/5603287.html
    https://blog.csdn.net/zhengzhb/article/details/7517213

    展开全文
  • JAVA类加载机制详解

    2021-03-01 10:59:51
    “代码编译的结果从本地机器码转变为字节码...2、类加载机制3、执行机制我们这里主要介绍编译和加载这两种机制。一、源码编译代码编译由JAVA源码编译器来完成。主要是将源码编译成字节码文件(class文件)。字节码...

    “代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是变成语言发展的一大步”,这句话出自《深入理解JAVA虚拟机》一书,后面关于jvm的系列文章主要都是参考这本书。

    JAVA源码编译由三个过程组成:

    1、源码编译机制。

    2、类加载机制

    3、类执行机制

    我们这里主要介绍编译和类加载这两种机制。

    一、源码编译

    代码编译由JAVA源码编译器来完成。主要是将源码编译成字节码文件(class文件)。字节码文件格式主要分为两部分:常量池和方法字节码。

    二、类加载

    类的生命周期是从被加载到虚拟机内存中开始,到卸载出内存结束。过程共有七个阶段,其中到初始化之前的都是属于类加载的部分

    加载----验证----准备----解析-----初始化----使用-----卸载

    系统可能在第一次使用某个类时加载该类,也可能采用预加载机制来加载某个类,当运行某个java程序时,会启动一个java虚拟机进程,两次运行的java程序处于两个不同的JVM进程中,两个jvm之间并不会共享数据。

    1、加载阶段

    这个流程中的加载是类加载机制中的一个阶段,这两个概念不要混淆,这个阶段需要完成的事情有:

    1)通过一个类的全限定名来获取定义此类的二进制字节流。

    2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

    3)在java堆中生成一个代表这个类的Class对象,作为访问方法区中这些数据的入口。

    由于第一点没有指明从哪里获取以及怎样获取类的二进制字节流,所以这一块区域留给我开发者很大的发挥空间。这个我在后面的类加载器中在进行介绍。

    2、准备阶段

    这个阶段正式为类变量(被static修饰的变量)分配内存并设置类变量初始值,这个内存分配是发生在方法区中。

    1、注意这里并没有对实例变量进行内存分配,实例变量将会在对象实例化时随着对象一起分配在JAVA堆中。

    2、这里设置的初始值,通常是指数据类型的零值。

    private static int a = 3;

    这个类变量a在准备阶段后的值是0,将3赋值给变量a是发生在初始化阶段。

    3、初始化阶段

    初始化是类加载机制的最后一步,这个时候才正真开始执行类中定义的JAVA程序代码。在前面准备阶段,类变量已经赋过一次系统要求的初始值,在初始化阶段最重要的事情就是对类变量进行初始化,关注的重点是父子类之间各类资源初始化的顺序。

    java类中对类变量指定初始值有两种方式:1、声明类变量时指定初始值;2、使用静态初始化块为类变量指定初始值。

    初始化的时机

    1)创建类实例的时候,分别有:1、使用new关键字创建实例;2、通过反射创建实例;3、通过反序列化方式创建实例。

    new Test();

    Class.forName(“com.mengdd.Test”);

    2)调用某个类的类方法(静态方法)

    Test.doSomething();

    3)访问某个类或接口的类变量,或为该类变量赋值。

    int b=Test.a;

    Test.a=b;

    4)初始化某个类的子类。当初始化子类的时候,该子类的所有父类都会被初始化。

    5)直接使用java.exe命令来运行某个主类。

    除了上面几种方式会自动初始化一个类,其他访问类的方式都称不会触发类的初始化,称为被动引用。

    1、子类引用父类的静态变量,不会导致子类初始化。

    public classSupClass

    {public static int a = 123;static{

    System.out.println("supclass init");

    }

    }public class SubClass extendsSupClass

    {static{

    System.out.println("subclass init");

    }

    }public classTest

    {public static voidmain(String[] args)

    {

    System.out.println(SubClass.a);

    }

    }

    执行结果:

    supclass init123

    2、通过数组定义引用类,不会触发此类的初始化

    public classSupClass

    {public static int a = 123;static{

    System.out.println("supclass init");

    }

    }public classTest

    {public static voidmain(String[] args)

    {

    SupClass[] spc= new SupClass[10];

    }

    }

    执行结果:

    3、引用常量时,不会触发该类的初始化

    public classConstClass

    {public static final String A= "MIGU";static{

    System.out.println("ConstCLass init");

    }

    }public classTestMain

    {public static voidmain(String[] args)

    {

    System.out.println(ConstClass.A);

    }

    }

    执行结果:

    MIGU

    用final修饰某个类变量时,它的值在编译时就已经确定好放入常量池了,所以在访问该类变量时,等于直接从常量池中获取,并没有初始化该类。

    初始化的步骤

    1、如果该类还没有加载和连接,则程序先加载该类并连接。

    2、如果该类的直接父类没有加载,则先初始化其直接父类。

    3、如果类中有初始化语句,则系统依次执行这些初始化语句。

    在第二个步骤中,如果直接父类又有直接父类,则系统会再次重复这三个步骤来初始化这个父类,依次类推,JVM最先初始化的总是java.lang.Object类。当程序主动使用任何一个类时,系统会保证该类以及所有的父类都会被初始化。

    展开全文
  • (1)问题分析:Class文件由装载器装载后,在JVM中将形成一份...虚拟机把描述的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载...

    (1)问题分析:

    Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能。

    虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。

    (2)核心答案讲解:

    类装载器就是寻找类的字节码文件,并构造出类在JVM内部表示的对象组件。在Java中,类装载器把一个类装入JVM中,要经过以下步骤:

    1)装载:查找和导入Class文件;

    2)链接:把类的二进制数据合并到JRE中;

    A)校验:检查载入Class文件数据的正确性;

    B)准备:给类的静态变量分配存储空间;

    C)解析:将符号引用转成直接引用;

    3)初始化:对类的静态变量,静态代码块执行初始化操作

    Java程序可以动态扩展是由运行期动态加载和动态链接实现的;比如:如果编写一个使用接口的应用程序,可以等到运行时再指定其实际的实现(多态),解析过程有时候还可以在初始化之后执行;比如:动态绑定(多态)。

    (3)问题扩展

    由于Java的跨平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化,包括:

    1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;

    2)如果类中存在初始化语句,就依次执行这些初始化语句。

    类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子类)。

    猜你喜欢:

    展开全文
  • 在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题:class Grandpa{static{System.out.println("爷爷在静态代码块");}}class Father extends Grandpa{static{System.out.println("爸爸在...
  • 2、类加载机制3、执行机制我们这里主要介绍编译和加载这两种机制。一、源码编译代码编译由JAVA源码编译器来完成。主要是将源码编译成字节码文件(class文件)。字节码文件格式主要分为两部分:常量池和方法字节码...
  • ClassLoader类加载Java 中的类加载器大致可以分成两:一是系统提供的:引导类加载器(Bootstrap classloader):它用来加载Java的核心库(如rt.jar),是用原生代码而不是java来实现的,并不继承自java.lang....
  • 双亲委派机制其实并不复杂,其实就是子类(应用程序加载器)查询自己所管理的区域有没有加载过需要加载,没有的话就要父类(扩展类加载器)去查,然后父类(扩展类加载器)没有找到的话
  • java类加载机制概述1

    2021-02-13 01:39:57
    从事java研发必然少不了对java类加载机制的涉及,本文结合例子讲述java classloader工作机制。一 jvm 类加载机制1)jvm位置:java是运行在java虚拟机上的程式,java虚拟机物理层面上来讲,就是我们安装在电脑上的jre...
  • Java 类加载机制

    2021-03-09 21:44:57
    1、什么是加载类加载指的是将的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在java堆区创建一个java.lang.Class对象,用来封装在方法区内的数据结构。加载的最终产品...
  • 前言在之前的文章中,我经常提到java类加载,ClassLoader等名词,而...相信你此时已经充满了疑惑,那么本篇我们就来深入浅出的分析ClassLoader加载器和JAVA类加载机制吧初识ClassLoaderClassLoader加载器...
  • Java 类加载机制详解

    2021-02-25 19:16:14
    什么是 Java 类加载机制?Java 虚拟机一般使用 Java 的流程为:首先将开发者编写的 Java 源代码(.java文件)编译成 Java 字节码(.class文件),然后加载器会读取这个 .class 文件,并转换成 java.lang.Class 的实例...
  • 类加载机制,执行顺序如下: 1.装载:查找和导入或接口的二进制数据; 2.链接:(1)检验:检查导入或接口的二进制数据的正确性; (2)准备:给的静态变量分配并初始化存储空间; (3)解析(可略):...
  • java类加载机制是什么

    2021-03-12 21:27:08
    加载机制类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括了:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(using)、和卸载...
  • Java类加载机制详解【java面试题】 (1)问题分析: Class文件由装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户...
  • 关于类加载类加载器的使用 //隐式加载 User user=new User(); //显式加载,并初始化 ...开发人员大多数时候并不需要显示地使用类加载器,但了解整个机制对于了解本身却很有用。 遇到java.lang.Cla
  • JAVA类加载机制

    2021-03-15 20:19:42
    “代码编译的结果从本地机器码转变为字节码...2、类加载机制3、执行机制我们这里主要介绍编译和加载这两种机制。一、源码编译代码编译由JAVA源码编译器来完成。主要是将源码编译成字节码文件(class文件)。字节码...
  • 1 前言:在上一篇文章一文让你明白Java字节码中,我们了解了java字节码的解析过程,那么在接下来的内容中,我们来了解一下加载机制。2 题外话Java的核心是什么?当然是JVM了,所以说了解并熟悉JVM对于我们理解...
  • Java类加载机制的理解

    2021-03-16 21:53:52
    算上大学,尽管接触Java已经有4年时间并对基本的API算得上熟练应用,但是依旧觉得自己对于Java的特性...1.类加载机制类加载是一个将类合并到正在运行着的JVM进程中的过程。首先要加载一个类,我们必须先得将类文件...
  • Java类加载机制与反射

    2021-09-23 17:35:23
    加载器5.1 根加载器5.2 扩展加载器5.3 系统加载器6 类加载机制 1. 的加载 当程序主动使用某个时,如果该还未被加载到内存中,则系统会通过加载,连接,初始化3个步骤对该进行初始化。如果没有意外...
  • java类加载器ClassLoader实际上也是由java语言编写而成,那么我们编译既然要使用类加载器,那什么东西可以编译类加载器的呢?有点类似与先有蛋还是先有鸡的问题,实际上jvm给我们提供了一个最基础的类加载器,就是...
  • 1、加载类加载是指将的.class文件读取进内存中,并将其放在JVM运行时数据区的方法区内,然后在堆中创建一个java.lang.Class对象,用于封装在方法区中的数据结构,同时作为方法区数据的访问入口。...
  • 类加载器分类从虚拟机层面讲分为两大类型的类加载器,一是Bootstrap Classloader即启动类加载器(C++实现),它是虚拟机的一部分,二是其他类型类加载器(JAVA实现),在虚拟机外部,并全部继承ClassLoader。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 414,858
精华内容 165,943
关键字:

java类的加载机制

java 订阅