三个参数的 Class.forName()的第二参数
给类添加一个静态代码块
public class Word implements Assembly { static { System.out.println("Word Static Initialization"); } public void start() { System.out.println("Word start"); } }
public class Office { public static void main(String args[]) throws Exception { Office o=new Office(); System.out.println("类准备载入"); //Class c=Class.forName(args[0],true,o.getClass().getClassLoader()); Class c=Class.forName(args[0],false,o.getClass().getClassLoader()); System.out.println("准备实例化"); Assembly a1=(Assembly)c.newInstance(); a1.start(); Assembly a2=(Assembly)c.newInstance(); a2.start(); } } 为true 时 C:\>java Office Word 类准备载入 Word Static Initialization 准备实例化 Word start Word start 为false时 C:\>java Office Word 类准备载入 准备实例化 Word Static Initialization Word start Word start
|
静态初始化区块是在类别第一次被实例化的时候才会被呼叫那仅仅一次。
注意:
不管您使用的是new来产生某类别的实例、或是使用只有一个参数的forName()方法,内部都隐含了“加载类别+呼叫静态初始化区块”的动作。
而使用具有三个参数的orName()方法时,如果第二个参数给定的是false,那么就只会命令类别加载器加载该类别,但不会叫用其静态初始化区块,只有等到整个程序第一次实例化某个类别时,静态初始化区块才会被叫用。
static块在什么时候执行?
1)当调用forName(String)载入class时执行,如果调用ClassLoader.loadClass并不会执行.forName(String,false,ClassLoader)时也不会执行。
2)如果载入Class时没有执行static块则在第一次实例化时执行.比如new,Class.newInstance()操作。
3)static块仅执行一次。
public class Office { public static void main(String args[]) throws Exception { Office o=new Office(); System.out.println("类准备载入"); ClassLoader loader=o.getClass().getClassLoader(); Class c=loader.loadClass(args[0]); System.out.println("准备实例化"); Assembly a1=(Assembly)c.newInstance(); a1.start(); Assembly a2=(Assembly)c.newInstance(); a2.start(); } } ------------------------------------------------
|
第二种:直接使用ClassLoader类别的loadClass()方法来加载类
此方式只会把类加载至内存,并不会调用该类别的静态初始化区块,而必须等到第一次实例化该类别时,该类别的静态初始化区块才会被叫用。
这种情形与使用Class 类别的forName()方法时,第二个参数传入false 几乎是相同的结果。
另一种方式
public class Office { public static void main(String args[]) throws Exception { Office o=new Office(); Class co=Office.class; System.out.println("类准备载入"); ClassLoader loader=co.getClassLoader(); Class c=loader.loadClass(args[0]); System.out.println("准备实例化"); Assembly a1=(Assembly)c.newInstance(); a1.start(); Assembly a2=(Assembly)c.newInstance(); a2.start(); } }
|
归纳:
1.
Office o=new Office();
ClassLoader loader=o.getClass().getClassLoader();
调用对象的getClass()方式取得该对象的引用,再调用该引用的getClassLoader()方法取得该对象类加载器的引用。
2. Office o=new Office();
Class co=Office.class;
ClassLoader loader=co.getClassLoader();
直接定义一个此对象类的.class 类引用然后由此对象的getClassLoader()方法取得该对象类加载器的引用。
然后 Class c=loader.loadClass(args[0]);
Class类的实例
◆Class类无法手工实例化,当载入任意类的时候自动创建一个该类对应的Class的实例。
◆某个类的所有实例内部都有一个栏位记录着该类对应的Class的实例的位置。
◆每个Java类对应的Class实例可以当作是类在内存中的代理人。所以当要获得类的信息(如有哪些类变量,有哪些方法)时,都可以让类对应的Class的实例代劳。Java的Reflection机制就大量的使用这种方法来实现。
◆每个Java类都是由某个classLoader(ClassLoader的实例)来载入的,因此Class类别的实例中都会有栏位记录他的ClassLoader的实例,如果该栏位为null,则表示该类别是由bootstrap loader载入的(也称root laoder),bootstrap loader不是java所写成,所以没有实例。
-------------------------------------------------------
自己建立类别加载器来加载类别
利用Java 本身提供的java.net.URLClassLoader
如实例化一个URLClassLoader. URLClassLoader ucl = new URLClassLoader(new URL[]{new URL("file:/e:/bin/")}),URLClassLoader优先找当前目录,再在url中找.class加载.URL中别忘在最后加"/"表示目录。
import java.net.*; public class Office { public static void main(String[] args) throws Exception { if(args.length!=1) { return ; }else { URL u=new URL("http://share/"); URLClassLoader ucl=new URLClassLoader(new URL[]{u}); Class c=ucl.loadClass(args[0]); Assembly asm=(Assembly)c.newInstance(); asm.start(); } } } ------------------------------------------------ import java.net.*; public class Office { public static void main(String[] args) throws Exception { URL u = new URL("Http://share/") ; URLClassLoader ucl = new URLClassLoader(new URL[]{ u }) ; Class c = ucl.loadClass(args[0]) ; Assembly asm = (Assembly) c.newInstance() ; asm.start() ; URL u1 = new URL("Http://share/") ; 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()) ; } } C:\>java Office Word Word Static Initialization Word start Word start sun.misc.Launcher$AppClassLoader@82ba41 null null sun.misc.Launcher$AppClassLoader@82ba41 sun.misc.Launcher$AppClassLoader@82ba41 null null sun.misc.Launcher$AppClassLoader@82ba41 sun.misc.Launcher$AppClassLoader@82ba41
|
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()

更多内容请看
Java环境安装配置、
Java编程开发手册专题,或
进入讨论组讨论。
【深 度 阅 读】 相 关 文 章