学习笔记
三、深入类载入器(classLoader)
Java是一种天生就具有动态连结能力的技术。Java把每个类、接口编译之后,全部变成一个个小的执行单位(.class文件)。一旦指定一个具有public static void main(String[] arg)方法的类作为起点开始运行后,JVM会找出所有在执行时需要的执行单位,并将它们载入内存之中,彼此交互运行。尽管本质上是一堆类文件,但是在内存中变成了一个“逻辑上”为一体的Java应用程序。所以,严格来说,每个类文件对JVM都是一个独立的动态连结函数库,不过他的类型不是.dll或.so而是.class罢了。因为有这种特性,可以在不重新编译其他Java程序的情况下,只修改有问题的执行单位,并放入系统中等到下次该JVM重启时,这个逻辑上的java应用程序就会因为载入新修改的.class文件,自己的功能也做了更新。这是一个基本的动态性功能。
可是,这需要重启JVM,若要不重启JVM且读取新版本后释放旧版本内存就要用到类载入器(classLoader)。
Java.exe是利用几个基本原则寻找JRE后把类可执行文件(.class文件)直接转交给JRE执行后,java.exe就功成身退。类载入器(classLoader)会自动从所在的JRE目录下的\lib\rt.jar载入基类函数库。载入分为两种:预先载入(pre-loading)和依需求载入(load-on-demand)。一般基类函数库都是都是预先载入,而自己撰写的函数为依需求载入(只有声明没有实例,在依需求载入的时候是不会载入的)。依需求载入的优点是节省内存,缺点是当程序第一次用到该类的时候,系统必须花一些额外的时间来载入该类,使得整体的执行效率受到影响。
让Java程序具有动态性的两种方法:
1、一种是隐式的(implicit),另一类是显式的(explicit)。两种方法底层机制完全相同,只是所使用的代码有所不同。隐式的(implicit)方法:当程序员用到new关键字时,类载入器依需求载入所需要的类。但仍有限制,无法达成更多的弹性。显式的(explicit)又分为两种方式,一种由java.lang.Class里的forName()方法,另一种则由java.lang.ClassLoader里的loadClass()方法。
2、使用Class.forName()方法的显式方法达成动态性:
Office.java
Word.java
主程序Office.java编译后,以后只要调用:java Office Word 就可以动态载入需要的类。(当类载入器载入的类继承了其他的类,或是实现了其他的接口,就会先载入该实现的接口类,也会载入其父类,如果父类也有其父类,也会一并优先载入。换句话说,类载入器会依照继承体系最上层的类往下依次载入,直到所有的祖先类都载入了,才轮到自己载入。)
在JDK的Class类中其实有两个forName()方法,一个是一个参数的(就是之前程序中使用的):
另外一个是需要三个参数的:
Office.java
Word.java
运行:java Office Word 时的结果如下:
即使类被载入了,其静态初始化模块(static initialization block)也不会被调用,而是在第一次调用newInstance()方法时才被调用。
「静态初始化模块实在类第一次被实例化的时候才会被调用那仅仅一次」
得到以下结论:不管使用new来产生某类的实例或是使用只有一个参数的forName()方法,内部都隐含了“载入类+调用静态初始化模块”的动作。而是要具有三个参数的forName()方法时,如果第二个参数给定的是false,那么就会命令类载入器载入该类,但不会调用其静态初始化模块,只有等到整个程序第一次实例化某个类时,静态初始化模块才会被调用。
3、直接使用类载入器的显式方法达成动态性:
类是一个样板,而物件就是根据这个模板产生的实例。在java中,每个类最后的祖先都是Object,而Object里有一个getClass()的方法,用来获取某个特定实例所属类的参数,这个参数指向的是一个名为Class类(Class.class)的实例,这个实例在类文件(.class)第一次载入内存时创建的,以后程序中产生任何该类的实例,这些实例的内部都会有一个栏位记录着这个Class类的所在位置。每个Class类的实例,都可以当作某个类在内存中的代理人。
系统中可以同时存在多个ClassLoader的实例,而且一个类载入器(ClassLoader的实例)可以载入多个类。Java中的类都是由某个类载入器(ClassLoader的实例)来载入。所以只要取得Class实例的参数,就可以利用其getClassLoader()方法来取得载入该类的类载入器的参数。最后,取得了ClassLoder的实例,调用loadClass()方法载入需要的类。
Office1.java
Word1.java
运行:java Office1 Word1 时的结果如下:
4、自己创建类载入器来载入类:
利用Java本身提供的java.net.URLClassLoader类就可以做到:
Office.java
URL可以指定网络上的任何位置,也可以指定本身计算机上的文件系统(包含jar文件)。
5、类载入器的阶层体系:
流程图如下:
[img]http://skyKing.iteye.com/upload/picture/pic/19023/dbf8e2fe-b683-3b6e-842f-a9d0e186ea9c.jpg [/img]
其中Bootstrap Loader->ExtClassLoader->AppClassLoader,就是类载入器的阶层体系。
test.java
输出结果如下:
[img] http://skyKing.iteye.com/upload/picture/pic/19019/c7d93621-de11-3c48-9672-7b77ad44a8dc.jpg [/img]
AppClassLoader 和ExtClassLoader 都是URLClassLoader的子类。
AppClassLoader 的URL参数 是由从系统参数java.class.path 取出的字串所决定,而java.class.path 则是执行java.exe 时,利用 –cp 或-classpath 或CLASSPATH 环境变量所决定。
在预设情況下,AppClassLoader 的搜寻路径为”.”(当前所在目录),如果使用-classpath 选项(与-cp 等效),就可以改变AppClassLoader 的搜寻路径,如果沒有指定-classpath 选项,就搜寻环境变量CLASSPATH。如果同时有CLASSPATH 的环境设定与-classpath 选项,则以-classpath 选项的内容为主,CLASSPATH 的环境设定与-classpath 选项两者的內容不会有加成的效果。
ExtClassLoader 也有相同的情形,不过其搜寻路径是参考系统参数java.ext.dirs。
系統参数java.ext.dirs 的內容,会指向java.exe 所选择的JRE 所在位置下的\lib\ext 子目录。
Bootstrap Loader,搜寻路径是参考系统参数sun.boot.class.path ,测试代码如下:
Test3.java
输出结果如下:
[img]http://skyKing.iteye.com/upload/picture/pic/19017/c4a6432a-c6b0-32c1-aee9-1af72d61a2b5.jpg [/img]
总述:路径java.class.path 与sun.boot.class.path,也就是AppClassLoader 与Bootstrap Loader 会搜寻的位置(或JAR 文件),如果找不到就找不到了,AppClassLoader 与Bootstrap Loader 不会递归搜寻这些位置下的 路径或其他没有被指定的JRE文件。ExtClassLoader参考的系统参数是java.ext.dirs,他会搜寻底下的所有JAR 文件以及classes 目录,作為其搜寻路径。
AppClassLoader 与ExtClassLoader 是各自参考系统参数sun.boot.class.path与java.ext.dirs容而建立,在命令行下更改这两个系统参数, AppClassLoader 与ExtClassLoader 在建立实例的时候会参考这两个系统参数,因而会改变他们搜寻类文件的路径;而系統参数sun.boot.class.path 则是预设与Bootstrap Loader 的搜寻路径相同,就算更改该系统参数,也与Bootstrap Loader 完全无关。
三、深入类载入器(classLoader)
Java是一种天生就具有动态连结能力的技术。Java把每个类、接口编译之后,全部变成一个个小的执行单位(.class文件)。一旦指定一个具有public static void main(String[] arg)方法的类作为起点开始运行后,JVM会找出所有在执行时需要的执行单位,并将它们载入内存之中,彼此交互运行。尽管本质上是一堆类文件,但是在内存中变成了一个“逻辑上”为一体的Java应用程序。所以,严格来说,每个类文件对JVM都是一个独立的动态连结函数库,不过他的类型不是.dll或.so而是.class罢了。因为有这种特性,可以在不重新编译其他Java程序的情况下,只修改有问题的执行单位,并放入系统中等到下次该JVM重启时,这个逻辑上的java应用程序就会因为载入新修改的.class文件,自己的功能也做了更新。这是一个基本的动态性功能。
可是,这需要重启JVM,若要不重启JVM且读取新版本后释放旧版本内存就要用到类载入器(classLoader)。
Java.exe是利用几个基本原则寻找JRE后把类可执行文件(.class文件)直接转交给JRE执行后,java.exe就功成身退。类载入器(classLoader)会自动从所在的JRE目录下的\lib\rt.jar载入基类函数库。载入分为两种:预先载入(pre-loading)和依需求载入(load-on-demand)。一般基类函数库都是都是预先载入,而自己撰写的函数为依需求载入(只有声明没有实例,在依需求载入的时候是不会载入的)。依需求载入的优点是节省内存,缺点是当程序第一次用到该类的时候,系统必须花一些额外的时间来载入该类,使得整体的执行效率受到影响。
让Java程序具有动态性的两种方法:
1、一种是隐式的(implicit),另一类是显式的(explicit)。两种方法底层机制完全相同,只是所使用的代码有所不同。隐式的(implicit)方法:当程序员用到new关键字时,类载入器依需求载入所需要的类。但仍有限制,无法达成更多的弹性。显式的(explicit)又分为两种方式,一种由java.lang.Class里的forName()方法,另一种则由java.lang.ClassLoader里的loadClass()方法。
2、使用Class.forName()方法的显式方法达成动态性:
Office.java
public class Office { public static void main(String args[]) throws Exception { Class c = Class.forName(args[0]) ; Object o = c.newInstance() ; Assembly a = (Assembly) o ; a.start() ; } }
Word.java
public class Word implements Assembly { public void start() { System.out.println("Word starts") ; } }
主程序Office.java编译后,以后只要调用:java Office Word 就可以动态载入需要的类。(当类载入器载入的类继承了其他的类,或是实现了其他的接口,就会先载入该实现的接口类,也会载入其父类,如果父类也有其父类,也会一并优先载入。换句话说,类载入器会依照继承体系最上层的类往下依次载入,直到所有的祖先类都载入了,才轮到自己载入。)
在JDK的Class类中其实有两个forName()方法,一个是一个参数的(就是之前程序中使用的):
public static Class forName(String className)
另外一个是需要三个参数的:
public static Class forName(String name, boolean initialize,ClassLoader loader)这两个方法,最后都是连接到原生方法forName0(),其声明如下:
private static native Class forName0(String name, boolean initialize,ClassLoader loader) throws ClassNotFoundException;只有一个参数的forName()方法,最后调用的是:
forName0(className, true,lassLoader.getCallerClassLoader());而具有三个参数的forName()方法,最后调用的是:
forName0(name, initialize, loader);其中initialize参数使用true 和false 会造成不同效果。为false时,测试如下:
Office.java
public class Office{ public static void main(String args[]) throws Exception{ Office off = new Office(); System.out.println("类准备输入"); Class c = Class.forName(args[0], false,off.getClass().getClassLoader()); System.out.println("类准备实例化"); Object o = c.newInstance(); Object o2 = c.newInstance(); } }
Word.java
public class Word implements Assembly{ static{ System.out.println("Word static initialization"); } public void start(){ System.out.println("Word starts"); } }
运行:java Office Word 时的结果如下:
即使类被载入了,其静态初始化模块(static initialization block)也不会被调用,而是在第一次调用newInstance()方法时才被调用。
「静态初始化模块实在类第一次被实例化的时候才会被调用那仅仅一次」
得到以下结论:不管使用new来产生某类的实例或是使用只有一个参数的forName()方法,内部都隐含了“载入类+调用静态初始化模块”的动作。而是要具有三个参数的forName()方法时,如果第二个参数给定的是false,那么就会命令类载入器载入该类,但不会调用其静态初始化模块,只有等到整个程序第一次实例化某个类时,静态初始化模块才会被调用。
3、直接使用类载入器的显式方法达成动态性:
类是一个样板,而物件就是根据这个模板产生的实例。在java中,每个类最后的祖先都是Object,而Object里有一个getClass()的方法,用来获取某个特定实例所属类的参数,这个参数指向的是一个名为Class类(Class.class)的实例,这个实例在类文件(.class)第一次载入内存时创建的,以后程序中产生任何该类的实例,这些实例的内部都会有一个栏位记录着这个Class类的所在位置。每个Class类的实例,都可以当作某个类在内存中的代理人。
系统中可以同时存在多个ClassLoader的实例,而且一个类载入器(ClassLoader的实例)可以载入多个类。Java中的类都是由某个类载入器(ClassLoader的实例)来载入。所以只要取得Class实例的参数,就可以利用其getClassLoader()方法来取得载入该类的类载入器的参数。最后,取得了ClassLoder的实例,调用loadClass()方法载入需要的类。
Office1.java
public class Office1{ public static void main(String args[]) throws Exception{ Office1 off = new Office1(); System.out.println("类准备输入"); Class c = Class.forName(args[0], false,off.getClass().getClassLoader()); System.out.println("类准备实例化"); Object o = c.newInstance(); Object o2 = c.newInstance(); } }
Word1.java
public class1 Word implements Assembly { public void start() { System.out.println("Word starts") ; } }
运行:java Office1 Word1 时的结果如下:
4、自己创建类载入器来载入类:
利用Java本身提供的java.net.URLClassLoader类就可以做到:
Office.java
import java.net.* ; public class Office{ public static void main(String args[]) throws Exception{ URL u = new URL("file:/d:/my/lib/"); URLClassLoader ucl = new URLClassLoader(new URL[]{ u }); Class c = ucl.loadClass(args[0]); Assembly asm = (Assembly) c.newInstance(); asm.start(); } }
URL可以指定网络上的任何位置,也可以指定本身计算机上的文件系统(包含jar文件)。
5、类载入器的阶层体系:
流程图如下:
[img]http://skyKing.iteye.com/upload/picture/pic/19023/dbf8e2fe-b683-3b6e-842f-a9d0e186ea9c.jpg [/img]
其中Bootstrap Loader->ExtClassLoader->AppClassLoader,就是类载入器的阶层体系。
test.java
public class test{ public static void main(String args[]){ ClassLoader cl = test.class.getClassLoader() ; System.out.println(cl) ; ClassLoader cl1 = cl.getParent() ; System.out.println(cl1) ; ClassLoader cl2 = cl1.getParent() ; System.out.println(cl2) ; } }
输出结果如下:
[img] http://skyKing.iteye.com/upload/picture/pic/19019/c7d93621-de11-3c48-9672-7b77ad44a8dc.jpg [/img]
AppClassLoader 和ExtClassLoader 都是URLClassLoader的子类。
AppClassLoader 的URL参数 是由从系统参数java.class.path 取出的字串所决定,而java.class.path 则是执行java.exe 时,利用 –cp 或-classpath 或CLASSPATH 环境变量所决定。
在预设情況下,AppClassLoader 的搜寻路径为”.”(当前所在目录),如果使用-classpath 选项(与-cp 等效),就可以改变AppClassLoader 的搜寻路径,如果沒有指定-classpath 选项,就搜寻环境变量CLASSPATH。如果同时有CLASSPATH 的环境设定与-classpath 选项,则以-classpath 选项的内容为主,CLASSPATH 的环境设定与-classpath 选项两者的內容不会有加成的效果。
ExtClassLoader 也有相同的情形,不过其搜寻路径是参考系统参数java.ext.dirs。
系統参数java.ext.dirs 的內容,会指向java.exe 所选择的JRE 所在位置下的\lib\ext 子目录。
Bootstrap Loader,搜寻路径是参考系统参数sun.boot.class.path ,测试代码如下:
Test3.java
public class Test3 { public static void main(String[] args) { String s = System.getProperty("sun.boot.class.path"); System.out.println(s); } }
输出结果如下:
[img]http://skyKing.iteye.com/upload/picture/pic/19017/c4a6432a-c6b0-32c1-aee9-1af72d61a2b5.jpg [/img]
总述:路径java.class.path 与sun.boot.class.path,也就是AppClassLoader 与Bootstrap Loader 会搜寻的位置(或JAR 文件),如果找不到就找不到了,AppClassLoader 与Bootstrap Loader 不会递归搜寻这些位置下的 路径或其他没有被指定的JRE文件。ExtClassLoader参考的系统参数是java.ext.dirs,他会搜寻底下的所有JAR 文件以及classes 目录,作為其搜寻路径。
AppClassLoader 与ExtClassLoader 是各自参考系统参数sun.boot.class.path与java.ext.dirs容而建立,在命令行下更改这两个系统参数, AppClassLoader 与ExtClassLoader 在建立实例的时候会参考这两个系统参数,因而会改变他们搜寻类文件的路径;而系統参数sun.boot.class.path 则是预设与Bootstrap Loader 的搜寻路径相同,就算更改该系统参数,也与Bootstrap Loader 完全无关。
相关推荐
这是本文精心搜集的关于java方面的深入学习的资料合集,包括 how tomcat works中文版367页pdf j2ee教程2010ppt java并发编程培训(阿里巴巴)ppt java反射机制总结pdf java数据结构上机实践指导教程pdf java网络编程...
"Java深入学习就靠他了"这个资源显然旨在帮助有经验的Java开发者深化对这门语言的理解,尤其是那些正处于技术突破阶段的程序员。它涵盖了Java的核心技术和高级特性,旨在提升开发者在J2SE(Java标准版)和J2EE(Java...
java泛型详细学习,深入学习java的不二之选
《深入学习:Java多线程编程》是一本专注于Java并发技术的专业书籍,旨在帮助开发者深入理解和熟练运用Java中的多线程编程。Java多线程是Java编程中的核心部分,尤其在现代高性能应用和分布式系统中不可或缺。理解并...
这个学习系列将深入探讨Java集合框架的几个关键组件,包括ArrayList、HashMap以及LinkedHashMap。以下是对这些主题的详细解释: 首先,我们来看ArrayList。ArrayList是Java集合框架中的一种线性数据结构,属于List...
这书着重点是讲java线程,但不局限于java,对posix线程库,win32线程库都有涉及,并对这三者做了一些比较,对于想深入了解线程的人,这书是绝对有帮助的。
"java深入学习笔记.pdf" java是一种广泛应用的编程语言,具有平台独立性、对象oriented、分布式处理等特点。在java深入学习笔记.pdf中,我们可以学习到以下知识点: 一、java基础知识 * 变量声明:在java中,变量...
《深入分析Java ++Web技术内幕 修订完全版》是一本专为Java Web开发人员精心编写的权威指南。这本书详尽地探讨了Java在Web领域的应用和技术内幕,旨在帮助读者掌握核心概念并提升实际开发能力。作为一本必备的参考...
面向对象编程(OOP)是Java的一个核心特点,因此深入学习OOP的相关概念是必不可少的。这部分包括但不限于: - **继承**:了解子类如何继承父类的属性和行为。 - **构造器**:掌握如何创建对象及其初始化过程。 - **...
### 学习Java必看的书籍 在Java学习过程中,选择合适的书籍对于深入理解这门语言至关重要。根据提供的部分信息,我们将重点介绍三本被广泛推荐的经典Java书籍:《Thinking in Java》、《Java Collections》以及...
《深入体验Java_Web开发内幕—核心基础》是一本针对Java Web开发的深度解析书籍,旨在帮助读者全面理解和掌握Java Web开发的核心技术。这本书涵盖了从基础到高级的多个主题,旨在提供一个完整的Java Web开发学习路径...
《深入Java虚拟机》这本书是Java开发者深入了解JVM(Java Virtual Machine)的必备经典之作。...通过深入学习,开发者不仅可以理解Java程序的运行机制,还能更好地优化代码,提高系统的稳定性和性能。
资源名称:深入学习 JFC Swing:Java 基础类组件集资源截图: 资源太大,传百度网盘了,链接在附件中,有需要的同学自取。
《深入浅出Java》这本书以其独特的讲解方式,旨在让学习者轻松掌握复杂的Java编程...通过学习,你将能够编写出高效、可靠的Java应用程序,并且为未来深入学习Java EE、Android开发或其他Java相关的技术打下坚实基础。
三次样条插值是一种在离散数据点之间构造平滑曲线的方法,广泛应用于数值分析、图形学、数据拟合等领域。在Java编程中实现三次样条插值,可以帮助我们处理和...通过深入学习和实践,你可以在处理离散数据时游刃有余。
深入JAVA虚拟机,帮助大家在Java方面进行学习,深入JAVA虚拟机,帮助大家在Java方面进行学习,
深入学习抽象类、接口、内部类、匿名类的使用,以及访问控制修饰符(public、private、protected和默认)的含义。 2. **异常处理**:熟悉Java的异常体系,掌握try-catch-finally语句块,了解Checked和Unchecked异常...
在深入体验Java项目开发的源代码中,我们可以找到一系列关于Java编程和项目实施的实践示例。这本书的目的是为了帮助读者不仅仅是理解Java语言的基础,更深入地了解如何将这些知识应用到实际项目中。源代码提供了丰富...