Onelong

分享知识,与你一起进步......
RSS icon Home icon
  • Java类加载机制

    post by onelong / 2010-1-29 5:51 Friday [java]

    预先加载与依需求加载

    Java运行环境为了优化系统,提高程序的执行速度,JRE运行的开始会将Java运行所需要的基本类采用预先加载(pre-loading)的方法全部加载

    要内存当中,因为这些单元在Java程序运行的过程当中经常要使用的,主要包括JRErt.jar文件里面所有的.class文件。

    java.exe虚拟机开始运行以后,它会找到安装在机器上的 JRE 环境,然后把控制权交给JRE,JRE的类加载器会将lib目录下的rt.jar基础类别文件库加载进内存,这些文件是Java程序执行所必须的,所以系统在开始就将这些文件加载,避免以后的多次 IO 操作,从而提高程序执行效率。

    相对于预先加载,我们在程序中需要使用自己定义的类的时候就要使用依需求加载方法(load-on-demand),就是在Java程序需要用到的时候再加载,以减少内存的消耗,因为Java语言的设计初衷就是面向嵌入式领域的。

    那么JRE的依需求加载究竟是在什么时候把类加载进入内部的呢?

    我们在定义一个类实例的时候,比如TestClassA testClassA,这个时候 testClassA 的值为 null,也就是说还没有初始化,没有调用 TestClassA 的构造函数,只有当执行testClassA = new TestClassA()以后,JRE才正真把TestClassA加载进来。

     

    隐式加载和显示加载

     

    Java 的加载方式分为隐式加载(implicit)和显示加载(explicit)

    隐式加载就是我们在程序中用new关键字来定义一个实例变量,JRE在执行到new关键字的时候就会把对应的实例类加载进入内存。隐式加载的方

     

    法很常见,用的也很多,JRE系统在后台自动的帮助用户加载,减少了用户的工作量,也增加了系统的安全性和程序的可读性。

     

    相对于隐式加载的就是我们不经常用到的显示加载。所谓显示加载就是有程序员自己写程序把需要的类加载到内存当中:

    class TestClass{

             public void method(){

                     System.out.println("TestClass-method");

             }

    }

     

    public class CLTest {

    public static void main(String args[]) {

            try{

                   Class c = Class.forName("TestClass");

                   TestClass object = (TestClass)c.newInstance();

                   object.method();

            }catch(Exception e){

                   e.printStackTrace();

            }

    }

    }

     

    我们通过Class类的forName(String s)方法把自定义类TestClass加载进来,并通过newInstance()方法把实例初始化。事实上Class类还很多的功能。

     

    ClassforName() 方法还有另外一种形式:Class forName(String s,boolean flag,ClassLoader classloader),s表示需要加载类的名称,flag表示在调用该函数加载类的时候是否初始化静态区, classloader 表示加载该类所需的加载器。forName(String s)是默认通过ClassLoader.getCallerClassLoader() 调用类加载器的,但是该方法是私有方法,我们无法调用,如果我们想使用

     

    Class forName(String s, boolean flag, ClassLoader classloader) 来加载类的话,就必须要指定类加载器,可以通过如下的方式来实现:

    Test test = new Test();//Test 类为自定义的一个测试类;

    ClassLoader cl = test. getClass().getClassLoader(); // 获取 test 的类装载器;

    Class c = Class.forName("TestClass", true, cl);

    因为一个类要加载就必需要有加载器,这里我们是通过获取加载Test类的加载器cl当作加载TestClass的类加载器来实现加载的。

     

    自定义类加载机制

    之前我们都是调用系统的类加载器来实现加载的,其实我们是可以自己定义类加载器的。利用 Java 提供的 java.net.URLClassLoader 类就可

     

    以实现。下面我们看一段范例:

        try{

                   URL url = new URL("file:/d:/test/lib/");

                   URLClassLoader urlCL = new URLClassLoader(new URL[]{url});

                   Class c = urlCL.loadClass("TestClassA");

                   TestClassA object = (TestClassA)c.newInstance();

                   object.method();

            }catch(Exception e){

                   e.printStackTrace();

            }

     

    我们通过自定义的类加载器实现了TestClassA类的加载并调用method()方法。分析一下这个程序:首先定义URL指定类加载器从何处加载类, URL 可以指向网际网络上的任何位置,也可以指向我们计算机里的文件系统 ( 包含 JAR 文件 ) 。上述范例当中我们从file:/d:/test/lib/ 处寻找类;然后定义 URLClassLoader 来加载所需的类,最后即可使用该实例了。

     

    类加载器的阶层体系

    Java 的类加载器的工作原理:

    当执行 java ***.class 的时候, java.exe会帮助我们找到 JRE ,接着找到位于 JRE 内部的 jvm.dll ,这才是真正的 Java 虚拟机器 , 最后加载动态库,激活Java虚拟机器。虚拟机器激活以后,会先做一些初始化的动作,比如说读取系统参数等。一旦初始化动作完成之后,就会产生第一个类加载器―― Bootstrap Loader , Bootstrap Loader 是由 C++ 所撰写而成,这个 Bootstrap Loader 所做的初始工作中,除了一些基本的初始化动作之外,最重要的就是加载 Launcher.java 之中的 ExtClassLoader ,并设定其 Parent null ,代表其父加载器为 BootstrapLoader 。然后 Bootstrap Loader 再要求加载 Launcher.java 之中的 AppClassLoader ,并设定其 Parent 为之前产生的 ExtClassLoader 实体。这两个加载器都是以静态类的形式存在的。这里要请大家注意的是, Launcher$ExtClassLoader.class Launcher$AppClassLoader.class 都是由 Bootstrap Loader 所加载,所以 Parent 和由哪个类加载器加载没有关系。

     

    这三个加载器就构成我们的 Java 类加载体系。他们分别从以下的路径寻找程序所需要的类:

     

    BootstrapLoader sun.boot.class.path

    ExtClassLoader:      java.ext.dirs

    AppClassLoader:      java.class.path

    这三个系统参量可以通过 System.getProperty() 函数得到具体对应的路径。大家可以自己编程实现查看具体的路径。

    实现自己的类加载器

    Java代码

    import java.io.IOException;   

    import java.net.URL;   

    import java.net.URLClassLoader;   

    import java.lang.reflect.Method;   

    public class Test {   

        static {   

             System.out.println("\n\n.......... Test class\n\n");   

         }   

        public static void main(String[] args) throws Exception{   

             URL url = null;   

            try {

                  url = new URL("file:/d:/temp/");   

                 } catch (Exception e) {   

                     e.printStackTrace();   

                 }   

              URLClassLoader cl = new URLClassLoader(new URL[]{url});   

              Class clz = cl.loadClass("Name");   

             Method method = clz.getMethod("sayHello5");   

             method.invoke(clz.newInstance());   

         }   

    }  

    使用了反射的方法来调用自定义类加载器加载上面的类。

     

    Java代码

    public class Name   

    {   

        public Name(){   

             System.out.println("Name Class");   

         }   

        static {   

             System.out.println("This is a static area");   

         }   

        public void sayHello(){   

             System.out.println("Name Class Say Hello");   

         }   

      

    } 

     

    标签: java 基础
    引用地址:
     

    我要评论