??xml version="1.0" encoding="utf-8" standalone="yes"?>
作者:qlampskyface
和作者联p:djb_skyface@tom.com
--------------------------------------------------------------------------------
接触多线E已l不时间了,也做了不事?但是一直觉得用h不那么顺?在debug的时?往往会比较担心在同步上出什么问?惌v"E序员最怕的是自己写的代?q句?觉得真是不假.
l于有一?我觉得是时候把q个问题弄清楚了,所?我就在网上找相关的内容看,l果竟然是找不到在我q个阶段应该看的,不是太简?是一W带q?不知所?
废了九牛二虎之力,l于差不多弄清楚?其中有不误?以前认ؓ的和真理相差甚大.惌v自己p的时?真是觉得有点?所以把它写出来,一是防止自׃后又会忘?二是l像我一L似懂非懂者留下一点可以参考的东东.
闲话说,转入正题!
--------------------------------------------------------------------------------
先从U程的创?U程的创Z共有两种形式:
--------------------------------------------------------------------------------
一U是l承自Threadc?Thread cL一个具体的c,即不是抽象类Q该cd装了U程的行为。要创徏一个线E,E序员必dZ个从 Thread cd出的新类。程序员通过覆盖 Thread ?run() 函数来完成有用的工作。用户ƈ不直接调用此函数Q而是通过调用 Thread ?start() 函数Q该函数再调?run()?br>
例如:
public class Test extends Thread{
public Test(){
}
public static void main(String args[]){
Test t1 = new Test();
Test t2 = new Test();
t1.start();
t2.start();
}
public void run(){
//do thread's things
}
}
--------------------------------------------------------------------------------
另一U是实现Runnable接口,此接口只有一个函敎ͼrun()Q此函数必须由实C此接口的cd现?br>
例如:
public class Test implements Runnable{
Thread thread1;
Thread thread2;
public Test(){
thread1 = new Thread(this,"1");
thread2 = new Thread(this,"2");
}
public static void main(String args[]){
Test t = new Test();
t.startThreads();
}
public void run(){
//do thread's things
}
public void startThreads(){
thread1.start();
thread2.start();
}
}
两种创徏方式看v来差别不?但是弄不清楚的话Q也怼你的程序弄得一团糟。两者区别有以下几点Q?/p>
1.当你想承某一其它cL,你只能用后一U方?
2.W一U因为承自Thread,只创Z自n对象,但是在数量上Q需要几个线E,得创徏几个自n对象Q第二种只创Z个自w对象,却创建几个Thread对象.而两U方法重大的区别在于此Q请你考虑Q如果你在第一U里创徏C自n对象q且start()后,你会发现好像synchronized不v作用了,已经加锁的代码块或者方法居然同时可以有几个U程q去Q而且同样一个变量,居然可以有好几个U程同时可以L改它。(例如下面的代码)q是因ؓQ在q个E序中,虽然你v了数个线E,可是你也创徏了数个对象,而且Q每个线E对应了每个对象也就是说Q每个线E更改和占有的对象都不一P所以就出现了同时有几个U程q入一个方法的现象Q其实,那也不是一个方法,而是不同对象的相同的Ҏ。所以,q时候你要加锁的话,只能方法或者变量声明ؓ静态,static加上后,你就会发玎ͼU程又能住Ҏ了,同时不可能有两个U程q入同样一个方法,那是因ؓQ现在不是每个对象都拥有一个方法了Q而是所有的对象共同拥有一个方法,q个Ҏ是静态方法?/p>
而你如果用第二种Ҏ使用U程的话Q就不会有上q的情况Q因为此Ӟ你只创徏了一个自w对象,所以,自n对象的属性和Ҏ对于U程来说是共有的?/p>
因此Q我Q最好用后一U方法来使用U程?/p>
public class mainThread extends Thread{
int i=0;
public static void main(String args[]){
mainThread m1 = new mainThread();
mainThread m2 = new mainThread();
mainThread m3 = new mainThread();
mainThread m4 = new mainThread();
mainThread m5 = new mainThread();
mainThread m6 = new mainThread();
m1.start();
m2.start();
m3.start();
m4.start();
m5.start();
m6.start();
}
public synchronized void t1(){
i=++i;
try{
Thread.sleep(500);
}
catch(Exception e){}
//每个U程都进入各自的t1()ҎQ分别打印各自的i
System.out.println(Thread.currentThread().getName()+" "+i);
}
public void run(){
synchronized(this){
while (true) {
t1();
}
}
}
}
--------------------------------------------------------------------------------
下面我们来讲synchronized?U用法吧:
1.Ҏ声明时?攑֜范围操作W?public{?之后,q回cd声明(void{?之前.即一ơ只能有一个线E进入该Ҏ,其他U程要想在此时调用该Ҏ,只能排队{?当前U程(是在synchronizedҎ内部的线E?执行完该Ҏ?别的U程才能q入.
例如:
public synchronized void synMethod() {
//Ҏ?br> }
2.Ҏ一代码块?synchronized后跟括号,括号里是变量,q样,一ơ只有一个线E进入该代码?例如:
public int synMethod(int a1){
synchronized(a1) {
//一ơ只能有一个线E进?br> }
}
3.synchronized后面括号里是一对象,此时,U程获得的是对象?例如:
public class MyThread implements Runnable {
public static void main(String args[]) {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt, "t1");
Thread t2 = new Thread(mt, "t2");
Thread t3 = new Thread(mt, "t3");
Thread t4 = new Thread(mt, "t4");
Thread t5 = new Thread(mt, "t5");
Thread t6 = new Thread(mt, "t6");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
public void run() {
synchronized (this) {
System.out.println(Thread.currentThread().getName());
}
}
}
对于3,如果U程q入,则得到对象锁,那么别的U程在该cL有对象上的Q何操作都不能q行.在对象使用锁通常是一U比较粗p的Ҏ。ؓ什么要整个对象都上锁Q而不允许其他U程短暂C用对象中其他同步Ҏ来访问共享资源?如果一个对象拥有多个资源,׃需要只Z让一个线E用其中一部分资源Q就所有线E都锁在外面。由于每个对象都有锁Q可以如下所CZ用虚拟对象来上锁Q?/p>
class FineGrainLock {
MyMemberClass x, y;
Object xlock = new Object(), ylock = new Object();
public void foo() {
synchronized(xlock) {
//access x here
}
//do something here - but don't use shared resources
synchronized(ylock) {
//access y here
}
}
public void bar() {
synchronized(this) {
//access both x and y here
}
//do something here - but don't use shared resources
}
}
4.synchronized后面括号里是c?例如:
class ArrayWithLockOrder{
private static long num_locks = 0;
private long lock_order;
private int[] arr;
public ArrayWithLockOrder(int[] a)
{
arr = a;
synchronized(ArrayWithLockOrder.class) {//-----------------------------------------q里
num_locks++; // 锁数?1?br> lock_order = num_locks; // 为此对象实例讄唯一?lock_order?br> }
}
public long lockOrder()
{
return lock_order;
}
public int[] array()
{
return arr;
}
}
class SomeClass implements Runnable
{
public int sumArrays(ArrayWithLockOrder a1,
ArrayWithLockOrder a2)
{
int value = 0;
ArrayWithLockOrder first = a1; // 保留数组引用的一?br> ArrayWithLockOrder last = a2; // 本地副本?br> int size = a1.array().length;
if (size == a2.array().length)
{
if (a1.lockOrder() > a2.lockOrder()) // 定q设|对象的锁定
{ // 序?br> first = a2;
last = a1;
}
synchronized(first) { // 按正的序锁定对象?br> synchronized(last) {
int[] arr1 = a1.array();
int[] arr2 = a2.array();
for (int i=0; i<size; i++)
value += arr1[i] + arr2[i];
}
}
}
return value;
}
public void run() {
//...
}
}
对于4,如果U程q入,则线E在该类中所有操作不能进?包括静态变量和静态方?实际?对于含有静态方法和静态变量的代码块的同步,我们通常?来加?
以上4U之间的关系Q?/p>
锁是和对象相兌的,每个对象有一把锁Qؓ了执行synchronized语句Q线E必能够获得synchronized语句中表辑ּ指定的对象的锁,一个对象只有一把锁Q被一个线E获得之后它׃再拥有这把锁Q线E在执行完synchronized语句后,获得锁交还l对象?br> 在方法前面加上synchronized修饰W即可以一个方法声明ؓ同步化方法。同步化Ҏ在执行之前获得一个锁。如果这是一个类ҎQ那么获得的锁是和声明方法的cȝ关的Classcd象的锁。如果这是一个实例方法,那么此锁是this对象的锁?/p>
--------------------------------------------------------------------------------
下面谈一谈一些常用的Ҏ:
wait(),wait(long),notify(),notifyAll(){方法是当前cȝ实例Ҏ,
wait()是持有对象锁的U程释放?
wait(long)是持有对象锁的U程释放锁时间ؓlong(毫秒)?再次获得?wait()和wait(0){h;
notify()是唤醒一个正在等待该对象锁的U程,如果{待的线E不止一?那么被唤醒的U程由jvm定;
notifyAll是唤醒所有正在等待该对象锁的U程.
在这里我也重申一?我们应该优先使用notifyAll()Ҏ,因ؓ唤醒所有线E比唤醒一个线E更Ҏ让jvm扑ֈ最适合被唤醒的U程.
对于上述Ҏ,只有在当前线E中才能使用,否则报运行时错误java.lang.IllegalMonitorStateException: current thread not owner.
--------------------------------------------------------------------------------
下面,我谈一下synchronized和wait()、notify(){的关系:
1.有synchronized的地方不一定有wait,notify
2.有wait,notify的地方必有synchronized.q是因ؓwait和notify不是属于U程c,而是每一个对象都h的方法,而且Q这两个Ҏ都和对象锁有养I有锁的地方,必有synchronized?/p>
另外Q请注意一点:如果要把notify和waitҎ攑֜一L的话Q必d调用notify后调用waitQ因为如果调用完waitQ该U程已l不是current thread了。如下例Q?/p>
/**
* Title: Jdeveloper's Java Projdect
* Description: n/a
* Copyright: Copyright (c) 2001
* Company: soho http://www.ChinaJavaWorld.com
* @author jdeveloper@21cn.com
* @version 1.0
*/
import java.lang.Runnable;
import java.lang.Thread;
public class DemoThread
implements Runnable {
public DemoThread() {
TestThread testthread1 = new TestThread(this, "1");
TestThread testthread2 = new TestThread(this, "2");
testthread2.start();
testthread1.start();
}
public static void main(String[] args) {
DemoThread demoThread1 = new DemoThread();
}
public void run() {
TestThread t = (TestThread) Thread.currentThread();
try {
if (!t.getName().equalsIgnoreCase("1")) {
synchronized (this) {
wait();
}
}
while (true) {
System.out.println("@time in thread" + t.getName() + "=" +
t.increaseTime());
if (t.getTime() % 10 == 0) {
synchronized (this) {
System.out.println("****************************************");
notify();
if (t.getTime() == 100)
break;
wait();
}
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
class TestThread
extends Thread {
private int time = 0;
public TestThread(Runnable r, String name) {
super(r, name);
}
public int getTime() {
return time;
}
public int increaseTime() {
return++time;
}
}
下面我们用生产?消费者这个例子来说明他们之间的关p?
public class test {
public static void main(String args[]) {
Semaphore s = new Semaphore(1);
Thread t1 = new Thread(s, "producer1");
Thread t2 = new Thread(s, "producer2");
Thread t3 = new Thread(s, "producer3");
Thread t4 = new Thread(s, "consumer1");
Thread t5 = new Thread(s, "consumer2");
Thread t6 = new Thread(s, "consumer3");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
class Semaphore
implements Runnable {
private int count;
public Semaphore(int n) {
this.count = n;
}
public synchronized void acquire() {
while (count == 0) {
try {
wait();
}
catch (InterruptedException e) {
//keep trying
}
}
count--;
}
public synchronized void release() {
while (count == 10) {
try {
wait();
}
catch (InterruptedException e) {
//keep trying
}
}
count++;
notifyAll(); //alert a thread that's blocking on this semaphore
}
public void run() {
while (true) {
if (Thread.currentThread().getName().substring(0,8).equalsIgnoreCase("consumer")) {
acquire();
}
else if (Thread.currentThread().getName().substring(0,8).equalsIgnoreCase("producer")) {
release();
}
System.out.println(Thread.currentThread().getName() + " " + count);
}
}
}
生者生?消费者消?一般没有冲H?但当库存??消费者要消费是不行的,但当库存Z?q里?0)?生者也不能生.请好好研M面的E序,你一定会比以前进步很?
上面的代码说明了synchronized和wait,notify没有l对的关p?在synchronized声明的方法、代码块?你完全可以不用wait,notify{方?但是,如果当线E对某一资源存在某种争用的情况下,你必适时得将U程攑օ{待或者唤?
--------------------------------------------------------------------------------
文章l于写完?基本上将我学习所得全部写出来?不过也留下些讔R?比如synchronized后是cL的深入的说明及讨?而且,文章׃自己能力有限,有些地方肯定会有错误,希望看过有何和批?请发帖在下面,我会修正该文.谢谢!
byte ——〉charQ?br>"?的gb码是Q?xc4e3 ,unicode?x4f60
String encoding = "gb2312";
byte b[] = {(byte)'\u00c4',(byte)'\u00e3'};
ByteToCharConverter converter =
ByteToCharConverter.getConverter(encoding);
char c[] = converter.convertAll(b);
for (int i = 0; i < c.length; i++) {
System.out.println(Integer.toHexString(c[i]));
}
l果是什么?0x4f60
如果encoding ="8859_1"Q结果又是什么?0x00c4,0x00e3
如果代码改ؓ
byte b[] = {(byte)'\u00c4',(byte)'\u00e3'};
ByteToCharConverter converter = ByteToCharConverter. getDefault();
char c[] = converter.convertAll(b);
for (int i = 0; i < c.length; i++) {
System.out.println(Integer.toHexString(c[i]));
}
l果又是什么?Ҏq_的编码而定?/p>
char ——〉byteQ?br>String encoding = "gb2312";
char c[] = {'\u4f60'};
CharToByteConverter converter = CharToByteConverter.getConverter(encoding);
byte b[] = converter.convertAll(c);
for (int i = 0; i < b.length; i++) {
System.out.println(Integer.toHexString(b[i]));
}
l果是什么?0x00c4,0x00e3
如果encoding ="8859_1"Q结果又是什么?0x3f
如果代码改ؓ
String encoding = "gb2312";
char c[] = {'\u4f60'};
CharToByteConverter converter = CharToByteConverter.getDefault();
byte b[] = converter.convertAll(c);
for (int i = 0; i < b.length; i++) {
System.out.println(Integer.toHexString(b[i]));
}
l果又是什么?Ҏq_的编码而定?br>很多中文问题是从这两个最单的cL生出来的。而却有很多类不直接支持把encoding输入Q这l我们带来诸多不ѝ很多程序难得用encoding了,直接用default的encodingQ这q我们UL带来了很多困难?/p>
2.utf-8
utf-8是和unicode一一对应的,其实现很?br>7位的unicode: 0 _ _ _ _ _ _ _
11位的unicode: 1 1 0 _ _ _ _ _ 1 0 _ _ _ _ _ _
16位的unicode: 1 1 1 0 _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _
21位的unicode: 1 1 1 1 0 _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _
大多数情冉|只用到16位以下的unicode:
"?的gb码是Q?xc4e3 ,unicode?x4f60
0xc4e3的二q制Q?br>1100 Q?100 Q?110 Q?011
׃只有两位我们按照两位的编码来排,但是我们发现q行不通,因ؓW7位不?因此Q返??"
0x4f60的二q制Q?br>0100 Q?111 Q?110 Q?000
我们用utf-8补齐Q变成:
1110 Q?100 Q?011 Q?101 Q?010 Q?000
e4--bd-- a0
于是q回Q?xe4,0xbd,0xa0?/p>
3.string和byte[]
string其实核心是char[],然而要把byte转化成stringQ必ȝq编码。string.length()其实是char数组的长度,如果使用不同的编码,很可能会错分Q造成散字和ؕ码?br>例如Q?br>String encoding = “”;
byte [] b={(byte)'\u00c4',(byte)'\u00e3'};
String str=new String(b,encoding);
如果encoding=8859_1Q会有两个字Q但是encoding=gb2312只有一个字q个问题在处理分|l常发生 ?/p>
4.Reader,Writer / InputStream,OutputStream
Reader和Writer核心是charQInputStream和OutputStream核心是byte。但是Reader和Writer的主要目的是要把char?写InputStream/OutputStream?br>例如Q?br>文gtest.txt只有一??字,0xc4,0xe3
String encoding = "gb2312";
InputStreamReader reader = new InputStreamReader(new FileInputStream(
"text.txt"), encoding);
char c[] = new char[10];
int length = reader.read(c);
for (int i = 0; i < length; i++) {
System.out.println(c[i]);
}
l果是什么??br>如果encoding ="8859_1"Q结果是什么???两个字符Q表CZ认识?br>反过来的例子自己做?/p>
5.我们要对java的编译器有所了解 Q?br>javac ?encoding
我们常常没有用到encodingq个参数。其实encodingq个参数对于跨^台的操作是很重要的。如果没有指定encodingQ则按照pȝ的默认encoding,gbq_上是gb2312Q英文^C是iso8859_1?br>java 的编译器实际上是调用sun.tools.javac.main的类Q对文gq行~译Q这个类有compile函数中间有一个encoding的变?- encoding的参数其实直接传lencoding变量。编译器是Ҏq个变量来读取java文g的,然后把用utf-8形式~译成class文g?br>例子代码Q?br>String str = "?;
FileWriter writer = new FileWriter("text.txt");
write.write(str);
writer.close();
如果用gb2312~译Q你会找到e4 bd a0的字D?Q?br>如果?859_1~译Q?00c4 00e3的二q制Q?br>0000Q?000 Q?100Q?100 Q?000Q?000 Q?110Q?011
因ؓ每个字符都大?位,因此?1位编码:
1100Q?001Q?000Q?100Q?100Q?011Q?010Q?011
c1-- 84-- c3-- a3
你会扑ֈc1 84 c3 a3 ?/p>
但是我们往往忽略掉这个参敎ͼ因此q样往往会有跨^台的问题Q?br>样例代码在中文^C~译Q生成zhclass
样例代码在英文^C~译Q输出enclass
(1). zhclass在中文^C执行ok,但是在英文^C不行
(2). enclass在英文^C执行ok,但是在中文^C不行
原因Q?br>(1). 在中文^C~译后,其实str在运行态的char[]?x4f60, 在中文^Cq行Qfilewriter的缺省编码是gb2312,因此 chartobyteconverter会自动用调用gb2312的converter,把str转化成byte输入到fileoutputstream 中,于是0xc4,0xe3放进了文件?br>但是如果是在英文q_下,chartobyteconverter的缺省值是8859_1, filewriter会自动调?859_1去{化str,但是他无法解释,因此他会输出"?"
(2). 在英文^C~译后,其实str在运行态的char[]?x00c4 0x00e3, 在中文^Cq行Q中文无法识别,因此会出??Q?br>在英文^CQ?x00c4-->0xc4,0x00e3->0xe3Q因?xc4,0xe3被放q了文g?/p>
6. 其它原因Q?
讄览器的昄~码Q如果response的数据是utf8~码Q显C将是ؕ码,但是q和上q原因还不一栗?/p>
7. 发生~码的地?Q?br>? 从数据库到javaE序 byte——〉char
? 从javaE序到数据库 char——〉byte
? 从文件到javaE序 byte——〉char
? 从javaE序到文?char——〉byte
? 从javaE序到页面显C?char——〉byte
? 从页面form提交数据到javaE序byte——〉char
? 从流到javaE序byte——〉char
? 从javaE序到流char——〉byte
谢志钢的解决ҎQ?br>我是使用配置qo器的Ҏ解决中文q的:
Qweb-app>
Qfilter>
Qfilter-name>RequestFilter
Qfilter-class>net.golden.uirs.util.RequestFilter
Qinit-param>
Qparam-name>charset
Qparam-value>gb2312
Q?init-param>
Q?filter>
Qfilter-mapping>
Qfilter-name>RequestFilter
Qurl-pattern>*.jsp
Q?filter-mapping>
Q?web-app>
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain fChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession();
String userId = (String) session.getAttribute("userid");
req.setCharacterEncoding(this.filterConfig.getInitParameter("charset")); // 讄字符集?
实际上是讄了byte ——〉char的encoding
try {
if (userId == null || userId.equals("")) {
if (!request.getRequestURL().toString().matches(
".*/uirs/logon/logon(Controller){0,1}\\x2Ejsp$")) {
session.invalidate();
response.sendRedirect(request.getContextPath() +
"/uirs/logon/logon.jsp");
}
}
else { // 看看是否h信息上报pȝ的权?br>if (!net.golden.uirs.util.UirsChecker.check(userId, "信息上报pȝ",
net.golden.uirs.util.UirsChecker.ACTION_DO)) {
if (!request.getRequestURL().toString().matches(
".*/uirs/logon/logon(Controller){0,1}\\x2Ejsp$")) {
response.sendRedirect(request.getContextPath() +
"/uirs/logon/logonController.jsp");
}
}
}
}
catch (Exception ex) {
response.sendRedirect(request.getContextPath() +
"/uirs/logon/logon.jsp");
}
fChain.doFilter(req, res);
}
Reflection ?Java E序开发语a的特征之一Q它允许q行中的 Java E序对自w进行检查,或者说“自审”Qƈ能直接操作程序的内部属性。例如,使用它能获得 Java cM各成员的名称q显C出来?/p>
Java 的这一能力在实际应用中也许用得不是很多Q但是在其它的程序设计语a中根本就不存在这一Ҏ。例如,Pascal、C 或?C++ 中就没有办法在程序中获得函数定义相关的信息?/p>
JavaBean ?reflection 的实际应用之一Q它能让一些工具可视化的操作Y件组件。这些工具通过 reflection 动态的载入q取?Java lg(c? 的属性?/p>
一个简单的例子
考虑下面q个单的例子Q让我们看看 reflection 是如何工作的?/p>
import java.lang.reflect.*;
public class DumpMethods {
public static void main(String args[]) {
try {
Class c = Class.forName(args[0]);
Method m[] = c.getDeclaredMethods();
for (int i = 0; i < m.length; i++)
System.out.println(m[i].toString());
}
catch (Throwable e) {
System.err.println(e);
}
}
}
按如下语句执行:
java DumpMethods java.util.Stack
它的l果输出为:
public java.lang.Object java.util.Stack.push(java.lang.Object)
public synchronized java.lang.Object java.util.Stack.pop()
public synchronized java.lang.Object java.util.Stack.peek()
public boolean java.util.Stack.empty()
public synchronized int java.util.Stack.search(java.lang.Object)
q样列Zjava.util.Stack cȝ各方法名以及它们的限制符和返回类型?/p>
q个E序使用 Class.forName 载入指定的类Q然后调?getDeclaredMethods 来获取这个类中定义了的方法列表。java.lang.reflect.Methods 是用来描q某个类中单个方法的一个类?/p>
开始?Reflection
用于 reflection 的类Q如 MethodQ可以在 java.lang.relfect 包中扑ֈ。用这些类的时候必要遵@三个步骤Q第一步是获得你想操作的类?java.lang.Class 对象。在q行中的 Java E序中,?java.lang.Class cL描述cd接口{?/p>
下面是获得一?Class 对象的方法之一Q?/p>
Class c = Class.forName("java.lang.String");
q条语句得到一?String cȝcd象。还有另一U方法,如下面的语句Q?/p>
Class c = int.class;
或?/p>
Class c = Integer.TYPE;
它们可获得基本类型的cM息。其中后一U方法中讉K的是基本cd的封装类 (?Integer) 中预先定义好?TYPE 字段?/p>
W二步是调用诸如 getDeclaredMethods 的方法,以取得该cM定义的所有方法的列表?/p>
一旦取得这个信息,可以进行第三步了——?reflection API 来操作这些信息,如下面这D代码:
Class c = Class.forName("java.lang.String");
Method m[] = c.getDeclaredMethods();
System.out.println(m[0].toString());
它将以文本方式打印出 String 中定义的W一个方法的原型?/p>
在下面的例子中,q三个步骤将Z?reflection 处理Ҏ应用E序提供例证?/p>
模拟 instanceof 操作W?/p>
得到cM息之后,通常下一个步骤就是解军_?Class 对象的一些基本的问题。例如,Class.isInstance Ҏ可以用于模拟 instanceof 操作W:
class A {}
public class instance1 {
public static void main(String args[])
{
try {
Class cls = Class.forName("A");
boolean b1
= cls.isInstance(new Integer(37));
System.out.println(b1);
boolean b2 = cls.isInstance(new A());
System.out.println(b2);
}
catch (Throwable e) {
System.err.println(e);
}
}
}
在这个例子中创徏了一?A cȝ Class 对象Q然后检查一些对象是否是 A 的实例。Integer(37) 不是Q但 new A() 是?/p>
扑ևcȝҎ
扑և一个类中定义了些什么方法,q是一个非常有价g非常基础?reflection 用法。下面的代码实Cq一用法Q?/p>
import java.lang.reflect.*;
public class method1 {
private int f1(Object p, int x) throws NullPointerException
{
if (p == null)
throw new NullPointerException();
return x;
}
public static void main(String args[])
{
try {
Class cls = Class.forName("method1");
Method methlist[]
= cls.getDeclaredMethods();
for (int i = 0; i < methlist.length;
i++) {
Method m = methlist[i];
System.out.println("name
= " + m.getName());
System.out.println("decl class = " +
m.getDeclaringClass());
Class pvec[] = m.getParameterTypes();
for (int j = 0; j < pvec.length; j++)
System.out.println("
param #" + j + " " + pvec[j]);
Class evec[] = m.getExceptionTypes();
for (int j = 0; j < evec.length; j++)
System.out.println("exc #" + j
+ " " + evec[j]);
System.out.println("return type = " +
m.getReturnType());
System.out.println("-----");
}
}
catch (Throwable e) {
System.err.println(e);
}
}
}
q个E序首先取得 method1 cȝ描述Q然后调?getDeclaredMethods 来获取一pd?Method 对象Q它们分别描qC定义在类中的每一个方法,包括 public Ҏ、protected Ҏ、package Ҏ?private Ҏ{。如果你在程序中使用 getMethods 来代?getDeclaredMethodsQ你q能获得l承来的各个Ҏ的信息?/p>
取得?Method 对象列表之后Q要昄q些Ҏ的参数类型、异常类型和q回值类型等׃难了。这些类型是基本cdq是cȝ型,都可以由描述cȝ对象按顺序给出?/p>
输出的结果如下:
name = f1
decl class = class method1
param #0 class java.lang.Object
param #1 int
exc #0 class java.lang.NullPointerException
return type = int
-----
name = main
decl class = class method1
param #0 class [Ljava.lang.String;
return type = void
-----
获取构造器信息
获取cL造器的用法与上述获取Ҏ的用法类|如:
import java.lang.reflect.*;
public class constructor1 {
public constructor1()
{
}
protected constructor1(int i, double d)
{
}
public static void main(String args[])
{
try {
Class cls = Class.forName("constructor1");
Constructor ctorlist[]
= cls.getDeclaredConstructors();
for (int i = 0; i < ctorlist.length; i++) {
Constructor ct = ctorlist[i];
System.out.println("name
= " + ct.getName());
System.out.println("decl class = " +
ct.getDeclaringClass());
Class pvec[] = ct.getParameterTypes();
for (int j = 0; j < pvec.length; j++)
System.out.println("param #"
+ j + " " + pvec[j]);
Class evec[] = ct.getExceptionTypes();
for (int j = 0; j < evec.length; j++)
System.out.println(
"exc #" + j + " " + evec[j]);
System.out.println("-----");
}
}
catch (Throwable e) {
System.err.println(e);
}
}
}
q个例子中没能获得返回类型的相关信息Q那是因为构造器没有q回cd?/p>
q个E序q行的结果是Q?/p>
name = constructor1
decl class = class constructor1
-----
name = constructor1
decl class = class constructor1
param #0 int
param #1 double
-----
获取cȝ字段(?
扑և一个类中定义了哪些数据字段也是可能的,下面的代码就在干q个事情Q?/p>
import java.lang.reflect.*;
public class field1 {
private double d;
public static final int i = 37;
String s = "testing";
public static void main(String args[])
{
try {
Class cls = Class.forName("field1");
Field fieldlist[]
= cls.getDeclaredFields();
for (int i
= 0; i < fieldlist.length; i++) {
Field fld = fieldlist[i];
System.out.println("name
= " + fld.getName());
System.out.println("decl class = " +
fld.getDeclaringClass());
System.out.println("type
= " + fld.getType());
int mod = fld.getModifiers();
System.out.println("modifiers = " +
Modifier.toString(mod));
System.out.println("-----");
}
}
catch (Throwable e) {
System.err.println(e);
}
}
}
q个例子和前面那个例子非常相伹{例中用了一个新东西 ModifierQ它也是一?reflection c,用来描述字段成员的修饰语Q如“private int”。这些修饰语自n由整数描qͼ而且使用 Modifier.toString 来返回以“官方”序排列的字W串描述 (?#8220;static”?#8220;final”之前)。这个程序的输出是:
name = d
decl class = class field1
type = double
modifiers = private
-----
name = i
decl class = class field1
type = int
modifiers = public static final
-----
name = s
decl class = class field1
type = class java.lang.String
modifiers =
-----
和获取方法的情况一下,获取字段的时候也可以只取得在当前cMx了的字段信息 (getDeclaredFields)Q或者也可以取得父类中定义的字段 (getFields) ?/p>
ҎҎ的名U来执行Ҏ
文本到这里,所丄例子无一例外都与如何获取cȝ信息有关。我们也可以?reflection 来做一些其它的事情Q比如执行一个指定了名称的方法。下面的CZ演示了这一操作Q?/p>
import java.lang.reflect.*;
public class method2 {
public int add(int a, int b)
{
return a + b;
}
public static void main(String args[])
{
try {
Class cls = Class.forName("method2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Method meth = cls.getMethod(
"add", partypes);
method2 methobj = new method2();
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj
= meth.invoke(methobj, arglist);
Integer retval = (Integer)retobj;
System.out.println(retval.intValue());
}
catch (Throwable e) {
System.err.println(e);
}
}
}
假如一个程序在执行的某处的时候才知道需要执行某个方法,q个Ҏ的名U是在程序的q行q程中指定的 (例如QJavaBean 开发环境中׃做这L?Q那么上面的E序演示了如何做到?/p>
上例中,getMethod 用于查找一个具有两个整型参C名ؓ add 的方法。找到该Ҏq创Z相应?Method 对象之后Q在正确的对象实例中执行它。执行该Ҏ的时候,需要提供一个参数列表,q在上例中是分别包装了整?37 ?47 的两?Integer 对象。执行方法的q回的同h一?Integer 对象Q它装了返回?84?/p>
创徏新的对象
对于构造器Q则不能像执行方法那栯行,因ؓ执行一个构造器意味着创徏了一个新的对?(准确的说Q创Z个对象的q程包括分配内存和构造对?。所以,与上例最怼的例子如下:
import java.lang.reflect.*;
public class constructor2 {
public constructor2()
{
}
public constructor2(int a, int b)
{
System.out.println(
"a = " + a + " b = " + b);
}
public static void main(String args[])
{
try {
Class cls = Class.forName("constructor2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Constructor ct
= cls.getConstructor(partypes);
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = ct.newInstance(arglist);
}
catch (Throwable e) {
System.err.println(e);
}
}
}
Ҏ指定的参数类型找到相应的构造函数ƈ执行它,以创Z个新的对象实例。用这U方法可以在E序q行时动态地创徏对象Q而不是在~译的时候创建对象,q一炚w常有价倹{?/p>
改变字段(?的?/p>
reflection 的还有一个用处就是改变对象数据字D늚倹{reflection 可以从正在运行的E序中根据名U找到对象的字段q改变它Q下面的例子可以说明q一点:
import java.lang.reflect.*;
public class field2 {
public double d;
public static void main(String args[])
{
try {
Class cls = Class.forName("field2");
Field fld = cls.getField("d");
field2 f2obj = new field2();
System.out.println("d = " + f2obj.d);
fld.setDouble(f2obj, 12.34);
System.out.println("d = " + f2obj.d);
}
catch (Throwable e) {
System.err.println(e);
}
}
}
q个例子中,字段 d 的D变ؓ?12.34?/p>
使用数组
本文介绍?reflection 的最后一U用法是创徏的操作数l。数l在 Java 语言中是一U特D的cȝ型,一个数l的引用可以赋给 Object 引用。观察下面的例子看看数组是怎么工作的:
import java.lang.reflect.*;
public class array1 {
public static void main(String args[])
{
try {
Class cls = Class.forName(
"java.lang.String");
Object arr = Array.newInstance(cls, 10);
Array.set(arr, 5, "this is a test");
String s = (String)Array.get(arr, 5);
System.out.println(s);
}
catch (Throwable e) {
System.err.println(e);
}
}
}
例中创徏?10 个单位长度的 String 数组QؓW?5 个位|的字符串赋了|最后将q个字符串从数组中取得ƈ打印了出来?/p>
下面q段代码提供了一个更复杂的例子:
import java.lang.reflect.*;
public class array2 {
public static void main(String args[])
{
int dims[] = new int[]{5, 10, 15};
Object arr
= Array.newInstance(Integer.TYPE, dims);
Object arrobj = Array.get(arr, 3);
Class cls =
arrobj.getClass().getComponentType();
System.out.println(cls);
arrobj = Array.get(arrobj, 5);
Array.setInt(arrobj, 10, 37);
int arrcast[][][] = (int[][][])arr;
System.out.println(arrcast[3][5][10]);
}
}
例中创徏了一?5 x 10 x 15 的整型数l,qؓ处于 [3][5][10] 的元素赋了gؓ 37。注意,多维数组实际上就是数l的数组Q例如,W一?Array.get 之后Qarrobj 是一?10 x 15 的数l。进而取得其中的一个元素,即长度ؓ 15 的数l,q?Array.setInt 为它的第 10 个元素赋倹{?/p>
注意创徏数组时的cd是动态的Q在~译时ƈ不知道其cd?/p>
结
Java reflection 非常有用Q它使类和数据结构能按名U动态检索相关信息,q允许在q行着的程序中操作q些信息。Java 的这一Ҏ非常强大,q且是其它一些常语言Q如 C、C++、Fortran 或?Pascal {都不具备的?nbsp;