http://blog.csdn.net/linyajun/article/details/3729121
来源:互联网 发布:linux 反向映射 编辑:程序博客网 时间:2024/06/03 17:08
下面用一个简单的例子让大家对 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 ,执行文件。
从运行结果我们可以看到, 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 操作,从而提高程序执行效率。
相对于预先加载,我们在程序中需要使用自己定义的类的时候就要使用依需求加载方法( load-on-demand ),就是在 Java 程序需要用到的时候再加载,以减少内存的消耗,因为 Java 语言的设计初衷就是面向嵌入式领域的。
在这里还有一点需要说明的是, JRE 的依需求加载究竟是在什么时候把类加载进入内部的呢?
我们在定义一个类实例的时候,比如 TestClassA testClassA ,这个时候 testClassA 的值为 null ,也就是说还没有初始化,没有调用 TestClassA 的构造函数,只有当执行 testClassA = new TestClassA() 以后, JRE 才正真把 TestClassA 加载进来。
1. JAVA类装载器在装载类的时候是按需加载的,只有当一个类要使用(使用new 关键字来实例化一个类)的时候,类加载器才会加载这 个类并初始化。
类Main:
- public class Main {
- public static void main(String[] args) {
- A a = new A();
- a.print();
- B b = new B();
- b.print();
- }
- }
类A:
- public class A {
- public void print() {
- System.out.println("Using Class A");
- }
- }
类B:
- public class B {
- public void print() {
- System.out.println("Using Class B");
- }
- }
执行:java -varbose:class Main
执行结果:
E:/DEV>java -verbose:class Main
[Opened C:/Program Files/Java/jre1.5.0_11/lib/rt.jar] (类装载器会首先加载rt.jar加载基础类)
.
.
[Loaded Main from file:/E:/DEV/] (类装载器载入相应类并初始化)
[Loaded A from file:/E:/DEV/]
Using Class A
[Loaded B from file:/E:/DEV/]
Using Class B
2. 让JAVA程序具有动态性
使用显式方式来实现动态性,我们需要自己动手处理类载入时的细节部分。
两种方法:
|
+-- 隐式的 : 使用new关键字让类加载器按需求载入所需的类
|
+-- 显式的 :
|
+-- 由 java.lang.Class的forName()方法加载
|
+-- 由 java.lang.ClassLoader的loadClass()方法加载
(1) 使用Class.forName()
Class.forName()方法具有两个重载的方法:
+- public static Class forName(String className)
|
+- public static Class forName(String className, boolean initialize,ClassLoader loader)
参数说明:
className - 所需类的完全限定名
initialize - 是否必须初始化类(静态代码块的初始化)
loader - 用于加载类的类加载器
调用只有一个参数的forName()方法等效于 Class.forName(className, true, loader)。
这两个方法,最后都要连接到原生方法forName0(),其定义如下:
private static native Class forName0(String name, boolean initialize,ClassLoader loader) throws ClassNotFoundException;
只有一个参数的forName()方法,最后调用的是:
forName0(className, true, ClassLoader.getCallerClassLoader());
而三个参数的forName(),最后调用的是:
forName0(name, initialize, loader);
所以,不管使用的是new 來实例化某个类、或是使用只有一个参数的Class.forName()方法,内部都隐含了“载入类 + 运行静态代码块”的步骤。而使用具有三个参数的Class.forName()方法时,如果第二个参数为false,那么类加载器只会加载类,而不会初始化静态代码块,只有当实例化这个类的时候,静态代码块才会被初始化,静态代码块是在类第一次实例化的时候才初始化的。
(2) 直接使用类加载器
+— 获得对象所属的类 : getClass()方法
|
+— 获得该类的类加载器 : getClassLoader()方法
- public class Main3 {
- public static void main(String[] args) throws Exception {
- Main3 main3 = new Main3();
- System.out.println("准备载入类");
- ClassLoader loader = main3.getClass().getClassLoader();
- Class clazzA = loader.loadClass(args[0]);
- System.out.println("实例化类A");
- A o1 = (A) clazzA.newInstance();
- }
- }
类加载层次
- http://blog.csdn.net/linyajun/article/details/3729121
- http://blog.csdn.net/IBM_hoojo/article/details/5688947
- http://blog.csdn.net/chenlaic/article/details/6143235
- http://blog.csdn.net/eaglewood2005/article/details/4335052
- http://blog.csdn.net/mchp/article/details/3995970
- http://blog.csdn.net/v_july_v/article/details/6015165
- http://blog.csdn.net/masterz/article/details/6232585
- http://blog.csdn.net/perfectpdl/article/details/6442847
- http://blog.csdn.net/eroswang/article/details/1967243
- http://blog.csdn.net/zhvsby/article/details/5986645
- http://blog.csdn.net/dz45693/article/details/6183645
- http://blog.csdn.net/david_lv/article/details/5798003
- http://blog.csdn.net/zxingchao2009/article/details/6299313
- http://blog.csdn.net/zhanxinhang/article/details/6783766
- http://blog.csdn.net/ruanruoshi/article/details/935510
- http://blog.csdn.net/yming0221/article/details/6538527
- http://blog.csdn.net/yming0221/article/details/6528490
- http://blog.csdn.net/yming0221/article/details/6704079
- 设置IP策略拒绝用户Ping服务器
- 别不把自己当有钱人——让白领族成为百万富翁族的六大理财秘籍
- 生活英语
- Eclipse 代码注释模版设置( Code Templates)
- 敏捷开发用户故事系列之十一:CSDN博客用户故事分析
- http://blog.csdn.net/linyajun/article/details/3729121
- 使用 JNDI 得到数据源
- C语言课程设计——学生学籍管理系统
- poj 3020 Antenna Placement(二分图匹配)
- 努力吧,现在也不晚
- Android应用程序Zipalign化 -- 如何让Android应用程序更有效率的执行
- 在wpf datagrid中,想要根据一个条件来改变datagrid行的背景颜色
- android 自定义日历控件(有图有真相)
- poj 1924 The Treasure (bfs+地图预处理)