`
369128396
  • 浏览: 83842 次
  • 性别: Icon_minigender_2
  • 来自: 北京
社区版块
存档分类
最新评论

理解java动态加载机制

    博客分类:
  • java
 
阅读更多
1.java动态性
java是一种动态解释性语言,程序运行时jvm将预先编译好的class文件按规则和需求加载到内存中,每个java文件都对应一个class文件,这些文件对于java运行环境都是一个个运行单元,当文件需要更新时,无需重新编译其他java文件,只需要将重新编译的class文件放到java路径当中,新功能就会更新。
2.动态加载机制
1)预先加载和按需加载
2)显示加载和隐式加载
3)类加载器层次体系
4)类加载器规则

Java 语言是一种具有动态性的解释型编程语言,当指定程序运行的时候, Java 虚拟机就将编译生成的 . class 文件按照需求和一定的规则加载进内存,并组织成为一个完整的 Java 应用程序。 Java 语言把每个单独的类 Class 和接口 Implements 编译成单独的一个 . class 文件,这些文件对于 Java 运行环境来说就是一个个可以动态加载的单元。正是因为 Java 的这种特性,我们可以在不重新编译其它代码的情况下,只编译需要修改的单元,并把修改文件编译后的 . class 文件放到 Java 的路径当中, 等到下次该 Java 虚拟机器重新激活时,这个逻辑上的 Java 应用程序就会因为加载了新修改的 .class 文件,自己的功能也做了更新,这就是 Java 的动态性。
下面用一个简单的例子让大家对 Java 的动态加载有一个基本的认识:
class TestClassA{
public void method(){
        System.out.println("Loading ClassA");
}
}
public class ClassLoaderTest {
public static void main(String args[]){
        TestClassA testClassA = new TestClassA();
        testClassA.method();
}
}
编译后输入命令: java -verbose:class ClassLoaderTest ,执行文件。
输出结构如图 (1)
图( 1 )
从运行结果我们可以看到, JRE ( JavaRuntime Environment )首先加载 ClassLoaderTest 文件,然后再加载 TestClassA 文件,从而实现了动态加载。

1. 预先加载与依需求加载
Java 运行环境为了优化系统,提高程序的执行速度,在 JRE 运行的开始会将 Java 运行所需要的基本类采用预先加载( pre-loading )的方法全部加载要内存当中,因为这些单元在 Java 程序运行的过程当中经常要使用的,主要包括 JRE 的 rt.jar 文件里面所有的 .class 文件。
当 java.exe 虚拟机开始运行以后,它会找到安装在机器上的 JRE 环境,然后把控制权交给 JRE , JRE 的类加载器会将 lib 目录下的 rt.jar 基础类别文件库加载进内存,这些文件是 Java 程序执行所必须的,所以系统在开始就将这些文件加载,避免以后的多次 IO 操作,从而提高程序执行效率。
图( 2 )我们可以看到多个基础类被加载, java.lang.Object,java.io.Serializable 等等。
图( 2 )
相对于预先加载,我们在程序中需要使用自己定义的类的时候就要使用依需求加载方法( load-on-demand ),就是在 Java 程序需要用到的时候再加载,以减少内存的消耗,因为 Java 语言的设计初衷就是面向嵌入式领域的。
在这里还有一点需要说明的是, JRE 的依需求加载究竟是在什么时候把类加载进入内部的呢?
我们在定义一个类实例的时候,比如 TestClassA testClassA ,这个时候 testClassA 的值为 null ,也就是说还没有初始化,没有调用 TestClassA 的构造函数,只有当执行 testClassA = new TestClassA() 以后, JRE 才正真把 TestClassA 加载进来。

2. 隐式加载和显示加载
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 类还很多的功能,这里就不细讲了,有兴趣的可以参考 JDK 文档。
Class 的 forName() 方法还有另外一种形式: 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 的类加载器来实现加载的。

3. 自定义类加载机制
之前我们都是调用系统的类加载器来实现加载的,其实我们是可以自己定义类加载器的。利用 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 来加载所需的类,最后即可使用该实例了。

4. 类加载器的阶层体系
讨论了这么多以后,接下来我们仔细研究一下 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 和由哪个类加载器加载没有关系。
下面的图形可以表示三者之间的关系:
父类
父类
载入
载入
BootstrapLoader
        PARENT
AppClassLoader
PARENT
ExtClassLoader
这三个加载器就构成我们的 Java 类加载体系。他们分别从以下的路径寻找程序所需要的类:
BootstrapLoader : sun.boot.class.path
ExtClassLoader:      java.ext.dirs
AppClassLoader:      java.class.path
这三个系统参量可以通过 System.getProperty() 函数得到具体对应的路径。大家可以自己编程实现查看具体的路径。

5. 总结
了解 Java 的类加载机制对我们熟练灵活运用 Java 语言,提高程序的运行效率有着非常重要的作用,知其然也要知其所以然,这样才能从整体提高程序的质量。


以上是个人为了毕业要发表的一篇论文,没有什么深度,下面再继续讨论一点关于ClassLoader的一定东东:

public class ClassLoaderTest1{
private ClassLoaderTest2 test = null;
public ClassLoaderTest1(){
  test = new ClassLoaderTest2();
}
public void method(){
  System.out.println("Loading ClassA");
}
}


class ClassLoaderTest2{
public ClassLoaderTest2(){
 
}
public void method(){
  System.out.println("Loading ClassA");
}
}

测试程序:
URL url = null;
try {
   url = new URL("file:/E:/JAVA/MyProject/string/");
  } catch (MalformedURLException e) {
   e.printStackTrace();
  }
  URLClassLoader cl = new URLClassLoader(new URL[]{url});
  URLClassLoader cl1 = new URLClassLoader(new URL[]{url});
        try {
   Class tempClass = cl.loadClass("ClassLoaderTest1");
   Class tempClass2 = cl.loadClass("ClassLoaderTest2");
   Object test = tempClass.newInstance();
   System.out.println(tempClass.getClassLoader());
   System.out.println(tempClass2.getClassLoader());
  } catch (Exception e) {
   e.printStackTrace();
  }

当ClassLoaderTest1,ClassLoaderTest2在当前目录和E:/JAVA/MyProject/string/都存在的时候输出为sun.misc.Launcher$AppClassLoader@1050169
  sun.misc.Launcher$AppClassLoader@1050169
即都是被AppClassLoader加载的, 即使在E:/JAVA/MyProject/string/下面也存在.

当ClassLoaderTest1,ClassLoaderTest2只在E:/JAVA/MyProject/string/下存在的时候输出为
java.net.URLClassLoader@480457
java.net.URLClassLoader@1a7bf11
即都是被自定义的加载器加载的,并且也可以Object test = tempClass.newInstance();

下面一的是最关键的,因为ClassLoaderTest1需要用到ClassLoaderTest2,如果ClassLoaderTest2被AppClassLoader加载,而ClassLoaderTest1是被自定义的类加载器加载,就会出现如下错误:

java.lang.IllegalAccessError: tried to access class ClassLoaderTest2 from class ClassLoaderTest1
at ClassLoaderTest1.<init>(ClassLoaderTest1.java:6)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:274)
at java.lang.Class.newInstance0(Class.java:308)
at java.lang.Class.newInstance(Class.java:261)
at ClassLoaderTest.main(ClassLoaderTest.java:43)


所以JVM在寻找类的时候,不仅仅是根据类的名称,而是根据类明和类的加载器一起来决定的!
分享到:
评论

相关推荐

    Java 动态加载jar文件示例

    首先,我们需要理解Java的类加载机制。Java中的类是由类加载器(ClassLoader)负责加载的。默认情况下,Java虚拟机(JVM)提供了三个内置的类加载器:启动类加载器(Bootstrap ClassLoader)、扩展类加载器...

    深入理解java类加载机制

    总之,深入理解Java类加载机制对于Java程序员来说非常重要,它有助于提升代码质量,优化程序性能,解决运行时问题,并为高级技术如动态代理、插件化开发等提供基础。通过学习和实践,开发者可以更好地利用Java的动态...

    深入研究Java类加载机制 深入研究Java类加载机制

    Java类加载机制是Java程序运行的第一步,它对于理解Java虚拟机(JVM)的行为至关重要。类加载过程涉及到类的加载、链接(验证、准备、解析)、初始化等阶段,并且这一过程是由类加载器系统完成的。 #### 二、类加载...

    java 动态加载jar包

    Java的类加载机制采用双亲委派模型,即每个类加载器在尝试加载类时,都会将任务委托给其父类加载器,直到Bootstrap ClassLoader,如果父类加载器无法加载,则子类加载器才会尝试自己加载。我们可以自定义类加载器来...

    Java类动态加载(一)——java源文件动态编译为class文件

    首先,我们需要理解Java的类加载机制。Java的类加载是通过ClassLoader实现的,每个类在被使用之前都会由ClassLoader查找并加载。ClassLoader有三个基本职责: 1. 加载:寻找并加载类的字节码。 2. 验证:确保加载的...

    Java动态类加载机制应用研究.pdf

    在实际开发中,理解Java类加载机制是十分重要的。它可以帮助开发者更好地管理应用程序的类和资源,同时也可以利用动态类加载机制为应用程序带来更多的可能性。例如,在需要对程序进行热部署(Hot Deployment)时,...

    java类加载机制

    ### Java 类加载机制详解 Java 类加载机制是Java运行时环境的一个核心组成部分,它负责将编译后的Java字节码加载到JVM中,并确保Java应用程序能够正确地...理解类加载机制对于开发高效稳定的Java应用程序至关重要。

    深入研究Java的类加载机制.pdf

    深入研究Java的类加载机制 Java类加载机制是Java虚拟机的一项核心技术,可以在运行时刻动态地加载或替换系统的某些功能模块,而不...理解Java类加载机制可以帮助开发者更好地理解Java虚拟机,并更好地开发Java程序。

    JAVA动态加载JAR zip包

    首先,我们要理解Java类加载器的工作原理。在Java中,类加载器(ClassLoader)是负责加载类到JVM中的核心组件。默认情况下,系统类加载器会搜索`CLASSPATH`环境变量或 `-cp`/`-classpath`命令行选项指定的路径下的`....

    Java类加载机制

    Java 类加载机制是 Java 开发的基础之一,理解和掌握类加载机制有助于开发者解决诸如 `ClassNotFoundException`、`ClassCastException` 等常见问题。此外,通过自定义类加载器,还可以实现更灵活的类加载策略,满足...

    JAVA 类加载机制分析

    JAVA 类加载机制是Java平台核心特性之一,它关乎到程序的运行时环境和代码的动态加载。理解这一机制有助于开发者解决与对象创建、配置问题、应用程序发布等相关的问题。以下是关于JAVA 类加载机制的详细分析: 首先...

    动态加载类机制JAVA

    - Java的类加载机制遵循双亲委派模型,即当一个类加载器接收到加载类的请求时,它首先会将请求委托给父类加载器。只有当父类加载器无法找到对应的类时,子类加载器才会尝试加载。这种设计有助于避免类的重复加载,...

    Java类加载机制.doc

    总的来说,理解Java类加载机制对于开发高效、可扩展的Java应用至关重要。它涉及到类的查找、加载和初始化过程,以及类加载器之间的协作。通过掌握这些知识,开发者可以更好地处理类冲突、优化资源加载,甚至实现动态...

    java类加载机制原理与实现

    Java 类加载机制原理与实现 Java 类加载机制是 Java 虚拟机(JVM)的一部分,负责将编译后的 Java 字节码文件加载到 JVM 中,以便...理解类加载机制可以帮助我们更好地编写 Java 程序,避免一些常见的错误和安全问题。

    译 Java类加载机制(二)

    总的来说,Java类加载机制是Java程序运行的基础,理解其原理和机制对于优化性能、排查问题以及进行高级应用开发都至关重要。通过阅读“译 Java类加载机制(一、二)”的文档,可以更深入地掌握这些知识,并应用于...

    Java动态类加载机制应用研究.zip

    Java动态类加载机制是Java平台一个非常重要的特性,它允许程序在运行时动态地加载类,增强了软件的灵活性和可扩展性。动态类加载对于理解和优化Java应用程序的性能、实现插件系统、以及处理复杂的模块化系统至关重要...

    Java技术----实现JAVA的动态类载入机制

    在Java编程语言中,动态类加载机制是一种强大的特性,它允许程序在运行时加载、实例化和执行未在编译时硬编码的类。这种能力是通过Java的反射API实现的,它为开发者提供了深入洞察和操作Java对象的能力。本文将深入...

    springboot+java类热加载

    综上所述,"springboot+java类热加载"是一个涉及SpringBoot框架、Java类加载机制、动态编译以及依赖管理的综合技术话题。通过理解这些概念和相关工具,开发者可以更高效地进行迭代开发,提高生产力。在实际应用中,...

Global site tag (gtag.js) - Google Analytics