??xml version="1.0" encoding="utf-8" standalone="yes"?> 在此我们先来明确一下我们的前提条g。该对象臛_有一个synchronizedҎ(gu)Q否则这个keyq有啥意义。当然也׃会有我们的这个主题了?/p>
一个h惌入某间上了锁的房_他来到房子门口,看见钥匙在那儿(说明暂时q没有其他h要用上锁的戉KQ。于是他CLC钥匙Qƈ且按照自q计划使用那些戉K。注意一点,他每ơ用完一ơ上锁的戉K后会马上把钥匙还回去。即使他要连l用两间上锁的戉KQ中间他也要把钥匙还回去Q再取回来?/p>
因此Q普通情况下钥匙的用原则是Q?#8220;随用随借,用完卌?#8221; q时其他人可以不受限制的使用那些不上锁的戉KQ一个h用一间可以,两个人用一间也可以Q没限制。但是如果当某个人想要进入上锁的戉KQ他p跑到大门口去看看了。有钥匙当然拿了pQ没有的话,只能等了?/p>
要是很多人在{这把钥匙,{钥匙还回来以后Q谁会优先得到钥匙?Not guaranteed。象前面例子里那个想q箋使用两个上锁戉K的家伙,他中间还钥匙的时候如果还有其他h在等钥匙Q那么没有Q何保证这家伙能再ơ拿到。(JAVA规范在很多地斚w明确说明不保证,象Thread.sleep()休息后多久会q回q行Q相同优先权的线E那个首先被执行Q当要访问对象的锁被释放后处于等待池的多个线E哪个会优先得到Q等{。我xl的军_权是在JVMQ之所以不保证Q就是因为JVM在做Zq决定的时候,l不是简单单Ҏ(gu)一个条件来做出判断Q而是Ҏ(gu)很多条。而由于判断条件太多,如果说出来可能会影响JAVA的推q,也可能是因ؓ知识产权保护的原因吧。SUNl了个不保证q去了。无可厚非。但我相信这些不定Qƈ非完全不定。因机q东西本w就是按指oq行的。即使看h很随机的现象Q其实都是有规律可寻。学q计机的都知道Q计机里随机数的学名是伪随机数Q是用一定的Ҏ(gu)写出来的Q看上去随机|了。另外,或许是因惛_的确定太费事Q也没多大意义,所以不定׃定了吧。) 再来看看同步代码块。和同步Ҏ(gu)有小的不同?/p>
1.从尺怸Ԍ同步代码块比同步Ҏ(gu)。你可以把同步代码块看成是没上锁戉K里的一块用带锁的屏风隔开的空间?/p>
2.同步代码块还可以Zؓ的指定获得某个其它对象的key。就像是指定用哪一把钥匙才能开q个屏风的锁Q你可以用本房的钥匙Q你也可以指定用另一个房子的钥匙才能开Q这L话,你要跑到另一栋房子那儿把那个钥匙拿来Qƈ用那个房子的钥匙来打开q个房子的带锁的屏风?/p>
C你获得的那另一栋房子的钥匙Qƈ不媄响其他hq入那栋房子没有锁的戉K?/p>
Z么要使用同步代码块呢Q我惛_该是q样的:首先对程序来讲同步的部分很媄响运行效率,而一个方法通常是先创徏一些局部变量,再对q些变量做一些操作,如运,昄{等Q而同步所覆盖的代码越多,Ҏ(gu)率的影响p严重。因此我们通常量~小其媄响范围。如何做Q同步代码块。我们只把一个方法中该同步的地方同步Q比如运?/p>
另外Q同步代码块可以指定钥匙q一特点有个额外的好处,是可以在一定时期内霸占某个对象的key。还记得前面说过普通情况下钥匙的用原则吗。现在不是普通情况了。你所取得的那把钥匙不是永q不q,而是在退出同步代码块时才q?/p>
q用前面那个惌l用两个上锁戉K的家伙打比方。怎样才能在用完一间以后,l箋使用另一间呢。用同步代码块吧。先创徏另外一个线E,做一个同步代码块Q把那个代码块的锁指向这个房子的钥匙。然后启动那个线E。只要你能在q入那个代码块时抓到q房子的钥匙Q你可以一直保留到退出那个代码块。也是说你甚至可以Ҏ(gu)房内所有上锁的戉K遍历Q甚臛_sleep(10*60*1000)Q而房门口却还?000个线E在{这把钥匙呢。很q瘾吧?/p>
在此对sleep()Ҏ(gu)和钥匙的兌性讲一下。一个线E在拿到key后,且没有完成同步的内容Ӟ如果被强制sleep()了,那keyq一直在它那ѝ直到它再次q行Q做完所有同步内容,才会归还key。记住,那家伙只是干zd累了Q去休息一下,他ƈ没干完他要干的事。ؓ了避免别入那个房间把里面搞的一团糟Q即使在睡觉的时候他也要把那唯一的钥匙戴在n上?/p>
最后,也许有h会问Qؓ什么要一把钥匙通开Q而不是一个钥匙一个门呢?我想q纯_Ҏ(gu)因ؓ复杂性问题。一个钥匙一个门当然更安全,但是会牵扯好多问题。钥匙的产生Q保,获得Q归q等{。其复杂性有可能随同步方法的增加呈几何数增加,严重影响效率?/p>
q也是一个权衡的问题吧。ؓ了增加一点点安全性,D效率大大降低Q是多么不可取啊?/p>
摘自Qhttp://www.54bk.com/more.asp?name=czp&id=2097 一、当两个q发U程讉K同一个对象object中的q个synchronized(this)同步代码块时Q一个时间内只能有一个线E得到执行。另一个线E必ȝ待当前线E执行完q个代码块以后才能执行该代码块?/p>
二、然而,当一个线E访问object的一个synchronized(this)同步代码块时Q另一个线E仍然可以访问该object中的非synchronized(this)同步代码块?/p>
三、尤其关键的是,当一个线E访问object的一个synchronized(this)同步代码块时Q其他线E对object中所有其它synchronized(this)同步代码块的讉K被d?/p>
四、第三个例子同样适用其它同步代码块。也是_当一个线E访问object的一个synchronized(this)同步代码块时Q它?yu)p得了q个object的对象锁。结果,其它U程对该object对象所有同步代码部分的讉K都被暂时d?/p>
五、以上规则对其它对象锁同样适用. 举例说明Q?/p>
一、当两个q发U程讉K同一个对象object中的q个synchronized(this)同步代码块时Q一个时间内只能有一个线E得到执行。另一个线E必ȝ待当前线E执行完q个代码块以后才能执行该代码块?/p>
package ths; public class Thread1 implements Runnable { l果Q?/p>
A synchronized loop 0 二、然而,当一个线E访问object的一个synchronized(this)同步代码块时Q另一个线E仍然可以访问该object中的非synchronized(this)同步代码块?/p>
package ths; public class Thread2 { l果Q?/p>
t1 : 4 三、尤其关键的是,当一个线E访问object的一个synchronized(this)同步代码块时Q其他线E对object中所有其它synchronized(this)同步代码块的讉K被d?/p>
//修改Thread2.m4t2()Ҏ(gu)Q?/p>
public void m4t2() { } l果Q?/p>
t1 : 4 四、第三个例子同样适用其它同步代码块。也是_当一个线E访问object的一个synchronized(this)同步代码块时Q它?yu)p得了q个object的对象锁。结果,其它U程对该object对象所有同步代码部分的讉K都被暂时d?/p>
//修改Thread2.m4t2()Ҏ(gu)如下Q?/p>
public synchronized void m4t2() { l果Q?/p>
t1 : 4 五、以上规则对其它对象锁同样适用: package ths; public class Thread3 { l果Q?/p>
管U程t1获得了对Inner的对象锁Q但׃U程t2讉K的是同一个Inner中的非同步部分。所以两个线E互不干扰?/p>
t1 : Inner.m4t1()=4 现在在Inner.m4t2()前面加上synchronizedQ?/p>
private synchronized void m4t2() { l果Q?/p>
管U程t1与t2讉K了同一个Inner对象中两个毫不相关的部分,但因为t1先获得了对Inner的对象锁Q所以t2对Inner.m4t2()的访问也被阻塞,因ؓm4t2()是Inner中的一个同步方法?/p>
t1 : Inner.m4t1()=4
public void run() {
synchronized(this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);
}
}
}
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t1, "B");
ta.start();
tb.start();
}
}
A synchronized loop 1
A synchronized loop 2
A synchronized loop 3
A synchronized loop 4
B synchronized loop 0
B synchronized loop 1
B synchronized loop 2
B synchronized loop 3
B synchronized loop 4
public void m4t1() {
synchronized(this) {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
}
public void m4t2() {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
public static void main(String[] args) {
final Thread2 myt2 = new Thread2();
Thread t1 = new Thread(
new Runnable() {
public void run() {
myt2.m4t1();
}
}, "t1"
);
Thread t2 = new Thread(
new Runnable() {
public void run() {
myt2.m4t2();
}
}, "t2"
);
t1.start();
t2.start();
}
}
t2 : 4
t1 : 3
t2 : 3
t1 : 2
t2 : 2
t1 : 1
t2 : 1
t1 : 0
t2 : 0
synchronized(this) {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0
class Inner {
private void m4t1() {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i);
try {
Thread.sleep(500);
} catch(InterruptedException ie) {
}
}
}
private void m4t2() {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);
try {
Thread.sleep(500);
} catch(InterruptedException ie) {
}
}
}
}
private void m4t1(Inner inner) {
synchronized(inner) { //使用对象?
inner.m4t1();
}
}
private void m4t2(Inner inner) {
inner.m4t2();
}
public static void main(String[] args) {
final Thread3 myt3 = new Thread3();
final Inner inner = myt3.new Inner();
Thread t1 = new Thread(
new Runnable() {
public void run() {
myt3.m4t1(inner);
}
}, "t1"
);
Thread t2 = new Thread(
new Runnable() {
public void run() {
myt3.m4t2(inner);
}
}, "t2"
);
t1.start();
t2.start();
}
}
t2 : Inner.m4t2()=4
t1 : Inner.m4t1()=3
t2 : Inner.m4t2()=3
t1 : Inner.m4t1()=2
t2 : Inner.m4t2()=2
t1 : Inner.m4t1()=1
t2 : Inner.m4t2()=1
t1 : Inner.m4t1()=0
t2 : Inner.m4t2()=0
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);
try {
Thread.sleep(500);
} catch(InterruptedException ie) {
}
}
}
t1 : Inner.m4t1()=3
t1 : Inner.m4t1()=2
t1 : Inner.m4t1()=1
t1 : Inner.m4t1()=0
t2 : Inner.m4t2()=4
t2 : Inner.m4t2()=3
t2 : Inner.m4t2()=2
t2 : Inner.m4t2()=1
t2 : Inner.m4t2()=0
]]>
Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在q行旉过Reflection APIs取得M一个已知名U的class的内部信息,包括其modifiersQ诸如public, static {等Q、superclassQ例如ObjectQ、实CinterfacesQ例如CloneableQ,也包括fields和methods的所有信息,q可于运行时改变fields内容或唤起methods。本文借由实例Q大面积CReflection APIs?br>
关键词:
IntrospectionQ内省、内观)
ReflectionQ反)
有时候我们说某个语言h很强的动态性,有时候我们会区分动态和静态的不同技术与作法。我们朗朗上口动态绑定(dynamic bindingQ、动态链接(dynamic linkingQ、动态加载(dynamic loadingQ等。然?#8220;动?#8221;一词其实没有绝对而普遍适用的严格定义,有时候甚臛_对象导向当初被导入编E领域一P一Z把号Q各吹各的调?br>
一般而言Q开发者社说到动态语aQ大致认同的一个定义是Q?#8220;E序q行Ӟ允许改变E序l构或变量类型,q种语言UCؓ动态语a”。从q个观点看,PerlQPythonQRuby是动态语aQC++QJavaQC#不是动态语a?br>
管在这L定义与分cMJava不是动态语aQ它却有着一个非常突出的动态相xӞReflection。这个字的意思是“反射、映象、倒媄”Q用在Javaw上指的是我们可以于q行时加载、探知、用编译期间完全未知的classes。换句话_JavaE序可以加蝲一个运行时才得知名U的classQ获(zhn)其完整构造(但不包括methods定义Q,q生成其对象实体、或对其fields讑ր{或唤v其methods1。这U?#8220;看透class”的能力(the ability of the program to examine itselfQ被UCؓintrospectionQ内省、内观、反省)。Reflection和introspection是常被ƈ提的两个术语?br>
Java如何能够做出上述的动态特性呢Q这是一个深q话题,本文Ҏ(gu)只简单介l一些概c整个篇q最主要q是介绍Reflection APIsQ也是让读者知道如何探索class的结构、如何对某个“q行时才L名称的class”生成一份实体、ؓ其fields讑ր{调用其methods。本文将谈到java.lang.ClassQ以及java.lang.reflect中的Method、Field、Constructor{等classes?br>
“Class”class
众所周知Java有个Object classQ是所有Java classes的承根源,其内声明了数个应该在所有Java class中被改写的methodsQhashCode()、equals()、clone()、toString()、getClass(){。其中getClass()q回一个Class object?br>
Class class十分Ҏ(gu)。它和一般classes一L承自ObjectQ其实体用以表达JavaE序q行时的classes和interfacesQ也用来表达enum、array、primitive Java typesQboolean, byte, char, short, int, long, float, doubleQ以及关键词void。当一个class被加载,或当加蝲器(class loaderQ的defineClass()被JVM调用QJVM 便自动生一个Class object。如果?zhn)惛_由“修改Java标准库源?#8221;来观察Class object的实际生成时机(例如在Class的constructor内添加一个println()Q,不能够!因ؓClassq没有public constructorQ见?Q。本文最后我会拨一块幅带谈谈Java标准库源码的改动办法?br>
Class是Reflection故事h。针对Q何?zhn)x勘的classQ唯有先为它产生一个Class objectQ接下来才能l由后者唤起ؓ数十多个的Reflection APIs。这些APIs在E后的探险活动中一一亮相?br>
#001 public final
#002 class Class<T> implements java.io.Serializable,
#003 java.lang.reflect.GenericDeclaration,
#004 java.lang.reflect.Type,
#005 java.lang.reflect.AnnotatedElement {
#006 private Class() {}
#007 public String toString() {
#008 return ( isInterface() ? "interface " :
#009 (isPrimitive() ? "" : "class "))
#010 + getName();
#011 }
...
?QClass class片段。注意它的private empty ctorQ意指不允许M人经qE方式生Class object。是的,其object 只能由JVM 产生?br>
“Class” object的取得途径
Java允许我们从多U管道ؓ一个class生成对应的Class object。图2是一份整理?br>Class object 诞生道 CZ
q用getClass()
注:每个class 都有此函?String str = "abc";
Class c1 = str.getClass();
q用
Class.getSuperclass()2 Button b = new Button();
Class c1 = b.getClass();
Class c2 = c1.getSuperclass();
q用static method
Class.forName()
Q最常被使用Q?Class c1 = Class.forName ("java.lang.String");
Class c2 = Class.forName ("java.awt.Button");
Class c3 = Class.forName ("java.util.LinkedList$Entry");
Class c4 = Class.forName ("I");
Class c5 = Class.forName ("[I");
q用
.class 语法 Class c1 = String.class;
Class c2 = java.awt.Button.class;
Class c3 = Main.InnerClass.class;
Class c4 = int.class;
Class c5 = int[].class;
q用
primitive wrapper classes
的TYPE 语法
Class c1 = Boolean.TYPE;
Class c2 = Byte.TYPE;
Class c3 = Character.TYPE;
Class c4 = Short.TYPE;
Class c5 = Integer.TYPE;
Class c6 = Long.TYPE;
Class c7 = Float.TYPE;
Class c8 = Double.TYPE;
Class c9 = Void.TYPE;
?QJava 允许多种道生成Class object?br>
Java classes l成分析
首先Ҏ(gu)以图3的java.util.LinkedListZQ将Java class的定义大卸八块,每一块分别对应图4所C的Reflection API。图5则是“获得class各区块信?#8221;的程序示例及执行l果Q它们都取自本文CZE序的对应片Dc?br>
package java.util; //(1)
import java.lang.*; //(2)
public class LinkedList<E> //(3)(4)(5)
extends AbstractSequentialList<E> //(6)
implements List<E>, Queue<E>,
Cloneable, java.io.Serializable //(7)
{
private static class Entry<E> { … }//(8)
public LinkedList() { … } //(9)
public LinkedList(Collection<? extends E> c) { … }
public E getFirst() { … } //(10)
public E getLast() { … }
private transient Entry<E> header = …; //(11)
private transient int size = 0;
}
?Q将一个Java class 大卸八块Q每块相应于一个或一lReflection APIsQ图4Q?br>
Java classes 各成份所对应的Reflection APIs
?的各个Java class成䆾Q分别对应于?的Reflection APIQ其中出现的Package、Method、Constructor、Field{等classesQ都定义于java.lang.reflect?br>Java class 内部模块Q参见图3Q?Java class 内部模块说明 相应之Reflection APIQ多半ؓClass methods?q回值类?return type)
(1) package class隶属哪个package getPackage() Package
(2) import class导入哪些classes 无直接对应之API?br>解决办法见图5-2?nbsp;
(3) modifier classQ或methods, fieldsQ的属?br> int getModifiers()
Modifier.toString(int)
Modifier.isInterface(int) int
String
bool
(4) class name or interface name class/interface 名称getName() String
(5) type parameters 参数化类型的名称 getTypeParameters() TypeVariable <Class>[]
(6) base class base classQ只可能一个) getSuperClass() Class
(7) implemented interfaces 实现有哪些interfaces getInterfaces() Class[]
(8) inner classes 内部classes getDeclaredClasses() Class[]
(8') outer class 如果我们观察的class 本n是inner classesQ那么相对它?yu)׃有个outer class?getDeclaringClass() Class
(9) constructors 构造函数getDeclaredConstructors() 不论 public 或private 或其它access levelQ皆可获得。另有功能近g取得函数?Constructor[]
(10) methods 操作函数getDeclaredMethods() 不论 public 或private 或其它access levelQ皆可获得。另有功能近g取得函数?Method[]
(11) fields 字段Q成员变量) getDeclaredFields()不论 public 或private 或其它access levelQ皆可获得。另有功能近g取得函数?Field[]
?QJava class大卸八块后(如图3Q,每一块所对应的Reflection API。本表ƈ?br>Reflection APIs 的全部?br>
Java Reflection API q用CZ
?C?提过的每一个Reflection APIQ及其执行结果。程序中出现的tName()是个辅助函数Q可其W一自变量所代表?#8220;Java class完整路径字符?#8221;剥除路径部分Q留下class名称Q储存到W二自变量所代表的一个hashtabledƈq回Q如果第二自变量为nullQ就不储存而只是返回)?br>
#001 Class c = null;
#002 c = Class.forName(args[0]);
#003
#004 Package p;
#005 p = c.getPackage();
#006
#007 if (p != null)
#008 System.out.println("package "+p.getName()+";");
执行l果Q例Q:
package java.util;
?-1Q找出class 隶属的package。其中的cl沿用于以下各程序片Dc?br>
#001 ff = c.getDeclaredFields();
#002 for (int i = 0; i < ff.length; i++)
#003 x = tName(ff[i].getType().getName(), classRef);
#004
#005 cn = c.getDeclaredConstructors();
#006 for (int i = 0; i < cn.length; i++) {
#007 Class cx[] = cn[i].getParameterTypes();
#008 for (int j = 0; j < cx.length; j++)
#009 x = tName(cx[j].getName(), classRef);
#010 }
#011
#012 mm = c.getDeclaredMethods();
#013 for (int i = 0; i < mm.length; i++) {
#014 x = tName(mm[i].getReturnType().getName(), classRef);
#015 Class cx[] = mm[i].getParameterTypes();
#016 for (int j = 0; j < cx.length; j++)
#017 x = tName(cx[j].getName(), classRef);
#018 }
#019 classRef.remove(c.getName()); //不必记录自己Q不需import 自己Q?br>
执行l果Q例Q:
import java.util.ListIterator;
import java.lang.Object;
import java.util.LinkedList$Entry;
import java.util.Collection;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
?-2Q找出导入的classesQ动作细节详见内文说明?br>
#001 int mod = c.getModifiers();
#002 System.out.print(Modifier.toString(mod)); //整个modifier
#003
#004 if (Modifier.isInterface(mod))
#005 System.out.print(" "); //关键?"interface" 已含于modifier
#006 else
#007 System.out.print(" class "); //关键?"class"
#008 System.out.print(tName(c.getName(), null)); //class 名称
执行l果Q例Q:
public class LinkedList
?-3Q找出class或interface 的名Uͼ及其属性(modifiersQ?br>
#001 TypeVariable<Class>[] tv;
#002 tv = c.getTypeParameters(); //warning: unchecked conversion
#003 for (int i = 0; i < tv.length; i++) {
#004 x = tName(tv[i].getName(), null); //例如 E,K,V...
#005 if (i == 0) //W一?br>#006 System.out.print("<" + x);
#007 else //非第一?br>#008 System.out.print("," + x);
#009 if (i == tv.length-1) //最后一?br>#010 System.out.println(">");
#011 }
执行l果Q例Q:
public abstract interface Map<K,V>
或public class LinkedList<E>
?-4Q找出parameterized types 的名U?br>
#001 Class supClass;
#002 supClass = c.getSuperclass();
#003 if (supClass != null) //如果有super class
#004 System.out.print(" extends" +
#005 tName(supClass.getName(),classRef));
执行l果Q例Q:
public class LinkedList<E>
extends AbstractSequentialList,
?-5Q找出base class。执行结果多Z个不该有的逗号于尾端。此非本处重点,为简化计Q不多做处理?br>
#001 Class cc[];
#002 Class ctmp;
#003 //扑և所有被实现的interfaces
#004 cc = c.getInterfaces();
#005 if (cc.length != 0)
#006 System.out.print(", \r\n" + " implements "); //关键?br>#007 for (Class cite : cc) //JDK1.5 新式循环写法
#008 System.out.print(tName(cite.getName(), null)+", ");
执行l果Q例Q:
public class LinkedList<E>
extends AbstractSequentialList,
implements List, Queue, Cloneable, Serializable,
?-6Q找出implemented interfaces。执行结果多Z个不该有的逗号于尾端。此非本处重点,为简化计Q不多做处理?br>
#001 cc = c.getDeclaredClasses(); //扑ևinner classes
#002 for (Class cite : cc)
#003 System.out.println(tName(cite.getName(), null));
#004
#005 ctmp = c.getDeclaringClass(); //扑ևouter classes
#006 if (ctmp != null)
#007 System.out.println(ctmp.getName());
执行l果Q例Q:
LinkedList$Entry
LinkedList$ListItr
?-7Q找出inner classes 和outer class
#001 Constructor cn[];
#002 cn = c.getDeclaredConstructors();
#003 for (int i = 0; i < cn.length; i++) {
#004 int md = cn[i].getModifiers();
#005 System.out.print(" " + Modifier.toString(md) + " " +
#006 cn[i].getName());
#007 Class cx[] = cn[i].getParameterTypes();
#008 System.out.print("(");
#009 for (int j = 0; j < cx.length; j++) {
#010 System.out.print(tName(cx[j].getName(), null));
#011 if (j < (cx.length - 1)) System.out.print(", ");
#012 }
#013 System.out.print(")");
#014 }
执行l果Q例Q:
public java.util.LinkedList(Collection)
public java.util.LinkedList()
?-8aQ找出所有constructors
#004 System.out.println(cn[i].toGenericString());
执行l果Q例Q:
public java.util.LinkedList(java.util.Collection<? extends E>)
public java.util.LinkedList()
?-8bQ找出所有constructors。本例在for 循环内用toGenericString()Q省事?br>
#001 Method mm[];
#002 mm = c.getDeclaredMethods();
#003 for (int i = 0; i < mm.length; i++) {
#004 int md = mm[i].getModifiers();
#005 System.out.print(" "+Modifier.toString(md)+" "+
#006 tName(mm[i].getReturnType().getName(), null)+" "+
#007 mm[i].getName());
#008 Class cx[] = mm[i].getParameterTypes();
#009 System.out.print("(");
#010 for (int j = 0; j < cx.length; j++) {
#011 System.out.print(tName(cx[j].getName(), null));
#012 if (j < (cx.length - 1)) System.out.print(", ");
#013 }
#014 System.out.print(")");
#015 }
执行l果Q例Q:
public Object get(int)
public int size()
?-9aQ找出所有methods
#004 System.out.println(mm[i].toGenericString());
public E java.util.LinkedList.get(int)
public int java.util.LinkedList.size()
?-9bQ找出所有methods。本例在for 循环内用toGenericString()Q省事?br>
#001 Field ff[];
#002 ff = c.getDeclaredFields();
#003 for (int i = 0; i < ff.length; i++) {
#004 int md = ff[i].getModifiers();
#005 System.out.println(" "+Modifier.toString(md)+" "+
#006 tName(ff[i].getType().getName(), null) +" "+
#007 ff[i].getName()+";");
#008 }
执行l果Q例Q:
private transient LinkedList$Entry header;
private transient int size;
?-10aQ找出所有fields
#004 System.out.println("G: " + ff[i].toGenericString());
private transient java.util.LinkedList.java.util.LinkedList$Entry<E> ??
java.util.LinkedList.header
private transient int java.util.LinkedList.size
?-10bQ找出所有fields。本例在for 循环内用toGenericString()Q省事?br>
扑ևclass参用Q导入)的所有classes
没有直接可用的Reflection API可以为我们找出某个class参用的所有其它classes。要获得q项信息Q必d苦工Q一步一脚印逐一记录。我们必观察所有fields的类型、所有methodsQ包括constructorsQ的参数cd和回q类型,剔除重复Q留下唯一。这正是Z么图5-2E序代码要ؓtName()指定一个hashtableQ而非一个nullQ做为第二自变量的缘故:hashtable可ؓ我们储存元素Q本例ؓ字符ԌQ又保证不重复?br>
本文讨论xQ几乎可以还原一个class的原貌(唯有methods 和ctors的定义无法取得)。接下来讨论Reflection 的另三个动态性质Q?1) q行时生成instancesQ?2) ?br>行期唤vmethodsQ?3) q行时改动fields?br>
q行时生成instances
Ʋ生成对象实体,在Reflection 动态机制中有两U作法,一个针?#8220;无自变量ctor”Q?br>一个针?#8220;带参数ctor”。图6是面?#8220;无自变量ctor”的例子。如果欲调用的是“带参数ctor“比较麻烦些Q图7是个例子Q其中不再调用Class的newInstance()Q而是调用Constructor 的newInstance()。图7首先准备一个Class[]做ؓctor的参数类型(本例指定Z个double和一个intQ,然后以此变量调用getConstructor()Q获得一个专属ctor。接下来再准备一个Object[] 做ؓctor实参|本例指定3.14159?25Q,调用上述专属ctor的newInstance()?br>
#001 Class c = Class.forName("DynTest");
#002 Object obj = null;
#003 obj = c.newInstance(); //不带自变?br>#004 System.out.println(obj);
?Q动态生?#8220;Class object 所对应之class”的对象实体;无自变量?br>
#001 Class c = Class.forName("DynTest");
#002 Class[] pTypes = new Class[] { double.class, int.class };
#003 Constructor ctor = c.getConstructor(pTypes);
#004 //指定parameter listQ便可获得特定之ctor
#005
#006 Object obj = null;
#007 Object[] arg = new Object[] {3.14159, 125}; //自变?br>#008 obj = ctor.newInstance(arg);
#009 System.out.println(obj);
?Q动态生?#8220;Class object 对应之class”的对象实体;自变量以Object[]表示?br>
q行时调用methods
q个动作和上q调?#8220;带参Cctor”相当cM。首先准备一个Class[]做ؓctor的参数类型(本例指定其中一个是StringQ另一个是HashtableQ,然后以此变量调用getMethod()Q获得特定的Method object。接下来准备一个Object[]攄自变量,然后调用上述所得之特定Method object的invoke()Q如?。知道ؓ什么烦取Method object时不需指定回返cd吗?因ؓmethod overloading机制要求signatureQv名式Q必d一Q而回q类型ƈ非signature的一个成份。换句话_只要指定了method名称和参数列Q就一定指Z一个独一无二的method?br>
#001 public String func(String s, Hashtable ht)
#002 {
#003 …System.out.println("func invoked"); return s;
#004 }
#005 public static void main(String args[])
#006 {
#007 Class c = Class.forName("Test");
#008 Class ptypes[] = new Class[2];
#009 ptypes[0] = Class.forName("java.lang.String");
#010 ptypes[1] = Class.forName("java.util.Hashtable");
#011 Method m = c.getMethod("func",ptypes);
#012 Test obj = new Test();
#013 Object args[] = new Object[2];
#014 arg[0] = new String("Hello,world");
#015 arg[1] = null;
#016 Object r = m.invoke(obj, arg);
#017 Integer rval = (String)r;
#018 System.out.println(rval);
#019 }
?Q动态唤起method
q行时变更fields内容
与先前两个动作相比,“变更field内容”L多了Q因为它不需要参数和自变量。首先调用Class的getField()q指定field名称。获得特定的Field object之后便可直接调用Field的get()和set()Q如??br>
#001 public class Test {
#002 public double d;
#003
#004 public static void main(String args[])
#005 {
#006 Class c = Class.forName("Test");
#007 Field f = c.getField("d"); //指定field 名称
#008 Test obj = new Test();
#009 System.out.println("d= " + (Double)f.get(obj));
#010 f.set(obj, 12.34);
#011 System.out.println("d= " + obj.d);
#012 }
#013 }
?Q动态变更field 内容
Java 源码改动办法
先前我曾提到Q原本想借由“改动Java标准库源?#8221;来测知Class object的生成,但由于其ctor原始设计为privateQ也是说不可能透过q个道生成Class objectQ而是由class loader负责生成Q,因此“在ctor中打印出某种信息”的企图也失M意义?br>
q里我要谈点题外话:如何修改Java标准库源码ƈ让它反应到我们的应用E序来。假设我想修改java.lang.ClassQ让它在某些情况下打印某U信息。首先必L出标准源码!当你下蝲JDK 套gq安装妥当,你会发现jdk150\src\java\lang 目录Q见?0Q之中有Class.javaQ这是我们此次行动的标准源码。备份后加以修改Q编译获得Class.class。接下来准备?class 搬移到jdk150\jre\lib\endorsedQ见?0Q?br>
q是一个十分特别的目录Qclass loader优先从该处d内含classes?jar文g——成功的条g?jar内的classes压羃路径必须和Java标准库的路径完全相同。ؓ此,我们可以刚才做出的Class.class先搬C个ؓ此目的而刻意做出来的\java\lang目录中,压羃为foo.zipQQ意命名,唯需夹带路径java\langQ,再将q个foo.zip搬到jdk150\jre\lib\endorsedq改名ؓfoo.jar。此后你的应用程序便会优先用上这里的java.lang.Class。整个过E可写成一个批处理文gQbatch fileQ,如图11Q在DOS Box中用?br>
?0QJDK1.5 安装后的目录l织。其中的endorsed 是我新徏?br>
del e:\java\lang\*.class //清理q净
del c:\jdk150\jre\lib\endorsed\foo.jar //清理q净
c:
cd c:\jdk150\src\java\lang
javac -Xlint:unchecked Class.java //~译源码
javac -Xlint:unchecked ClassLoader.java //~译另一个源码(如有必要Q?br>move *.class e:\java\lang //搬移臛_意制造的目录?br>e:
cd e:\java\lang //以下压羃至适当目录
pkzipc -add -path=root c:\jdk150\jre\lib\endorsed\foo.jar *.class
cd e:\test //q入试目录
javac -Xlint:unchecked Test.java //~译试E序
java Test //执行试E序
?1Q一个可在DOS Box中用的批处理文Ӟbatch fileQ,用以自动化java.lang.Class
的修改动作。Pkzipc(.exe)是个命o列压~工Padd和path都是其命令?br>
更多信息
以下是视野所及与本文主题相关的更多讨论。这些信息可以I补因文章幅限制而带来的不Q或带给(zhn)更多视野?br>
l "Take an in-depth look at the Java Reflection API -- Learn about the new Java 1.1 tools forfinding out information about classes", by Chuck McManis。此文章所附程序代码是本文CZE序的主要依据(本文CZE序C了更多Reflection APIsQƈ采用JDK1.5 新式的for-loop 写法Q?br>l
extends Object
implements Enumeration<Object>
string tokenizer cd许应用程序将字符串分解ؓ标记。tokenization Ҏ(gu)?StreamTokenizer cL使用的方法更单。StringTokenizer Ҏ(gu)不区分标识符、数和带引号的字W串Q它们也不识别ƈ跌注释?/p>
可以在创建时指定Q也可以Ҏ(gu)每个标记来指定分隔符Q分隔标记的字符Q集合?/p>
StringTokenizer 的实例有两种行ؓ方式Q这取决于它在创建时使用?returnDelims 标志的值是 true q是 falseQ?/p>
如果标志?falseQ则分隔W字W用来分隔标记。标记是q箋字符Q不是分隔符Q的最大序列?
如果标志?trueQ则认ؓ那些分隔W字W本w即为标记。因此标记要么是一个分隔符字符Q要么是那些q箋字符Q不是分隔符Q的最大序列?
StringTokenizer 对象在内部维护字W串中要被标记的当前位置。某些操作将此当前位|移臛_处理的字W后?/p>
通过截取字符串的一个子串来q回标记Q该字符串用于创?StringTokenizer 对象?/p>
下面是一个?tokenizer 的实例。代码如下:
StringTokenizer st = new StringTokenizer("this is a test");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
输出以下字符Ԍ
this
is
a
test
StringTokenizer 是出于兼Ҏ(gu)的原因而被保留的遗留类Q虽然在C码中q不鼓励使用它)。徏议所有寻求此功能的h使用 String ?split Ҏ(gu)?java.util.regex 包?/p>
下面的示例阐明了如何使用 String.split Ҏ(gu)字W串分解为基本标讎ͼ
String[] result = "this is a test".split("\\s");
for (int x=0; x<result.length; x++)
System.out.println(result[x]);
输出以下字符Ԍ
this
is
a
test