zoukankan      html  css  js  c++  java
  • JAVA类加载和初始化

    JAVA类的加载和初始化

     

    一、类的加载和初始化过程


    JVM将类的加载分为3个步骤:

    1、加载(Load):class文件创建Class对象。

    2、链接(Link)

    3、初始化(Initialize)

    其中 链接(Link)又分3个步骤,如下图所示:

    类什么时候才被初始化:

    1)创建类的实例,也就是new一个对象

    2)访问某个类或接口的静态变量,或者对该静态变量赋值 或者调用类的静态方法(注意:访问常量不会触发)

    3)反射(Class.forName("com.lyj.load")) (注意:classLoader.loadClass只动态装载,不会初始化)

    4)初始化一个类的子类(会首先初始化子类的父类)

    5)JVM启动时标明的启动类.

     

    注意:

      1、类加载不一定会初始化。一个类只加载一次。

      2、在初始化一个类或接口时,并不会先初始化它所实现的接口。 

      3、如果静态方法或变量在parent中定义,从子类进行调用,则不会初始化子类。 

     

     

    二 、Class.forName(className)与ClassLoader.loadClass

            1、 Class.forName(className)方法,内部实际调用的方法是  Class.forName(className,true,classloader);

                     第2个boolean参数表示类是否需要初始化,  Class.forName(className)默认是需要初始化。一旦初始化,就会触发目标对象的 static块代码执行,static参数也也会被再次初始化。

     

            2、 ClassLoader.loadClass(className)方法,内部实际调用的方法是  ClassLoader.loadClass(className,false); 第2个 boolean参数,表示目标对象是否进行链接,false表示不进行链接

     

            3、数据库驱动使用Class.forName(className)是为了执行静态块代码

     

            4. Class.forName(“pacage.A”).newInstance()与 new A()的效果是一样的。但是newInstance()只能调用无参数构造器,效率低下。 

    三、类加载器:双亲委派机制 - 父类加载器成功加载就使用父类加载器加载。

        启动(Bootstrap)类加载器引导类加载器是用 本地代码实现的类加载器,它负责将 <JAVA_HOME>/lib下面的核心类库(rt.jar) 或 -Xbootclasspath选项指定的jar包等 虚拟机识别的类库 加载到内存中。由于引导类加载器涉及到虚拟机本地实现细节由 C++ 实现,不是 ClassLoader 子类)开发者无法直接获取到启动类加载器的引用(ExtClassLoader的父loader返会返回null),所以不允许直接通过引用进行操作。

        扩展(Extension)类加载器扩展类加载器是由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的,它负责将 <JAVA_HOME >/lib/ext或者由系统变量-Djava.ext.dir指定位置中的类库 加载到内存中。开发者可以直接使用标准扩展类加载器。

        系统(System)类加载器系统类加载器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的,它负责将 用户类路径(java -classpath或-Djava.class.path变量所指的目录,即当前类所在路径及其引用的第三方类库的路径,如第四节中的问题6所述)下的类库 加载到内存中。开发者可以直接使用系统类加载器。

         注意:同一class实例在同一个类加载器命名空间(由加载器和其所有父加载器构成)下是唯一的。

                   而不同的类加载器或者同一类加载器不同实例,加载同一个class,在内存中是会产生多个class对象的。

    四、线程上下文加载器

           Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。

      这些 SPI 的接口由 Java 核心库来提供,而这些 SPI 的实现代码则是作为 Java 应用所依赖的 jar 包被包含进类路径(CLASSPATH)里。SPI接口中的代码经常需要加载具体的实现类。那么问题来  了,SPI的接口是Java核心库的一部分,是启动类加载器来加载的;SPI的实现类是由系统类加载器来加载的。启动类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能委派给系统类加载器,因为它是系统类加载器的祖先类加载器。但是可以使用Thread.getContextClassLoader()调用,比如,bootstartp ClassLoader通过它去载入只有SystemClassLoader可见的类

      线程上下文类加载器正好解决了这个问题。如果不做任何的设置,Java 应用的线程的上下文类加载器默认就是系统上下文类加载器。在 SPI 接口的代码中使用线程上下文类加载器,就可以成功的加载到 SPI 实现的类。线程上下文类加载器在很多 SPI 的实现中都会用到。

        线程上下文类加载器(context class loader)是从 JDK 1.2 开始引入的。Java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。如果不做任何的设置,Java应用的线程上下文类加载器默认就是系统类加载器。在 SPI 接口的代码中使用线程上下文类加载器,就可以成功的加载到 SPI 实现的类。

    通过SPI方式,读取 META-INF/services 下文件中的类名,使用线程上下文类加载器加载。



  • 相关阅读:
    char类型细节
    Hibernate面试题
    线程
    IO流
    集合
    链表相关的一点东西
    正则表达式学习
    python中的变量域问题
    python的输出和输入形式
    python mutable 和 immutable
  • 原文地址:https://www.cnblogs.com/caoshouling/p/8644948.html
Copyright © 2011-2022 走看看