设为首页收藏本站language 语言切换
查看: 1278|回复: 0
收起左侧

新手看招:JAVA程序的类加载及其反射机制

[复制链接]
发表于 2010-2-20 13:57:45 | 显示全部楼层 |阅读模式
<p >JAVA中类文件加载是动态的。JVM指令是被封装在了. class文件里面,而.class文件的加载过程是动态的,也就是说当我们用到的时候才会去加载,如果不用的话,就不会去加载我们的类。这里所说的用到包括两种方式,第一种就是new一个对象的时候(这个时候要特别注意,当设计到多态的时候,就会有一点点变化,这时候编译器会做一些优化,这样以来当加载的时候会提前加载设计到多态的类,关于这一点下面有个例子(example 1)来说明。另一种就是当一个类的静态代码被调用的时候。 <BR><BR><CENTER><ccid_nobr><table width="400" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code" ><pre>java 代码</P><>//example 1 <BR>// Zoo.java </P><>abstract class Animal { </P><>Animal(){ </P><>System.out.println("Animal constructor"); <BR>} <BR>} </P><>class Tiger extends Animal { </P><>Tiger(){ </P><>System.out.println("Tig constructor "); <BR>} </P><>} </P><>class Dog extends Animal { </P><>Dog(){ </P><>System.out.println("Dog Constructor "); <BR>} <BR>} </P><>public class Zoo { </P><>private Animal am; //Example 1.1 <BR>//private Dog am; Example 1.2 <BR>private Tiger tiger; </P><>Zoo(){ <BR>tiger = new Tiger(); <BR>am = new Dog(); </P><>} </P><><BR>public static void main(String [] args){ </P><>System.out.println("new Zoo before"); <BR>Zoo z = new Zoo(); <BR>System.out.println("new Zoo after "); </P><>} <BR>} </pre></td></tr></table></ccid_nobr></CENTER><BR><BR>  当我们注释掉Example.1.1行时,运行Example1.2行,结果如下:<BR><BR>  Example 1.2<BR><BR>  分析以上两图的运行结果我们可以看出:当我们将子类对象赋值给父类时,编译器会做一点优化,于是加载器在还没有new 子类对象的时候已经加载了父类以及子类(example1.1结果),当不存在多态的时候,我们可以看到是当要new Dog()的时候才会加载Dog以及父类。无论何种方式,在new之前,类确实已经加载到了内存中。 <BR><BR>  JAVA为我们提供了两种动态机制。第一种是隐式机制。其实new一个对象和调用类的静态方法时,就是隐式机制在工作。第二种是显示机制。显示的机制又有两种策略(第一种是用java.lang.Class的forName(String str)方法,第二种是用java.lang.ClassLoader的loadClass())。 <BR><BR>  第一种:利用forName方法<BR><BR>  当我们查API文档就会发现forName方法有两种形式。分别如下:<BR><BR><CENTER><ccid_nobr><table width="400" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code" ><pre>public static Class<?> forName(String className) <BR>throws ClassNotFoundException </P><>public static Class<?> forName(String name, <BR>boolean initialize, <BR>ClassLoader loader) <BR>throws ClassNotFoundException </pre></td></tr></table></ccid_nobr></CENTER><BR><BR>  先来说说第二种方法:第二个方法值得注意的就是第二个参数boolean initialize,如果我们把这个参数设置为false,那么当我们加载完类后就不会执行静态代码和静态的初始化动作。只有当我们new一个对象的时候才会初始化。而第三个参数是用来指明类的加载器的。 <BR><BR>  如果查看java.lang.Class类的源代码,上述两种方法最终都会调用Class类中的私有的native方法forName0(),此方法的声明如下: <BR><BR><CENTER><ccid_nobr><table width="400" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code" ><pre>private static native Class forName0(String name, boolean init , ClassLoader loader) <BR>throws ClassNotFoundException; </pre></td></tr></table></ccid_nobr></CENTER><BR><BR>  所以当我们调用Class.forName(name )时,其实是在方法内部调用了:<BR><BR><CENTER><ccid_nobr><table width="400" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code" ><pre>forName0(name, true, ClassLoader.getCallerClassLoader()); </pre></td></tr></table></ccid_nobr></CENTER><BR>  当我们调用Class.forName(name, initialize, loader )的时候,实际上此方法内部调用了: <BR><BR><CENTER><ccid_nobr><table width="400" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code" ><pre>forName0(name, initialize, loader); </pre></td></tr></table></ccid_nobr></CENTER><BR><BR>  下面看一个例子,如果方法中第二个参数为false的情况: <BR><BR><CENTER><ccid_nobr><table width="400" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code" ><pre>java 代码<BR>//example 2.1 <BR>// Zoo.java </P><>abstract class Animal { </P><>static { </P><>System.out.println("Animal static code block "); <BR>} <BR>Animal(){ </P><>System.out.println("Animal constructor"); <BR>} <BR>} </P><>class Tiger extends Animal { </P><>Tiger(){ </P><>System.out.println("Tig constructor "); <BR>} </P><>} </P><>class Dog extends Animal { </P><>Dog(){ </P><>System.out.println("Dog Constructor "); <BR>} <BR>} </P><>public class Zoo { </P><><BR>public static void main(String [] args)throws Exception { </P><>System.out.println("new Zoo before"); <BR>Zoo z = new Zoo(); <BR>Class c = Class.forName("Dog",false,z.getClass().getClassLoader()); <BR>System.out.println("initilize before "); <BR>Animal dog = (Animal)c.newInstance(); <BR>System.out.println("new Zoo after "); </P><>} <BR>} </pre></td></tr></table></ccid_nobr></CENTER><BR><BR>  类加载完成后并没有立即执行静态初始化代码,而是到了实例化的时候才进行了静态初始化。有时候我们会说静态代码是在类第一次被加载时执行的,并且只执行一次。其实这是对与new一个对象,第一次访问类的静态代码以及第二个参数为true时而言的,对于动态的加载来说,如果forName方法的第二个参数设置为false,那么就是在实例化的时候才会执行静态初始化。当然默认情况下第二个参数是true.<BR><BR>  第二种方法:利用Class对象获取的ClassLoader装载。<BR><BR>  下面是一个简单的例子:<BR><BR><CENTER><ccid_nobr><table width="400" border="1" cellspacing="0" cellpadding="2" bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"><tr><td bgcolor="e6e6e6" class="code" ><pre>java 代码<BR>//Example 2.2 <BR>//Zoo.java <BR>abstract class Animal { </P><>static { </P><>System.out.println("Animal static code block "); <BR>} <BR>Animal(){ </P><>System.out.println("Animal constructor"); <BR>} <BR>} </P><>class Tiger extends Animal { </P><>Tiger(){ </P><>System.out.println("Tig constructor "); <BR>} </P><>} </P><>class Dog extends Animal { </P><>Dog(){ </P><>System.out.println("Dog Constructor "); <BR>} <BR>} </P><>public class Zoo { </P><><BR>public static void main(String [] args)throws Exception { </P><><BR>Class c = Zoo.class; <BR>ClassLoader loader = c.getClassLoader(); <BR>System.out.println("loader before"); <BR>Class dog = loader.loadClass("Dog"); <BR>System.out.println("instance before "); <BR>Animal an = (Animal)dog.newInstance(); </P><></P><>} <BR>}</pre></td></tr></table></ccid_nobr></CENTER><BR><BR>  loader完成以后并没有立即进行静态代码的执行。只有当newInstance()的时候才执行静态初始化,这和把public static Class forName(String name, boolean initialize, ClassLoader loader)的第二个参数指定为false的情况完全一样。其实每当我们写完一个编译单元以后就会得到一个.calss文件,这个文件中就包含了该类的Class对象。JVM就是利用这个class对象来进行动态装载类的。</P>(责任编辑:董建伟)                                 <p align="center"></p></p>
您需要登录后才可以回帖 登录 | 论坛注册

本版积分规则

QQ|Archiver|手机版|小黑屋|sitemap|鸿鹄论坛 ( 京ICP备14027439号 )  

GMT+8, 2025-4-9 00:57 , Processed in 0.072800 second(s), 24 queries , Redis On.  

  Powered by Discuz!

  © 2001-2025 HH010.COM

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