Java动态模型

  1. Java的连接模型允许用户自行设计类装载器,这样可以在运行时定制地扩展用户的程序,用户子定义的类装载器是以定制的方式装载类,可以装载在编译时并不知道的类或者接口;

     使用用户自定义的类装载器可以采用定制的方式加载一个类,即用户可以指定类的获取方式。

  2. class文件把它所有的引用符号保存在常量池中,当一个类型被首次装载到JVM中时,它的所有的符号引用都保存在常量池中,在程序运行的过程中,如果使用到符号引用,需要根据符号引用查找到实体,将符号引用替换成一个直接引用的过程,这个过程称为常量池解析;

     在类装载的过程中,class文件会把它所有的引用符号保存在常量池中,在程序运行的过程中,使用到引用符号的时候,会用直接引用替换引用符号,这个过程称为常量池解析。

  3. 常量池解析的两种策略

      (1)早解析 在类的装载,连接,初始化阶段,在Java的main()函数执行之前解析常量池中的符号引用;

      (2)晚解析 直至符号引用被使用时,才对常量池进行解析

  4. 动态扩展Java程序的两种方式

       a.使用系统加载器的forName()方法,这个方法会加载,连接和初始化一个类型;

       b.使用用户自定义的类型加载器,调用自定义加载器的loadClass()方法,这个方法只会加载一个类型,并不会让它初始化,这样程序还是不能够直接使用这个类。

       综上,使用系统类装载器的好处是可以初始化类型,使用用户自定义装载器的好处是可以采用定制的方法装载类型,即用户可以决定采用何种方式获取类型的class文件,并且可以保证安全性,因为只有同一个类型装载器装载的类型才彼此可见,它们位于同一个命名空间中。

       所有返回Class对象的装载器都是初始化类型装载器,真正装载一个类型的是定义类装载器。

      (1)使用java.lang.Class的forName()方法

      (2)使用用户自定义的类装载器的loadClass()方法

    4.1 动态扩展Java程序最直接的方式是使用java.lang.Class的forName()方法,如果需要请求的类型(无论是类还是接口类型)需要在装载时就初始化,必须使用forName()方法,因为loadClass()方法返回类型的时候,类型可能并没有被连接,或者初始化,而只有当一个类型被正确的初始化之后,Java程序才能够使用这个类型。

    4.2 当需要采用定制的方式加载类型时,需要采用自定义类加载器,比如,如果类型从网上获取,从数据库中获取,或者从加密文件中获取,在这种情况下,需要使用定制的方式加载这个类型,采用用户自定义的类加载器,可以通过定制的方式将类型的全限定名转换成为一个Javaclass文件格式的字节数组,另外,使用自定义的类加载器可以保证安全性,使用同一个类装载器装载的类型彼此可见,使用不同类装载器装载的类型彼此之间不可见。

    4.3 JVM解析常量池的符号引用时,JVM会使用装载引用类型的同一个类装载器来装载所需要的类型

    4.4 采用类装载器装载类型时,会采用父亲委托机制,即类装载器会委托他的父类装载器装载某个类型,只有当父类不能够装载该类型时,才由该装载器对类型进行装载,当某个类型装载一个类型成功之后,会返回代表这个类型的class对象,然后通过所有的装载器类一级一级地返回,直至第一个加载器类,成功装载类型的装载器是类型的定义类装载器,所有返回Class对象的装载器是类型的初始化类装载器

          用户自定义类装载器可以自行装载这个类型,或者委派其他的类装载器完成这个工作,用户自定义的类装载器可以委派其他用户自定义的类装载器或者启动类装载器实现类型的装载,启动类装载器可能要求一个用户自定义的类装载器来装载这个类型。

          如果类装载器要把类的装载工作委派给另外一个用户自定义的类装载器,则需要调用该装载器的loadClass()方法,如果要把类的装载工作委派给一个启动类装载器,则需要调用java.lang.ClassLoader的findSystemClass()方法,直到一个类型装载器决定自己去装载类型,它就不会继续委派了。

         一个类型装载器可以自己装载一个类型,调用findClass()和defineClass()方法,传入类型的全限定名,findClass()的作用是找到类型的class表示形式,defineClass()方法的作用是解析这个class表示形式,并保存到内存运行时数据区方法区的数据结构中,defineClass()方法会返回类型的Class对象。

         一个类型也可以委托其他类型加载器对某个类型进行加载,当委托用户自定义的类型加载器时,调用它的loadClass()方法,提供要加载类型全限定名,当委托系统的类加载器时,调用java.lang.ClassLoader的findSystemClass()方法,提供类型的全限定名,返回代表类型的Class对象。

         使用用户自定义的类装载器装载一个类型时,调用装载器的loadClass()方法,loadClass()方法会找到一个字节数组,用java class文件格式描述该类型,loadClass()方法会调用defineClass()方法,调用defineClass()方法会解析class文件的二进制格式,变为方法区内部的数据结构,最后,defineCssla()方法会返回一个代表类型信息的java.lang.Class对象

         如果使用启动类装载器,java.lang.Class的findSystemClass()方法会返回代表类型的java.lang.Class对象。

         综上所述,采用用户自定义的类装载器时,会调用loadClass()方法,loadClass方法会找到一个java class表示的字节数组,调用defineClass()方法,将class文件的内容解析成为方法区的数据结构,同时返回class对象

         采用系统的启动类装载器,会调用findSystemClass()方法,返回代表类型的class对象

      

         当装载一个类型时,JVM会进行一系列的校验,判断类的超类型是否被加载,如果没有,先装载超类,一直到Object类为止。

   

    5. 方法区的表示

         5.1 直接引用

               对类型,类变量和类方法的直接引用是指向方法区的指针(类型,类变量,类方法)

               对实例变量和实例方法(实例变量和实例方法,实例变量直接保存在堆区中,为了能够快速调用实例方法,采用方法表的形式,方法表保存指向方法数据的指针,方法表和方法数据都保存在方法区中)的直接引用是相对于堆的起始位置的偏移量

 

    6. 使用用户自定义的类型装载器

         Class classObj = gcl.loadClass("edu.bupt.newClass");

         Object o = classObj.newInstance();

         Greet greet = (Greet)o;

         第一条语句加载edu.bupt.newClass类型到方法区中,返回代表该类型的Class对象,第二句通过newInstance()语句创建并且初始化该Class对象代表的类的实例

         loadClass()方法的工作过程;

            首先检查类型是否已经被加载过,如果已经被加载,则直接返回代表类型的Class对象,这个通过调用java.lang.ClassLoader的findLoadedClass()方法实现;

            如果类型尚未被加载,则委托父亲装载器进行装载,或者是其他用户自定义类型装载器的loadClass()方法,或者是调用系统加载器的findSystemClass()方法;

            如果不能够委托,则该类型加载器只能够自己加载这个类型,首先调用findClass()方法,找到java class类型的字节数组,如果生成成功,则会将这个数组传给defineClass()方法,defineClass()方法解析这个class文件,保存到内存运行数据区的方法区的数据结构中,如果加载成功,则返回代表类型的class对象

         6.1 loadClass()方法的工作过程

               a. 检查被请求调用的类型是否已经被装载过了,通过调用java.lang.ClassLoader的findLoadedClass()方法判断,如果已经被装载过了,直接返回代表该类型的Class对象,一个类型只会被装载一次,JVM会在每次装载类型之前进行判断,如果该类型已经被装载到方法区中,则不会再次进行装载

              b. 如果JVM发现类型尚未被装载,先委托给父亲装载器进行装载,则将类型的全限定名传递给findSystemClass()方法,如果findSystemClass()方法成功装载该类型,则会返回代表该类型的Class实例;

              c.如果不能够委托,则调用findClass()方法,该方法会试图寻找或者生成一个java class类型的字节数组,如果生成成功,findClass()方法会把这个字节数组传递给defineClass()方法,这个方法会解析class文件,保存到方法区的内部数据结构中,创建一个代表类型的Class实例并且返回

              d.如果都不行,则抛出异常并且返回

阅读更多
个人分类: Java
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭