在命令提示符下输入java –verbose Office Word 输出入下:
0 Y9 E O; l1 W5 @$ u/ K" Z4 d 通过上图你可以看到,interface 如同class 一般,会由编译器产生一个独立的类别档(.class),当类别载入器载入类别时,如果发现该类别继承了其他类别,或是实作了其他介面,就会先载入代表该介面的类别档,也会载入其父类别的类别档,如果父类别也有其父类别,也会一并优先载入。换句话说,类别载入器会依继承体系最上层的类别往下依序载入,直到所有的祖先类别都载入了,才轮到自己载入。
2 O+ y% X5 E% S7 ?# }, x 下面介绍一下 forName 函数, 如果您亲自搜寻Java 2 SDK 说明档内部对於Class 这个类别的说明,您可以发现其实有两个forName()方法,一个是只有一个参数的(就是之前程式之中所使用的):! G2 p, y5 U- D
public static Class forName(String className)
; I; c+ h. h4 J1 e3 S, P 另外一个是需要三个参数的:
( f. {" x& Z: X+ o* j" ] public static Class forName(String name, boolean initialize,ClassLoader loader)
1 A J5 m, v" F8 w 这两个方法,最後都是连接到原生方法forName0(),其宣告如下:; V1 e- I7 k. D. R
private static native Class forName0(String name, boolean initialize, ClassLoader loader)
$ k5 j5 _) @, R& m) @: r7 @3 m( E- z throws ClassNotFoundException;
5 p* F; Q7 Y: D$ Q. O 只有一个参数的forName()方法,最後叫用的是:
9 s5 D- Q' l1 R4 B! ^) F! \ forName0(className, true, ClassLoader.getCallerClassLoader());
" Y6 p N( B" T6 M" g p) h9 A. i 而具有三个参数的forName()方法,最後叫用的是:# K8 D% I0 O' E: y; n; x, R
forName0(name, initialize, loader);
2 s! A; U3 k& b- { 这里initialize参数指,在载入类之后是否进行初始化,对于该参数的作用可用如下示例察看:$ K( s) i/ N/ l% Y9 F, D
类里的静态初始化块在类第一次被初始化时才被呼叫,且仅呼叫一次。在Word类里,加入静态初始化块: R2 Z* Z# z" ?9 H
public class Word implements Assembly% s: w3 D5 c$ |1 t
{( j9 D; l: ^3 }7 z0 S& C/ Q3 ]9 q
static
# w% w4 C. W- S/ @7 Z/ d; |9 x {
% f0 ~/ e$ B5 A. K. Y+ o% }0 ?! K1 k System.out.println("word static initialization ");
7 e3 h- {5 G z/ T. O$ Y- y( z }' v- W; B9 r1 ~4 Z: v- K
public void start()
0 R+ I- w, e* x {
) z+ R" W- t8 ? System.out.println("using word");( A, X% `" e( }$ [: P. M$ y4 C
}
# U& b0 s0 l" W* L* r9 d z }
% [( V5 G+ E/ }9 {* t9 D, F6 K 将类Office作如下改变:
( y1 E; i& h9 y; N( c public class Office
# ~( F/ d- u( F3 ^. a {
+ E* V) j" }/ p. t public static void main(String[] args) throws Exception
* ?; V9 G! r* g! l% {0 z {
" x8 X* z/ {1 G( P5 p0 q& D Office off= new Office();0 E0 @1 A$ x! ^: W# h: G
System.out.println("类别准备载入");+ u9 ?7 o# Q1 e3 n+ S
java.lang.Class c = java.lang.Class.forName(args[0],true,off.getClass().getClassLoader());
* C( m! f" I/ M9 n3 u8 }) _ System.out.println("类别准备实体化");
" n+ X8 u- G& ?. Z I$ m Object o = c.newInstance();
; V+ w% p0 M! Q8 x Object o2 = c.newInstance();( c; z3 A% r8 |& M; j
}* n6 l- [; U s7 e2 Z$ ?
}1 P* z6 C* B; L3 B1 H
如果第二个参数为true 则输出入下
, U( f- Z6 J5 T' z 如果为false ,则输出入下:
% {- g# P! u' |5 o& m/ v' ? 可见,类里的静态初始化块仅在初始化时才执行,且不过初始化几次,它仅执行一次(这里有一个条件,那就是只有它是被同一个类别载入器多次载入时,才是这样,如果被不同的载入器,载入多次,则静态初始化块会执行多次)。
; |% B- f% W5 B$ | 关于第三个参数请见下节介绍4 n3 k8 b/ v- s
3.2 直接使用类别载入器 java.lang.ClassLoader
) z- {: `% b: B4 F5 i' k- m 在Java 之中,每个类别最後的老祖宗都是Object,而Object 里有一个名为getClass()的方法,就是用来取得某特定实体所属类别的参考,这个参考,指向的是一个名为Class 类别(Class.class) 的实体,您无法自行产生一个Class 类别的实体,因为它的建构式被宣告成private,这个Class 类别的实体是在类别档(.class)第一次载入记忆体时就建立的,往後您在程式中产生任何该类别的实体,这些实体的内部都会有一个栏位记录着这个Class 类别的所在位置。
X$ O0 U! d5 e6 `& m* v 基本上,我们可以把每个Class 类别的实体,当作是某个类别在记忆体中的代理人。每次我们需要
" M1 _0 {, n) F# d2 v4 Y0 m0 Y 查询该类别的资料(如其中的field、method 等)时,就可以请这个实体帮我们代劳。事实上,Java的Reflection 机制,就大量地利用Class 类别。去深入Class 类别的原始码,我们可以发现Class类别的定义中大多数的方法都是原生方法(native method)。
9 S$ m8 M) W- y! S2 o5 D2 H 在Java 之中,每个类别都是由某个类别载入器(ClassLoader 的实体)来载入,因此,Class 类别的实体中,都会有栏位记录着载入它的ClassLoader 的实体(注意:如果该栏位是null,并不代表它不是由类别载入器所载入,而是代表这个类别由靴带式载入器(bootstrap loader,也有人称rootloader)所载入,只不过因为这个载入器并不是用Java 所写成,是用C++写的,所以逻辑上没有实体)。/ i# ~9 ?/ p/ Q2 L% d# C0 A$ j
系统里同时存在多个ClassLoader 的实体,而且一个类别载入器不限於只能载入一个类别,类别载入器可以载入多个类别。所以,只要取得Class 类别实体的参考,就可以利用其getClassLoader()方法篮取得载入该类别之类别载入器的参考。getClassLoader()方法最後会呼叫原生方法getClassLoader0(),其宣告如下:private native ClassLoader getClassLoader0();0 x( p, z! m% ^ m
最後,取得了ClassLoader 的实体,我们就可以叫用其loadClass()方法帮我们载入我们想要的类别,因此上面的Office类可做如下修改: h; ~9 Y+ f- N5 R9 U
public class Office% K* ]( d- T- \8 N
{7 M5 t2 s" a, L( g
public static void main(String[] args) throws Exception/ C$ y& n Y1 B; i; k
{
- y5 B" D/ L+ B% N+ t8 X Office off= new Office();
& ?, S! U/ ` ~. n System.out.println("类别准备载入");
7 S4 y& ]# n, t, m+ F2 z ClassLoader loader = off.getClass().getClassLoader();
+ I' _' z# \7 j7 g3 L java.lang.Class c = loader.loadClass(args[0]);( c1 ~7 I$ q. F/ s0 t l9 C
System.out.println("类别准备实体化");( b- M" |3 X# p9 N% K9 W$ ?
Object o = c.newInstance();. t7 z: ?$ X. O3 M# t/ w- n
Object o2 = c.newInstance();
, C" d4 c5 _# W3 t2 o) O0 @5 \' K. y }# o7 N" D5 J0 b8 E2 \2 Y4 H3 `. L
} |