信息发布软件,b2b软件,广告发布软件

 找回密码
 立即注册
搜索

Java类别载入器和实例多教程用法

查看数: 1832 | 评论数: 2 | 收藏 0
关灯 | 提示:支持键盘翻页<-左 右->
    组图打开中,请稍候......
发布时间: 2016-9-25 11:17

正文摘要:

 1 Java的动态特性Java的动态特性有两种,一是隐式的;另一种是显示的。隐式的(implicit)方法就是当程式设计师用到new 这个Java 关键字时,会让类别载入器依需求载入您所需要的类别,这种方式使用了隐式的(implicit ...

回复

软件定制 发表于 2016-9-25 11:19:00
多实例教程3

Java在需要使用类别的时候,才会将类别加载,Java的类别载入是由类别载入器(Class loader)来达到的,预设上,在程序启动之后,主要会有三个类别加载器:Bootstrap Loader、ExtClassLoader与AppClassLoader。

Bootstrap Loader是由C++撰写而成,预设上它负责搜寻JRE所在目录的classes或lib目录下的.jar档案中(例如rt.jar)是否有指定的类别并加载(实际上是由系统参数sun.boot.class.path指定);预设上ExtClassLoader负责搜寻JRE所在目录的lib/ext 目录下的classes或.jar中是否有指定的类别并加载(实际上是由系统参数java.ext.dirs指定);AppClassLoader则搜寻 Classpath中是否有指定的classes并加载(由系统参数java.class.path指定)。

Bootstrap Loader会在JVM启动之后载入,之后它会载入ExtClassLoader并将ExtClassLoader的parent设为Bootstrap Loader,然后BootstrapLoader再加载AppClassLoader,并将AppClassLoader的parent设定为 ExtClassLoader。

在加载类别时,每个类别加载器会先将加载类别的任务交由其parent,如果parent找不到,才由自己负责加载,如果自己也找不到,就会丢出 NoClassDefFoundError。

每一个类别被载入后,都会有一个Class的实例来代表它,每个Class的实例都会记得是哪个ClassLoader加载它的,可以由Class的getClassLoader()取得加载该类别的ClassLoader。
软件定制 发表于 2016-9-25 11:18:08
多方法实例教程2


1.java执行


java.exe 是利用几个基本原则来寻找Java


Runtime Environment(JRE),然后把类别档(.class)直接转交给JRE 执行之后,java.exe   就功成身退。类别加载器也是构成JRE 的其中一个重要成员,所以最后类别加载器就会自动从所在之JRE 目录底下的librt.jar 载入基础类别函式库。所以在上图里,一定是因为java.exe 定位到c:j2sdk1.4.0jre,所以才会有此输出结果。


2.预先加载与依需求加载


像基础类别函式库这样的加载方法我们叫做预先加载(pre-loading),这是因为基础类别函式库里头的类别大多是Java 程序执行时所必备的类别,所以为了不要老是做浪费时间的I/O 动作(读取档案系统,然后将类别文件加载内存之中),预先加载这些类别会让Java 应用程序在执行时速度稍微快一些。相对来说,我们自己所撰写的类别之加载方式,叫做依需求加载(load-on-demand),也就是Java 程序真正用到该类别的时候,才真的把类别文件从档案系统之中加载内存



3.Java 提供两种方法来达成动态性


一种是隐式的(implicit),另一种是显式的(explicit)。


隐式的(implicit)方法我们已经谈过了,也就是当程序设计师用到new 这个Java 关键词时,会让类别加载器依需求加载您所需要的类别,这种方式使用了隐式的(implicit)方法。显式的方法,又分成两种方式,一种是藉由java.lang.Class 里的forName()方法,另一种则是藉由java.lang.ClassLoader 里的loadClass()方法。



4.forName方法


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, ClassLoader.getCallerClassLoader());而具有三个参数的forName()方法,最后叫用的是:forName0(name, initialize, loader);initialized的用法:true,表示载入实例的同时也载入静态初始化区块;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() ;


}


}


则输出变成:




5.用显式的方法来达成动态性:直接使用类别加载器



这种情形与使用Class 类别的forName()方法时,第二个参数传入false


几乎是相同的结果。


档案:Office.java


public class Office


{


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


{


Office off = new Office() ;


System.out.println("类别准备载入") ;


ClassLoader loader = off.getClass().getClassLoader() ;


Class c = loader.loadClass(args[0]) ;


System.out.println("类别准备实体化") ;


Object o = c.newInstance() ;


Object o2 = c.newInstance() ;


}


}


执行


java Office Word



6.自己建立类别加载器来加载类别


档案: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() ;


}


}


7.类别被哪个类别载入器载入


我们将上述的程序代码稍作修改,修改后的程序代码如下:


档案: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 u1 = new URL("file:/d:/my/lib/") ;


URLClassLoader ucl1 = new URLClassLoader(new URL[]{ u1 }) ;


Class c1 = ucl1.loadClass(args[0]) ;


Assembly asm1 = (Assembly) c1.newInstance() ;


asm1.start() ;


System.out.println(Office.class.getClassLoader()) ;


System.out.println(u.getClass().getClassLoader()) ;


System.out.println(ucl.getClass().getClassLoader()) ;


System.out.println(c.getClassLoader()) ;


System.out.println(asm.getClass().getClassLoader()) ;


System.out.println(u1.getClass().getClassLoader()) ;


System.out.println(ucl1.getClass().getClassLoader()) ;


System.out.println(c1.getClassLoader()) ;


System.out.println(asm1.getClass().getClassLoader()) ;


}


}


执行后输出结果如下图:



从输出中我们可以得知,Office.class 由AppClassLoader(又称做System Loader,系统加载器)所加载,URL.class 与URLClassLoader.class 由Bootstrap Loader 所加载(注意:输出null 并非代表不是由类别载入器所载入。在Java 之中,所有的类别都必须由类别加载器加载才行,只不过Bootstrap Loader 并非由Java 所撰写而成,而是由C++实作而成,因此以Java 的观点来看,逻辑上并没有Bootstrap Loader 的类别实体)。而Word.class 分别由两个不同的URLClassLoader 实体加载。至于Assembly.class,本身应该是由AppClassLoader 加载,但是由于多型(Polymorphism)的关系,所指向的类别实体(Word.class)由特定的加载器所加载,导致打印在屏幕上的内容是其所参考的类别实体之类别加载器。Interface 这种型态本身无法直接使用new 来产生实体,所以在执行getClassLoader()的时候,叫用的一定是所参考的类别实体的getClassLoader(),要知道Interface 本身由哪个类别加载器加载,您必须使用底下程序代码:Assembly.class.getClassLoader()



8一切都是由Bootstrap Loader 开始 : 类别载入器的阶层体系


当我们在命令列输入java xxx.class 的时候,java.exe 根据我们之前所提过的逻辑找到了


JRE(Java Runtime Environment),接着找到位在JRE 之中的jvm.dll(真正的Java 虚拟机器),最后加载这个动态联结函式库,启动Java 虚拟机器。这个动作的详细介绍请回头参阅第一章。虚拟机器一启动,会先做一些初始化的动作,比方说抓取系统参数等。一旦初始化动作完成之后,就会产生第一个类别载入器,即所谓的Bootstrap Loader,Bootstrap Loader 是由C++所撰写而成(所以前面我们说,以Java 的观点来看,逻辑上并不存在Bootstrap Loader 的类别实体,所以在Java 程序代码里试图印出其内容的时候,我们会看到的输出为null),这个Bootstrap Loader 所做的初始工作中,除了也做一些基本的初始化动作之外,最重要的就是加载定义在sun.misc 命名空间底下的Launcher.java 之中的ExtClassLoader(因为是inner class,所以编译之后会变成


Launcher$ExtClassLoader.class),并设定其Parent 为null,代表其父加载器为Bootstrap


Loader。然后Bootstrap Loader 再要求加载定义于sun.misc 命名空间底下的Launcher.java 之中的AppClassLoader(因为是inner class,所以编译之后会变成Launcher$AppClassLoader.class),并设定其Parent 为之前产生的ExtClassLoader 实体。这里要请大家注意的是,Launcher$ExtClassLoader.class 与Launcher$AppClassLoader.class 都是由Bootstrap Loader 所加载,所以Parent 和由哪个类别加载器加载没有关系。我们可以用下图来表示:





这个由Bootstrap Loader   ExtClassLoader   AppClassLoader,就是我们所谓「类别载入


器的阶层体系」。



在此要请大家注意的是,AppClassLoader 和ExtClassLoader 都是URLClassLoader 的子类别。由于它们都是URLClassLoader 的子类别,所以它们也应该有URL 作为搜寻类别档的参考,由原始码中我们可以得知,AppClassLoader 所参考的URL 是从系统参数java.class.path 取出的字符串所决定,而java.class.path 则是由我们在执行java.exe 时,利用 –cp 或-classpath 或CLASSPATH 环境变量所决定。我们可以用底下程序代码测试之:


档案:test.java


public class test


{


public static void main(String args[])


{


String s = System.getProperty("java.class.path");


System.out.println(s) ;


}


}


输出结果如下:



从这个输出结果,我们可以看出,在预设情况下,AppClassLoader 的搜寻路径为”.”(目前所在目录),如果使用-classpath 选项(与-cp 等效),就可以改变AppClassLoader 的搜寻路径,如果没有指定-classpath 选项,就会搜寻环境变量CLASSPATH。如果同时有CLASSPATH 的环境设定与-classpath 选项,则以-classpath 选项的内容为主,CLASSPATH 的环境设定与-classpath 选项两者的内容不会有加成的效果。


至于ExtClassLoader 也有相同的情形,不过其搜寻路径是参考系统参数 java.ext.dirs。我们可以用底下程序代码测试:


档案:test.java


public class test


{


public static void main(String args[])


{


String s = System.getProperty("java.ext.dirs");


System.out.println(s) ;


}


}


输出结果如下:



输出结果告诉我们,系统参数java.ext.dirs 的内容,会指向java.exe 所选择的JRE 所在位置下的


libext 子目录。系统参数java.ext.dirs 的内容可以在一开始下命列的时候来更改,如下:



最后一个类别加载器是Bootstrap Loader , 我们可以经由查询由系统参数


sun.boot.class.path 得知Bootstrap Loader 用来搜寻类别的路径。请使用底下的程序代码测试之:


档案:test.java


public class test


{


public static void main(String args[])


{


String s = System.getProperty("sun.boot.class.path");


System.out.println(s) ;


}


}


输出结果如下:



系统参数sun.boot.class.path 的内容可以在一开始下命列的时候来更改,如下:




从这三个类别加载器的搜寻路径所参考的系统参数的名字中,其实还透漏了一个讯息。请回头看到java.class.path 与sun.boot.class.path,也就是说,AppClassLoader 与Bootstrap Loader会搜寻它们所指定的位置(或JAR 文件),如果找不到就找不到了,AppClassLoader 与Bootstrap Loader不会递归式地搜寻这些位置下的其它路径或其它没有被指定的JAR 檔。反观ExtClassLoader,所参考的系统参数是java.ext.dirs,意思是说,他会搜寻底下的所有JAR 文件以及classes 目录,作为其搜寻路径(所以您会发现上面我们在测试的时候,如果加入 -Dsun.boot.class.path=c:winnt选项时,程序的起始速度会慢了些,这是因为c:winnt 目录下的档案很多,必须花额外的时间来列举JAR 檔)。


在命令列下参数时,使用 –classpath / -cp / 环境变量CLASSPATH 来更改AppClassLoader


的搜寻路径,或者用 –Djava.ext.dirs 来改变ExtClassLoader 的搜寻目录,两者都是有意义的。可是用–Dsun.boot.class.path 来改变Bootstrap Loader 的搜寻路径是无效。这是因为AppClassLoader 与ExtClassLoader 都是各自参考这两个系统参数的内容而建立,当您在命令列下变更这两个系统参数之后, AppClassLoader 与ExtClassLoader 在建立实体的时候会参考这两个系统参数,因而改变了它们搜寻类别文件的路径;而系统参数sun.boot.class.path 则是预设与Bootstrap Loader 的搜寻路径相同,就算您更改该系统参与,与Bootstrap Loader 完全无关。如果您手边有原始码, 以JDK 1.4.x 为例, 请参考< 原始码根目录>hotspotsrcsharevmruntimeos.cpp 这个档案里头的os::set_boot_path 方法,您将看到程序代码片段:


static const char classpathFormat[] =


"%/lib/rt.jar:"


"%/lib/i18n.jar:"


"%/lib/sunrsasign.jar:"


"%/lib/jsse.jar:"


"%/lib/jce.jar:"


"%/lib/charsets.jar:"


"%/classes";


JDK 1.4.x 比JDK 1.3.x 新增了一些核心类别函式库(例如jsse.jar 与jce.jar),所以如果您测试时用的是1.4.x 版的JDK,sun.boot.class.path 内容应该如下:




9. 委派模型



如果您对整个类别加载的方式仍有所疑问,请容笔者重新解释一下之前程序


档案:Office.java


import java.net.* ;


public class Office


{


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


{


QQ|( 京ICP备09078825号 )

本网站信息发布软件,是可以发布论坛,发送信息到各大博客,各大b2b软件自动发布,好不夸张的说:只要手工能发在电脑打开IE能发的网站,用这个宣传软件就可以仿制动作,进行推送发到您想发送的B2B网站或是信息发布平台上,不管是后台,还是前台,都可以进行最方便的广告发布,这个广告发布软件,可以按月购买,还可以试用软件,对网站的验证码也可以完全自动对信息发布,让客户自动找上门,使企业轻松实现b2b发布,这个信息发布软件,均是本站原创正版开发,拥有正版的血统,想要新功能,欢迎提意见给我,一好的分类信息群发软件在手,舍我其谁。QQ896757558

GMT+8, 2024-11-24 01:18 , Processed in 0.136515 second(s), 42 queries .

宣传软件--信息发布软件--b2b软件广告发布软件

快速回复 返回顶部 返回列表