﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-&amp;豪-文章分类-Java</title><link>http://www.cppblog.com/qywyh/category/5408.html</link><description>豪-&gt;blog</description><language>zh-cn</language><lastBuildDate>Mon, 19 May 2008 19:10:48 GMT</lastBuildDate><pubDate>Mon, 19 May 2008 19:10:48 GMT</pubDate><ttl>60</ttl><item><title>【转】Java 多线程入门大全</title><link>http://www.cppblog.com/qywyh/articles/39317.html</link><dc:creator>豪</dc:creator><author>豪</author><pubDate>Sat, 22 Dec 2007 19:25:00 GMT</pubDate><guid>http://www.cppblog.com/qywyh/articles/39317.html</guid><wfw:comment>http://www.cppblog.com/qywyh/comments/39317.html</wfw:comment><comments>http://www.cppblog.com/qywyh/articles/39317.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/qywyh/comments/commentRss/39317.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/qywyh/services/trackbacks/39317.html</trackback:ping><description><![CDATA[<p>Java 多线程入门大全<br>2007-03-15 14:18:41 / 个人分类：java </p>
<p><br>作者：qlampskyface </p>
<p>和作者联系：<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#100;&#106;&#98;&#95;&#115;&#107;&#121;&#102;&#97;&#99;&#101;&#64;&#116;&#111;&#109;&#46;&#99;&#111;&#109;">djb_skyface@tom.com</a></p>
<p>&nbsp;</p>
<p>--------------------------------------------------------------------------------</p>
<p><br>&nbsp;&nbsp;&nbsp; 接触多线程已经不少时间了,也做了不少事情,但是一直觉得用起来不那么顺手,在debug的时候,往往会比较担心在同步上出什么问题,想起"程序员最怕的是自己写的代码"这句话,觉得真是不假.<br>&nbsp;&nbsp;&nbsp; 终于有一天,我觉得是时候把这个问题弄清楚了,所以,我就在网上找相关的内容看,结果竟然是找不到在我这个阶段应该看的,不是太简单,就是一笔带过,不知所云.<br>&nbsp;&nbsp;&nbsp; 废了九牛二虎之力,终于差不多弄清楚了,其中有不少误区,以前认为的和真理相差甚大.想起自己花费的时间,真是觉得有点多,所以把它写出来,一是防止自己以后又会忘掉,二是给像我一样的似懂非懂者留下一点可以参考的东东.<br>&nbsp;&nbsp;&nbsp; 闲话少说,转入正题!</p>
<p>&nbsp;</p>
<p>--------------------------------------------------------------------------------</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; 先从线程的创建说起.线程的创建一共有两种形式:</p>
<p>&nbsp;</p>
<p>--------------------------------------------------------------------------------</p>
<p>&nbsp;&nbsp;&nbsp; 一种是继承自Thread类.Thread 类是一个具体的类，即不是抽象类，该类封装了线程的行为。要创建一个线程，程序员必须创建一个从 Thread 类导出的新类。程序员通过覆盖 Thread 的 run() 函数来完成有用的工作。用户并不直接调用此函数；而是通过调用 Thread 的 start() 函数，该函数再调用 run()。<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 例如: </p>
<p>&nbsp;&nbsp;&nbsp; public class Test extends Thread{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Test(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void main(String args[]){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Test t1 = new Test();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Test t2 = new Test();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t1.start();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t2.start();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //do thread's things<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>--------------------------------------------------------------------------------</p>
<p>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 另一种是实现Runnable接口,此接口只有一个函数，run()，此函数必须由实现了此接口的类实现。<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 例如: </p>
<p>&nbsp;&nbsp;&nbsp; public class Test implements Runnable{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread thread1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread thread2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Test(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread1 = new Thread(this,"1");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread2 = new Thread(this,"2");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void main(String args[]){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Test t = new Test();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.startThreads();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //do thread's things<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void startThreads(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread1.start();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread2.start();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; 两种创建方式看起来差别不大,但是弄不清楚的话，也许会将你的程序弄得一团糟。两者区别有以下几点：</p>
<p>1.当你想继承某一其它类时,你只能用后一种方式.</p>
<p>2.第一种因为继承自Thread,只创建了自身对象,但是在数量上，需要几个线程，就得创建几个自身对象；第二种只创建一个自身对象，却创建几个Thread对象.而两种方法重大的区别就在于此，请你考虑：如果你在第一种里创建数个自身对象并且start()后，你会发现好像synchronized不起作用了，已经加锁的代码块或者方法居然同时可以有几个线程进去，而且同样一个变量，居然可以有好几个线程同时可以去更改它。（例如下面的代码）这是因为，在这个程序中，虽然你起了数个线程，可是你也创建了数个对象，而且，每个线程对应了每个对象也就是说，每个线程更改和占有的对象都不一样，所以就出现了同时有几个线程进入一个方法的现象，其实，那也不是一个方法，而是不同对象的相同的方法。所以，这时候你要加锁的话，只能将方法或者变量声明为静态，将static加上后，你就会发现，线程又能管住方法了，同时不可能有两个线程进入同样一个方法，那是因为，现在不是每个对象都拥有一个方法了，而是所有的对象共同拥有一个方法，这个方法就是静态方法。</p>
<p>&nbsp;&nbsp;&nbsp; 而你如果用第二种方法使用线程的话，就不会有上述的情况，因为此时，你只创建了一个自身对象，所以，自身对象的属性和方法对于线程来说是共有的。</p>
<p>&nbsp;&nbsp;&nbsp; 因此，我建议，最好用后一种方法来使用线程。</p>
<p>public class mainThread extends Thread{<br>&nbsp; int i=0;<br>&nbsp; public static void main(String args[]){<br>&nbsp;&nbsp;&nbsp; mainThread m1 = new mainThread();<br>&nbsp;&nbsp;&nbsp; mainThread m2 = new mainThread();<br>&nbsp;&nbsp;&nbsp; mainThread m3 = new mainThread();<br>&nbsp;&nbsp;&nbsp; mainThread m4 = new mainThread();<br>&nbsp;&nbsp;&nbsp; mainThread m5 = new mainThread();<br>&nbsp;&nbsp;&nbsp; mainThread m6 = new mainThread();<br>&nbsp;&nbsp;&nbsp; m1.start();<br>&nbsp;&nbsp;&nbsp; m2.start();<br>&nbsp;&nbsp;&nbsp; m3.start();<br>&nbsp;&nbsp;&nbsp; m4.start();<br>&nbsp;&nbsp;&nbsp; m5.start();<br>&nbsp;&nbsp;&nbsp; m6.start();<br>&nbsp; }<br>&nbsp; public synchronized void t1(){<br>&nbsp;&nbsp;&nbsp; i=++i;<br>&nbsp;&nbsp;&nbsp; try{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.sleep(500);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; catch(Exception e){}<br>&nbsp;&nbsp;&nbsp; //每个线程都进入各自的t1()方法，分别打印各自的i<br>&nbsp;&nbsp;&nbsp; System.out.println(Thread.currentThread().getName()+" "+i);<br>&nbsp; }<br>&nbsp; public void run(){<br>&nbsp;&nbsp;&nbsp; synchronized(this){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (true) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t1();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; }<br>}</p>
<p>&nbsp;</p>
<p><br>--------------------------------------------------------------------------------</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; 下面我们来讲synchronized的4种用法吧:</p>
<p>&nbsp;&nbsp;&nbsp; 1.方法声明时使用,放在范围操作符(public等)之后,返回类型声明(void等)之前.即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入.<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 例如:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public synchronized void synMethod() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //方法体<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; 2.对某一代码块使用,synchronized后跟括号,括号里是变量,这样,一次只有一个线程进入该代码块.例如:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public int synMethod(int a1){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; synchronized(a1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //一次只能有一个线程进入<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; 3.synchronized后面括号里是一对象,此时,线程获得的是对象锁.例如:</p>
<p>public class MyThread implements Runnable {<br>&nbsp; public static void main(String args[]) {<br>&nbsp;&nbsp;&nbsp; MyThread mt = new MyThread();<br>&nbsp;&nbsp;&nbsp; Thread t1 = new Thread(mt, "t1");<br>&nbsp;&nbsp;&nbsp; Thread t2 = new Thread(mt, "t2");<br>&nbsp;&nbsp;&nbsp; Thread t3 = new Thread(mt, "t3");<br>&nbsp;&nbsp;&nbsp; Thread t4 = new Thread(mt, "t4");<br>&nbsp;&nbsp;&nbsp; Thread t5 = new Thread(mt, "t5");<br>&nbsp;&nbsp;&nbsp; Thread t6 = new Thread(mt, "t6");<br>&nbsp;&nbsp;&nbsp; t1.start();<br>&nbsp;&nbsp;&nbsp; t2.start();<br>&nbsp;&nbsp;&nbsp; t3.start();<br>&nbsp;&nbsp;&nbsp; t4.start();<br>&nbsp;&nbsp;&nbsp; t5.start();<br>&nbsp;&nbsp;&nbsp; t6.start();<br>&nbsp; }</p>
<p>&nbsp; public void run() {<br>&nbsp;&nbsp;&nbsp; synchronized (this) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(Thread.currentThread().getName());<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; }<br>} </p>
<p><br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 对于3,如果线程进入,则得到对象锁,那么别的线程在该类所有对象上的任何操作都不能进行.在对象级使用锁通常是一种比较粗糙的方法。为什么要将整个对象都上锁，而不允许其他线程短暂地使用对象中其他同步方法来访问共享资源？如果一个对象拥有多个资源，就不需要只为了让一个线程使用其中一部分资源，就将所有线程都锁在外面。由于每个对象都有锁，可以如下所示使用虚拟对象来上锁：</p>
<p>class FineGrainLock {</p>
<p>&nbsp;&nbsp; MyMemberClass x, y;<br>&nbsp;&nbsp; Object xlock = new Object(), ylock = new Object();</p>
<p>&nbsp;&nbsp; public void foo() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; synchronized(xlock) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //access x here<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //do something here - but don't use shared resources</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; synchronized(ylock) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //access y here<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp; public void bar() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; synchronized(this) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //access both x and y here<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //do something here - but don't use shared resources<br>&nbsp;&nbsp; }<br>}</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; 4.synchronized后面括号里是类.例如:</p>
<p>class ArrayWithLockOrder{<br>&nbsp; private static long num_locks = 0;<br>&nbsp; private long lock_order;<br>&nbsp; private int[] arr;</p>
<p>&nbsp; public ArrayWithLockOrder(int[] a)<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp; arr = a;<br>&nbsp;&nbsp;&nbsp; synchronized(ArrayWithLockOrder.class) {//-----------------------------------------这里<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; num_locks++;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 锁数加 1。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lock_order = num_locks;&nbsp; // 为此对象实例设置唯一的 lock_order。<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; }<br>&nbsp; public long lockOrder()<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp; return lock_order;<br>&nbsp; }<br>&nbsp; public int[] array()<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp; return arr;<br>&nbsp; }<br>}</p>
<p>class SomeClass implements Runnable<br>{<br>&nbsp; public int sumArrays(ArrayWithLockOrder a1,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ArrayWithLockOrder a2)<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp; int value = 0;<br>&nbsp;&nbsp;&nbsp; ArrayWithLockOrder first = a1;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 保留数组引用的一个<br>&nbsp;&nbsp;&nbsp; ArrayWithLockOrder last = a2;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 本地副本。<br>&nbsp;&nbsp;&nbsp; int size = a1.array().length;<br>&nbsp;&nbsp;&nbsp; if (size == a2.array().length)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (a1.lockOrder() &gt; a2.lockOrder())&nbsp; // 确定并设置对象的锁定<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 顺序。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; first = a2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; last = a1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; synchronized(first) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 按正确的顺序锁定对象。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; synchronized(last) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int[] arr1 = a1.array();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int[] arr2 = a2.array();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i=0; i&lt;size; i++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value += arr1[i] + arr2[i];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; return value;<br>&nbsp; }<br>&nbsp; public void run() {<br>&nbsp;&nbsp;&nbsp; //...<br>&nbsp; }<br>}</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; 对于4,如果线程进入,则线程在该类中所有操作不能进行,包括静态变量和静态方法,实际上,对于含有静态方法和静态变量的代码块的同步,我们通常用4来加锁.</p>
<p>以上4种之间的关系：</p>
<p>&nbsp;&nbsp;&nbsp; 锁是和对象相关联的，每个对象有一把锁，为了执行synchronized语句，线程必须能够获得synchronized语句中表达式指定的对象的锁，一个对象只有一把锁，被一个线程获得之后它就不再拥有这把锁，线程在执行完synchronized语句后，将获得锁交还给对象。<br>&nbsp;&nbsp;&nbsp; 在方法前面加上synchronized修饰符即可以将一个方法声明为同步化方法。同步化方法在执行之前获得一个锁。如果这是一个类方法，那么获得的锁是和声明方法的类相关的Class类对象的锁。如果这是一个实例方法，那么此锁是this对象的锁。</p>
<p>&nbsp;</p>
<p><br>--------------------------------------------------------------------------------</p>
<p><br>&nbsp; 下面谈一谈一些常用的方法:</p>
<p>&nbsp; wait(),wait(long),notify(),notifyAll()等方法是当前类的实例方法,<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait()是使持有对象锁的线程释放锁;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait(long)是使持有对象锁的线程释放锁时间为long(毫秒)后,再次获得锁,wait()和wait(0)等价;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; notify()是唤醒一个正在等待该对象锁的线程,如果等待的线程不止一个,那么被唤醒的线程由jvm确定;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; notifyAll是唤醒所有正在等待该对象锁的线程.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在这里我也重申一下,我们应该优先使用notifyAll()方法,因为唤醒所有线程比唤醒一个线程更容易让jvm找到最适合被唤醒的线程.</p>
<p>&nbsp;&nbsp;&nbsp; 对于上述方法,只有在当前线程中才能使用,否则报运行时错误java.lang.IllegalMonitorStateException: current thread not owner.</p>
<p>&nbsp;</p>
<p>--------------------------------------------------------------------------------</p>
<p><br>&nbsp;&nbsp;&nbsp; 下面,我谈一下synchronized和wait()、notify()等的关系:</p>
<p>1.有synchronized的地方不一定有wait,notify</p>
<p>2.有wait,notify的地方必有synchronized.这是因为wait和notify不是属于线程类，而是每一个对象都具有的方法，而且，这两个方法都和对象锁有关，有锁的地方，必有synchronized。</p>
<p>另外，请注意一点：如果要把notify和wait方法放在一起用的话，必须先调用notify后调用wait，因为如果调用完wait，该线程就已经不是current thread了。如下例：</p>
<p>/**<br>&nbsp;* Title:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Jdeveloper's Java Projdect<br>&nbsp;* Description:&nbsp; n/a<br>&nbsp;* Copyright:&nbsp;&nbsp;&nbsp; Copyright (c) 2001<br>&nbsp;* Company:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; soho&nbsp; <a href="http://www.chinajavaworld.com/">http://www.ChinaJavaWorld.com</a><br>&nbsp;* @author <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#106;&#100;&#101;&#118;&#101;&#108;&#111;&#112;&#101;&#114;&#64;&#50;&#49;&#99;&#110;&#46;&#99;&#111;&#109;">jdeveloper@21cn.com</a><br>&nbsp;* @version 1.0<br>&nbsp;*/<br>import java.lang.Runnable;<br>import java.lang.Thread;</p>
<p>public class DemoThread<br>&nbsp;&nbsp;&nbsp; implements Runnable {</p>
<p>&nbsp; public DemoThread() {<br>&nbsp;&nbsp;&nbsp; TestThread testthread1 = new TestThread(this, "1");<br>&nbsp;&nbsp;&nbsp; TestThread testthread2 = new TestThread(this, "2");</p>
<p>&nbsp;&nbsp;&nbsp; testthread2.start();<br>&nbsp;&nbsp;&nbsp; testthread1.start();</p>
<p>&nbsp; }</p>
<p>&nbsp; public static void main(String[] args) {<br>&nbsp;&nbsp;&nbsp; DemoThread demoThread1 = new DemoThread();</p>
<p>&nbsp; }</p>
<p>&nbsp; public void run() {</p>
<p>&nbsp;&nbsp;&nbsp; TestThread t = (TestThread) Thread.currentThread();<br>&nbsp;&nbsp;&nbsp; try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!t.getName().equalsIgnoreCase("1")) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; synchronized (this) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (true) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("@time in thread" + t.getName() + "=" +<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.increaseTime());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (t.getTime() % 10 == 0) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; synchronized (this) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("****************************************");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; notify();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (t.getTime() == 100)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; catch (Exception e) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; }</p>
<p>}</p>
<p>class TestThread<br>&nbsp;&nbsp;&nbsp; extends Thread {<br>&nbsp; private int time = 0;<br>&nbsp; public TestThread(Runnable r, String name) {<br>&nbsp;&nbsp;&nbsp; super(r, name);<br>&nbsp; }</p>
<p>&nbsp; public int getTime() {<br>&nbsp;&nbsp;&nbsp; return time;<br>&nbsp; }</p>
<p>&nbsp; public int increaseTime() {<br>&nbsp;&nbsp;&nbsp; return++time;<br>&nbsp; }</p>
<p>}</p>
<p>&nbsp;&nbsp;&nbsp; 下面我们用生产者/消费者这个例子来说明他们之间的关系:</p>
<p>&nbsp;&nbsp;&nbsp; public class test {<br>&nbsp; public static void main(String args[]) {<br>&nbsp;&nbsp;&nbsp; Semaphore s = new Semaphore(1);<br>&nbsp;&nbsp;&nbsp; Thread t1 = new Thread(s, "producer1");<br>&nbsp;&nbsp;&nbsp; Thread t2 = new Thread(s, "producer2");<br>&nbsp;&nbsp;&nbsp; Thread t3 = new Thread(s, "producer3");<br>&nbsp;&nbsp;&nbsp; Thread t4 = new Thread(s, "consumer1");<br>&nbsp;&nbsp;&nbsp; Thread t5 = new Thread(s, "consumer2");<br>&nbsp;&nbsp;&nbsp; Thread t6 = new Thread(s, "consumer3");<br>&nbsp;&nbsp;&nbsp; t1.start();<br>&nbsp;&nbsp;&nbsp; t2.start();<br>&nbsp;&nbsp;&nbsp; t3.start();<br>&nbsp;&nbsp;&nbsp; t4.start();<br>&nbsp;&nbsp;&nbsp; t5.start();<br>&nbsp;&nbsp;&nbsp; t6.start();<br>&nbsp; }<br>}</p>
<p>class Semaphore<br>&nbsp;&nbsp;&nbsp; implements Runnable {<br>&nbsp; private int count;<br>&nbsp; public Semaphore(int n) {<br>&nbsp;&nbsp;&nbsp; this.count = n;<br>&nbsp; }</p>
<p>&nbsp; public synchronized void acquire() {<br>&nbsp;&nbsp;&nbsp; while (count == 0) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (InterruptedException e) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //keep trying<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; count--;<br>&nbsp; }</p>
<p>&nbsp; public synchronized void release() {<br>&nbsp;&nbsp;&nbsp; while (count == 10) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wait();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (InterruptedException e) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //keep trying<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; count++;<br>&nbsp;&nbsp;&nbsp; notifyAll(); //alert a thread that's blocking on this semaphore<br>&nbsp; }</p>
<p>&nbsp; public void run() {<br>&nbsp;&nbsp;&nbsp; while (true) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (Thread.currentThread().getName().substring(0,8).equalsIgnoreCase("consumer")) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; acquire();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (Thread.currentThread().getName().substring(0,8).equalsIgnoreCase("producer")) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; release();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(Thread.currentThread().getName() + " " + count);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; }<br>}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 生产者生产,消费者消费,一般没有冲突,但当库存为0时,消费者要消费是不行的,但当库存为上限(这里是10)时,生产者也不能生产.请好好研读上面的程序,你一定会比以前进步很多.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上面的代码说明了synchronized和wait,notify没有绝对的关系,在synchronized声明的方法、代码块中,你完全可以不用wait,notify等方法,但是,如果当线程对某一资源存在某种争用的情况下,你必须适时得将线程放入等待或者唤醒.</p>
<p>&nbsp;</p>
<p>--------------------------------------------------------------------------------</p>
<p><br>文章终于写完了,基本上将我学习所得全部写出来了,不过也留下些许遗憾,比如synchronized后是类时的深入的说明及讨论.而且,文章由于自己能力有限,有些地方肯定会有错误,希望看过有何建议和批评,请发帖在下面,我会修正该文.谢谢!<br>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/qywyh/aggbug/39317.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/qywyh/" target="_blank">豪</a> 2007-12-23 03:25 <a href="http://www.cppblog.com/qywyh/articles/39317.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】Java与XML读写之DOM篇</title><link>http://www.cppblog.com/qywyh/articles/39318.html</link><dc:creator>豪</dc:creator><author>豪</author><pubDate>Sat, 22 Dec 2007 19:25:00 GMT</pubDate><guid>http://www.cppblog.com/qywyh/articles/39318.html</guid><wfw:comment>http://www.cppblog.com/qywyh/comments/39318.html</wfw:comment><comments>http://www.cppblog.com/qywyh/articles/39318.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/qywyh/comments/commentRss/39318.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/qywyh/services/trackbacks/39318.html</trackback:ping><description><![CDATA[Java与XML读写之DOM篇<br>DOM<br>初步<br>DOM是Document Object Model的缩写，即文档对象模型。前面说过，XML将数据组织为一颗树，<br>所以DOM就是对这颗树的一个对象描叙。通俗的说，就是通过解析XML文档，为XML文档在<br>逻辑上建立一个树模型，树的节点是一个个对象。我们通过存取这些对象就能够存取XML文档<br>的内容。<br>下面我们来看一个简单的例子，看看在DOM中，我们是如何来操作一个XML文档的。<br>这是一个XML文档，也是我们要操作的对象：<br>下面，我们需要把这个文档的内容解析到一个个的Java对象中去供程序使用，利用JAXP，我们<br>只需几行代码就能做到这一点。首先，我们需要建立一个解析器工厂，以利用这个工厂来获得<br>一个具体的解析器对象：<br>我们在这里使用DocumentBuilderFacotry的目的是为了创建与具体解析器无关的程序，当<br>DocumentBuilderFactory类的静态方法newInstance()被调用时，它根据一个系统变量来决定具体<br>使用哪一个解析器。又因为所有的解析器都服从于JAXP所定义的接口，所以无论具体使用哪一<br>个解析器，代码都是一样的。所以当在不同的解析器之间进行切换时，只需要更改系统变量的<br>值，而不用更改任何代码。这就是工厂所带来的好处。这个工厂模式的具体实现，可以参看下<br>面的类图。<br>当获得一个工厂对象后， 使用它的静态方法newDocumentBuilder() 方法可以获得一个<br>DocumentBuilder对象，这个对象代表了具体的DOM解析器。但具体是哪一种解析器，微软的或<br>者IBM的，对于程序而言并不重要。<br>然后，我们就可以利用这个解析器来对XML文档进行解析了：<br>DocumentBuilder的parse()方法接受一个XML文档名作为输入参数，返回一个Document对象，这<br>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br>&lt;messages&gt;<br>&lt;message&gt;Good-bye serialization, hello Java!&lt;/message&gt;<br>&lt;/messages&gt;<br>DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();<br>DocumentBuilder db = dbf.newDocumentBuilder();<br>Document doc = db.parse("c:/xml/message.xml");<br>个Document对象就代表了一个XML文档的树模型。以后所有的对XML文档的操作，都与解析器<br>无关，直接在这个Document对象上进行操作就可以了。而具体对Document操作的方法，就是由<br>DOM所定义的了。<br>Jaxp支持W3C所推荐的DOM 2。如果你对DOM很熟悉，那么下面的内容就很简单了：只需要按<br>照DOM的规范来进行方法调用就可以。当然，如果你对DOM不清楚，也不用着急，后面我们<br>会有详细的介绍。在这儿，你所要知道并牢记的是：DOM是用来描叙XML文档中的数据的模型，<br>引入DOM的全部原因就是为了用这个模型来操作XML文档的中的数据。DOM规范中定义有节<br>点（即对象）、属性和方法，我们通过这些节点的存取来存取XML的数据。<br>从上面得到的Document对象开始，我们就可以开始我们的DOM之旅了。使用Document对象的<br>getElementsByTagName()方法，我们可以得到一个NodeList对象，一个Node对象代表了一个XML<br>文档中的一个标签元素，而NodeList对象，观其名而知其意，所代表的是一个Node对象的列表：<br>我们通过这样一条语句所得到的是XML文档中所有&lt;message&gt;标签对应的Node对象的一个列<br>表。然后，我们可以使用NodeList对象的item()方法来得到列表中的每一个Node对象：<br>当一个Node对象被建立之后，保存在XML文档中的数据就被提取出来并封装在这个Node中了。<br>NodeList nl = doc.getElementsByTagName("message");<br>Node my_node = nl.item(0);<br>在这个例子中，要提取Message标签内的内容，我们通常会使用Node对象的getNodeValue()方法：<br>请注意，这里还使用了一个getFirstChild()方法来获得message下面的第一个子Node对象。虽然在<br>message 标签下面除了文本外并没有其它子标签或者属性， 但是我们坚持在这里使用<br>getFirseChild()方法，这主要和W3C对DOM的定义有关。W3C把标签内的文本部分也定义成一<br>个Node，所以先要得到代表文本的那个Node，我们才能够使用getNodeValue()来获取文本的内<br>容。<br>现在，既然我们已经能够从XML文件中提取出数据了，我们就可以把这些数据用在合适的地方，<br>来构筑应用程序。<br>下面的内容，我们将更多的关注DOM，为DOM作一个较为详细的解析，使我们使用起来更为<br>得心应手。<br>DOM<br>详解<br>DOM的基本对象有5个：Document，Node，NodeList，Element和Attr。下面就这些对象的功<br>能和实现的方法作一个大致的介绍。<br>Document对象代表了整个XML的文档，所有其它的Node，都以一定的顺序包含在Document<br>String message = my_node.getFirstChild().getNodeValue();<br>1．基本的DOM对象<br>对象之内，排列成一个树形的结构，程序员可以通过遍历这颗树来得到XML文档的所有的内容，<br>这也是对XML文档操作的起点。我们总是先通过解析XML源文件而得到一个Document对象，<br>然后再来执行后续的操作。此外，Document还包含了创建其它节点的方法，比如createAttribut()<br>用来创建一个Attr对象。它所包含的主要的方法有：<br>createAttribute(String)：用给定的属性名创建一个Attr对象，并可在其后使用setAttributeNode<br>方法来放置在某一个Element对象上面。<br>createElement(String)：用给定的标签名创建一个Element对象，代表XML文档中的一个标签，<br>然后就可以在这个Element对象上添加属性或进行其它的操作。<br>createTextNode(String)：用给定的字符串创建一个Text对象，Text对象代表了标签或者属性中<br>所包含的纯文本字符串。如果在一个标签内没有其它的标签，那么标签内的文本所代表的Text<br>对象是这个Element对象的唯一子对象。<br>getElementsByTagName(String)：返回一个NodeList对象，它包含了所有给定标签名字的标签。<br>getDocumentElement()：返回一个代表这个DOM树的根节点的Element对象，也就是代表XML<br>文档根元素的那个对象。<br>Node对象是DOM结构中最为基本的对象，代表了文档树中的一个抽象的节点。在实际使用的<br>时候，很少会真正的用到Node这个对象，而是用到诸如Element、Attr、Text等Node对象的子<br>对象来操作文档。Node对象为这些对象提供了一个抽象的、公共的根。虽然在Node对象中定<br>义了对其子节点进行存取的方法，但是有一些Node子对象，比如Text对象，它并不存在子节点，<br>这一点是要注意的。Node对象所包含的主要的方法有：<br>appendChild(org.w3c.dom.Node)：为这个节点添加一个子节点，并放在所有子节点的最后，<br>如果这个子节点已经存在，则先把它删掉再添加进去。<br>getFirstChild()：如果节点存在子节点，则返回第一个子节点，对等的，还有getLastChild()方法<br>返回最后一个子节点。<br>getNextSibling() ： 返回在DOM 树中这个节点的下一个兄弟节点， 对等的， 还有<br>getPreviousSibling()方法返回其前一个兄弟节点。<br>getNodeName()：根据节点的类型返回节点的名称。<br>getNodeType()：返回节点的类型。<br>getNodeValue()：返回节点的值。<br>hasChildNodes()：判断是不是存在有子节点。<br>hasAttributes()：判断这个节点是否存在有属性。<br>getOwnerDocument()：返回节点所处的Document对象。<br>insertBefore(org.w3c.dom.Node new，org.w3c.dom.Node ref)：在给定的一个子对象前再插入<br>一个子对象。<br>removeChild(org.w3c.dom.Node)：删除给定的子节点对象。<br>replaceChild(org.w3c.dom.Node new，org.w3c.dom.Node old)：用一个新的Node对象代替给<br>定的子节点对象。<br>NodeList对象，顾名思义，就是代表了一个包含了一个或者多个Node的列表。可以简单的把它<br>看成一个Node的数组，我们可以通过方法来获得列表中的元素：<br>GetLength()：返回列表的长度。<br>Item(int)：返回指定位置的Node对象。<br>Element对象代表的是XML文档中的标签元素，继承于Node，亦是Node的最主要的子对象。在<br>标签中可以包含有属性，因而Element对象中有存取其属性的方法，而任何Node中定义的方法，<br>也可以用在Element对象上面。<br>getElementsByTagName(String)：返回一个NodeList对象，它包含了在这个标签中其下的子孙<br>节点中具有给定标签名字的标签。<br>getTagName()：返回一个代表这个标签名字的字符串。<br>getAttribute(String)：返回标签中给定属性名称的属性的值。在这儿需要主要的是，应为XML<br>文档中允许有实体属性出现，而这个方法对这些实体属性并不适用。这时候需要用到<br>getAttributeNodes()方法来得到一个Attr对象来进行进一步的操作。<br>getAttributeNode(String)：返回一个代表给定属性名称的Attr对象。<br>Attr对象代表了某个标签中的属性。Attr继承于Node，但是因为Attr实际上是包含在Element中<br>的，它并不能被看作是Element的子对象，因而在DOM中Attr并不是DOM树的一部分，所以Node<br>中的getparentNode()，getpreviousSibling()和getnextSibling()返回的都将是null。也就是说，<br>Attr其实是被看作包含它的Element对象的一部分，它并不作为DOM树中单独的一个节点出现。<br>这一点在使用的时候要同其它的Node子对象相区别。<br>需要说明的是，上面所说的DOM对象在DOM中都是用接口定义的，在定义的时候使用的是与<br>具体语言无关的IDL语言来定义的。因而，DOM其实可以在任何面向对象的语言中实现，只要<br>它实现了DOM所定义的接口和功能就可以了。同时，有些方法在DOM中并没有定义，是用IDL<br>的属性来表达的，当被映射到具体的语言时，这些属性被映射为相应的方法。<br>有了上面的介绍，相信你对DOM理解的更多了吧。下面的例子将让你对DOM更加熟悉起来。<br>先说说这个例子到底要做的是什么吧，我们希望在一个名为link.xml文件中保存了一些URL地<br>址，通过一个简单的程序，我们可以通过DOM把这些URL读出并显示出来，也可以反过来向这<br>个XML文件中写入加入的URL地址。很简单，却很实用，也足够来例示DOM的绝大部分用法了。<br>XML文件本身不复杂，就不给出它的DTD了。link.xml:<br>2．DOM实例<br>&lt;?xml version="1.0" standalone="yes"?&gt;<br>&lt;links&gt;<br>&lt;link&gt;<br>&lt;text&gt;JSP Insider&lt;/text&gt;<br>&lt;url newWindow="no"&gt;http://www.jspinsider.com&lt;/url&gt;<br>&lt;author&gt;JSP Insider&lt;/author&gt;<br>&lt;date&gt;<br>&lt;day&gt;2&lt;/day&gt;<br>&lt;month&gt;1&lt;/month&gt;<br>&lt;year&gt;2001&lt;/year&gt;<br>&lt;/date&gt;<br>&lt;description&gt;A JSP information site.&lt;/description&gt;<br>&lt;/link&gt;<br>&lt;link&gt;<br>&lt;text&gt;The makers of Java&lt;/text&gt;<br>&lt;url newWindow="no"&gt;http://java.sun.com&lt;/url&gt;<br>&lt;author&gt;Sun Microsystems&lt;/author&gt;<br>&lt;date&gt;<br>&lt;day&gt;3&lt;/day&gt;<br>&lt;month&gt;1&lt;/month&gt;<br>&lt;year&gt;2001&lt;/year&gt;<br>&lt;/date&gt;<br>&lt;description&gt;Sun Microsystem's website.&lt;/description&gt;<br>&lt;/link&gt;<br>&lt;link&gt;<br>第一个程序我们称为xmldisplay.java，具体的程序清单可以在附件中找到。主要的功能就是读<br>取这个XML文件中各个节点的内容，然后在格式化输出在System.out上，我们来看看这个程序：<br>这是引入必要的类， 因为在这里使用的是Sun 所提供的XML 解析器， 因而需要引入<br>java.xml.parsers包，其中包含了有DOM解析器和SAX解析器的具体实现。org.w3c.dom包中定<br>义了w3c所制定的DOM接口。<br>除了上面讲到的，还有一个小技巧，对Document对象调用normalize()，可以去掉XML文档中作<br>为格式化内容的空白而映射在DOM树中的不必要的Text Node对象。否则你得到的DOM树可能<br>并不如你所想象的那样。特别是在输出的时候，这个normalize()更为有用。<br>刚才说过，XML文档中的空白符也会被作为对象映射在DOM树中。因而，直接调用Node方法<br>的getChildNodes方法有时候会有些问题，有时不能够返回所期望的NodeList对象。解决的办法<br>是使用Element的getElementByTagName(String)，返回的NodeLise就是所期待的对象了。然<br>后，可以用item()方法提取想要的元素。<br>&lt;text&gt;The standard JSP container&lt;/text&gt;<br>&lt;url newWindow="no"&gt;http://jakarta.apache.org&lt;/url&gt;<br>&lt;author&gt;Apache Group&lt;/author&gt;<br>&lt;date&gt;<br>&lt;day&gt;4&lt;/day&gt;<br>&lt;month&gt;1&lt;/month&gt;<br>&lt;year&gt;2001&lt;/year&gt;<br>&lt;/date&gt;<br>&lt;description&gt;Some great software.&lt;/description&gt;<br>&lt;/link&gt;<br>&lt;/links&gt;<br>import javax.xml.parsers.*;<br>import org.w3c.dom.*;<br>DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();<br>DocumentBuilder builder=factory.newDocumentBuilder();<br>Document doc=builder.parse("links.xml");<br>doc.normalize();<br>NodeList links =doc.getElementsByTagName("link");<br>for (int i=0;i&lt;links.getLength();i++){<br>Element link=(Element) links.item(i);<br>System.out.print("Content: ");<br>System.out.println(link.getElementsByTagName("text").item(0).getFirstChild().getNodeValue()<br>上面的代码片断就完成了对XML文档内容的格式化输出。只要注意到一些细节的问题，比如<br>getFirstChile()方法和getElementsByTagName()方法的使用，这些还是比较容易的。<br>下面的内容，就是在修改了DOM树后重新写入到XML文档中去的问题了。这个程序名为<br>xmlwrite.java。在JAXP1.0版本中，并没有直接的类和方法能够处理XML文档的写入问题，需<br>要借助其它包中的一些辅助类。而在JAXP1.1版本中，引入了对XSLT的支持，所谓XSLT，就<br>是对XML文档进行变换（Translation）后，得到一个新的文档结构。利用这个新加入的功能，<br>我们就能够很方便的把新生成或者修改后的DOM树从新写回到XML文件中去了，下面我们来看<br>看代码的实现，这段代码的主要功能是向links.xml文件中加入一个新的link节点：<br>新引入的java.xml.transform包中的几个类，就是用来处理XSLT变换的。<br>我们希望在上面的XML文件中加入一个新的link节点，因而首先还是要读入links.xml文件，构建<br>一个DOM树，然后再对这个DOM树进行修改（添加节点），最后把修改后的DOM写回到links.xml<br>文件中：<br>);<br>System.out.print("URL: ");<br>System.out.println(link.getElementsByTagName("url").item(0).getFirstChild().getNodeValue());<br>System.out.print("Author: ");<br>System.out.println(link.getElementsByTagName("author").item(0).getFirstChild().getNodeVal<br>ue());<br>System.out.print("Date: ");<br>Element linkdate=(Element) link.getElementsByTagName("date").item(0);<br>String day=linkdate.getElementsByTagName("day").item(0).getFirstChild().getNodeValue();<br>String<br>month=linkdate.getElementsByTagName("month").item(0).getFirstChild().getNodeValue();<br>String year=linkdate.getElementsByTagName("year").item(0).getFirstChild().getNodeValue();<br>System.out.println(day+"-"+month+"-"+year);<br>System.out.print("Description: ");<br>System.out.println(link.getElementsByTagName("description").item(0).getFirstChild().getNod<br>eValue());<br>System.out.println();<br>}<br>import javax.xml.parsers.*;<br>import javax.xml.transform.*;<br>import javax.xml.transform.dom.DOMSource;<br>import javax.xml.transform.stream.StreamResult;<br>import org.w3c.dom.*;<br>为了看清重点，简化程序，我们把要加入的内容硬编码到记忆String对象中，而实际操作中，往<br>往利用一个界面来提取用户输入，或者通过JDBC从数据库中提取想要的内容。<br>首先应该明了的是，无论什么类型的Node，Text型的也好，Attr型的也好，Element型的也好，<br>它们的创建都是通过Document对象中的createXXX()方法来创建的（XXX代表具体要创建的类<br>型），因此，我们要向XML文档中添加一个link项目，首先要创建一个link对象：<br>DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();<br>DocumentBuilder builder=factory.newDocumentBuilder();<br>Document doc=builder.parse("links.xml");<br>doc.normalize();<br>//---取得变量----<br>String text="Hanzhong's Homepage";<br>String url="<a href="http://www.hzliu.com/">www.hzliu.com</a>";<br>String author="Hzliu Liu";<br>String discription="A site from Hanzhong Liu, give u lots of suprise!!!";<br>Text textseg;<br>Element link=doc.createElement("link");<br>Element linktext=doc.createElement("text");<br>textseg=doc.createTextNode(text);<br>linktext.appendChild(textseg);<br>link.appendChild(linktext);<br>Element linkurl=doc.createElement("url");<br>textseg=doc.createTextNode(url);<br>linkurl.appendChild(textseg);<br>link.appendChild(linkurl);<br>Element linkauthor=doc.createElement("author");<br>textseg=doc.createTextNode(author);<br>linkauthor.appendChild(textseg);<br>link.appendChild(linkauthor);<br>java.util.Calendar rightNow = java.util.Calendar.getInstance();<br>String day=Integer.toString(rightNow.get(java.util.Calendar.DAY_OF_MONTH));<br>String month=Integer.toString(rightNow.get(java.util.Calendar.MONTH));<br>String year=Integer.toString(rightNow.get(java.util.Calendar.YEAR));<br>Element linkdate=doc.createElement("date");<br>Element linkdateday=doc.createElement("day");<br>textseg=doc.createTextNode(day);<br>linkdateday.appendChild(textseg);<br>Element linkdatemonth=doc.createElement("month");<br>创建节点的过程可能有些千篇一律，但需要注意的地方是，对Element中所包含的text（在DOM<br>中，这些text也是代表了一个Node的，因此也必须为它们创建相应的node），不能直接用Element<br>对象的setNodeValue()方法来设置这些text的内容，而需要用创建的Text对象的setNodeValue()<br>方法来设置文本，这样才能够把创建的Element和其文本内容添加到DOM树中。看看前面的代<br>码，你会更好的理解这一点：<br>最后，不要忘记把创建好的节点添加到DOM树中。Document类的getDocumentElement()方法，<br>返回代表文档根节点的Element对象。在XML文档中，根节点一定是唯一的。<br>然后就是用XSLT把DOM树输出了。这里的TransformerFactory也同样应用了工厂模式，使得具<br>体的代码同具体的变换器无关。实现的方法和DocumentBuilderFactory相同，这儿就不赘述了。<br>Transformer类的transfrom方法接受两个参数、一个数据源Source和一个输出目标Result。这里<br>分别使用的是DOMSource和StreamResult，这样就能够把DOM的内容输出到一个输出流中，<br>当这个输出流是一个文件的时候，DOM的内容就被写入到文件中去了。<br>好了，关于DOM的话题就先到这里，下篇文章将介绍的内容是SAX。请看Java与XML读写之<br>SAX篇<br>textseg=doc.createTextNode(month);<br>linkdatemonth.appendChild(textseg);<br>Element linkdateyear=doc.createElement("year");<br>textseg=doc.createTextNode(year);<br>linkdateyear.appendChild(textseg);<br>linkdate.appendChild(linkdateday);<br>linkdate.appendChild(linkdatemonth);<br>linkdate.appendChild(linkdateyear);<br>link.appendChild(linkdate);<br>Element linkdiscription=doc.createElement("description");<br>textseg=doc.createTextNode(discription);<br>linkdiscription.appendChild(textseg);<br>link.appendChild(linkdiscription);<br>doc.getDocumentElement().appendChild(link);<br>TransformerFactory tFactory =TransformerFactory.newInstance();<br>Transformer transformer = tFactory.newTransformer();<br>DOMSource source = new DOMSource(doc);<br>StreamResult result = new StreamResult(new java.io.File("links.xml"));<br>transformer.transform(source, result);
<img src ="http://www.cppblog.com/qywyh/aggbug/39318.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/qywyh/" target="_blank">豪</a> 2007-12-23 03:25 <a href="http://www.cppblog.com/qywyh/articles/39318.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】java乱码解决方案</title><link>http://www.cppblog.com/qywyh/articles/39316.html</link><dc:creator>豪</dc:creator><author>豪</author><pubDate>Sat, 22 Dec 2007 19:24:00 GMT</pubDate><guid>http://www.cppblog.com/qywyh/articles/39316.html</guid><wfw:comment>http://www.cppblog.com/qywyh/comments/39316.html</wfw:comment><comments>http://www.cppblog.com/qywyh/articles/39316.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/qywyh/comments/commentRss/39316.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/qywyh/services/trackbacks/39316.html</trackback:ping><description><![CDATA[<p>1.字节和unicode<br>java内核是unicode的，就连class文件也是，但是很多媒体，包括文件/流的保存方式是使用字节流的。因此java要对这些字节流经行转化。char是unicode的，而byte是字节。java中byte/char互转的函数在sun.io的包中间有。<br>其中ByteToCharConverter类是中调度，可以用来告诉你，你用的convertor。其中两个很常用的静态函数是<br>public static ByteToCharConverter getDefault();<br>public static ByteToCharConverter getConverter(String encoding);<br>如果你不指定converter，则系统会自动使用当前的encoding,gb平台上用gbk,en平台上用8859_1。</p>
<p><br>byte ——〉char：<br>"你"的gb码是：0xc4e3 ,unicode是0x4f60<br>String encoding = "gb2312";<br>byte b[] = {(byte)'\u00c4',(byte)'\u00e3'};<br>ByteToCharConverter converter =<br>ByteToCharConverter.getConverter(encoding);<br>char c[] = converter.convertAll(b);<br>for (int i = 0; i &lt; c.length; i++) {<br>System.out.println(Integer.toHexString(c[i]));<br>}<br>结果是什么？0x4f60<br>如果encoding ="8859_1"，结果又是什么？0x00c4,0x00e3<br>如果代码改为<br>byte b[] = {(byte)'\u00c4',(byte)'\u00e3'};<br>ByteToCharConverter converter = ByteToCharConverter. getDefault();<br>char c[] = converter.convertAll(b);<br>for (int i = 0; i &lt; c.length; i++) {<br>System.out.println(Integer.toHexString(c[i]));<br>}<br>结果将又是什么？根据平台的编码而定。</p>
<p>char ——〉byte：<br>String encoding = "gb2312";<br>char c[] = {'\u4f60'};<br>CharToByteConverter converter = CharToByteConverter.getConverter(encoding);<br>byte b[] = converter.convertAll(c);<br>for (int i = 0; i &lt; b.length; i++) {<br>System.out.println(Integer.toHexString(b[i]));<br>}<br>结果是什么？0x00c4,0x00e3<br>如果encoding ="8859_1"，结果又是什么？0x3f<br>如果代码改为<br>String encoding = "gb2312";<br>char c[] = {'\u4f60'};<br>CharToByteConverter converter = CharToByteConverter.getDefault();<br>byte b[] = converter.convertAll(c);<br>for (int i = 0; i &lt; b.length; i++) {<br>System.out.println(Integer.toHexString(b[i]));<br>}<br>结果将又是什么？根据平台的编码而定。<br>很多中文问题就是从这两个最简单的类派生出来的。而却有很多类不直接支持把encoding输入，这给我们带来诸多不便。很多程序难得用encoding了，直接用default的encoding，这就给我们移植带来了很多困难。</p>
<p>2.utf-8<br>utf-8是和unicode一一对应的，其实现很简单<br>7位的unicode: 0 _ _ _ _ _ _ _<br>11位的unicode: 1 1 0 _ _ _ _ _ 1 0 _ _ _ _ _ _<br>16位的unicode: 1 1 1 0 _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _<br>21位的unicode: 1 1 1 1 0 _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _<br>大多数情况是只使用到16位以下的unicode:<br>"你"的gb码是：0xc4e3 ,unicode是0x4f60<br>0xc4e3的二进制：<br>1100 ，0100 ，1110 ，0011<br>由于只有两位我们按照两位的编码来排，但是我们发现这行不通，因为第７位不是0因此，返回"?"<br>0x4f60的二进制：<br>0100 ，1111 ，0110 ，0000<br>我们用utf-8补齐，变成：<br>1110 ，0100 ，1011 ，1101 ，1010 ，0000<br>e4--bd-- a0<br>于是返回：0xe4,0xbd,0xa0。</p>
<p>3.string和byte[]<br>string其实核心是char[],然而要把byte转化成string，必须经过编码。string.length()其实就是char数组的长度，如果使用不同的编码，很可能会错分，造成散字和乱码。<br>例如：<br>String encoding = &#8220;&#8221;;<br>byte [] b={(byte)'\u00c4',(byte)'\u00e3'};<br>String str=new String(b,encoding);　　<br>如果encoding=8859_1，会有两个字，但是encoding=gb2312只有一个字这个问题在处理分页是经常发生 。</p>
<p>4.Reader,Writer / InputStream,OutputStream<br>Reader和Writer核心是char，InputStream和OutputStream核心是byte。但是Reader和Writer的主要目的是要把char读/写InputStream/OutputStream。<br>例如：<br>文件test.txt只有一个"你"字，0xc4,0xe3<br>String encoding = "gb2312";<br>InputStreamReader reader = new InputStreamReader(new FileInputStream(<br>"text.txt"), encoding);<br>char c[] = new char[10];<br>int length = reader.read(c);<br>for (int i = 0; i &lt; length; i++) {<br>System.out.println(c[i]);<br>}<br>结果是什么？你<br>如果encoding ="8859_1"，结果是什么？??两个字符，表示不认识。<br>反过来的例子自己做。</p>
<p>5.我们要对java的编译器有所了解 ：<br>javac ?encoding<br>我们常常没有用到encoding这个参数。其实encoding这个参数对于跨平台的操作是很重要的。如果没有指定encoding，则按照系统的默认encoding,gb平台上是gb2312，英文平台上是iso8859_1。<br>java 的编译器实际上是调用sun.tools.javac.main的类，对文件进行编译，这个类有compile函数中间有一个encoding的变量,- encoding的参数其实直接传给encoding变量。编译器就是根据这个变量来读取java文件的，然后把用utf-8形式编译成class文件。<br>例子代码：<br>String str = "你";<br>FileWriter writer = new FileWriter("text.txt");<br>write.write(str);<br>writer.close();</p>
<p>如果用gb2312编译，你会找到e4 bd a0的字段 ；<br>如果用8859_1编译， 00c4 00e3的二进制：<br>0000，0000 ，1100，0100 ，0000，0000 ，1110，0011<br>因为每个字符都大于7位，因此用11位编码：<br>1100，0001，1000，0100，1100，0011，1010，0011<br>c1-- 84--　c3--　 a3<br>你会找到c1 84 c3 a3 。</p>
<p>但是我们往往忽略掉这个参数，因此这样往往会有跨平台的问题：<br>样例代码在中文平台上编译，生成zhclass<br>样例代码在英文平台上编译，输出enclass<br>(1).　 zhclass在中文平台上执行ok,但是在英文平台上不行<br>(2). enclass在英文平台上执行ok,但是在中文平台上不行<br>原因：<br>(1). 在中文平台上编译后，其实str在运行态的char[]是0x4f60,　在中文平台上运行，filewriter的缺省编码是gb2312,因此 chartobyteconverter会自动用调用gb2312的converter,把str转化成byte输入到fileoutputstream 中，于是0xc4,0xe3放进了文件。<br>但是如果是在英文平台下，chartobyteconverter的缺省值是8859_1, filewriter会自动调用8859_1去转化str,但是他无法解释，因此他会输出"?"<br>(2). 在英文平台上编译后，其实str在运行态的char[]是0x00c4 0x00e3, 在中文平台上运行，中文无法识别，因此会出现??；<br>在英文平台上，0x00c4--&gt;0xc4,0x00e3-&gt;0xe3，因此0xc4,0xe3被放进了文件。</p>
<p>6. 其它原因： <br>设置浏览器的显示编码，如果response的数据是utf8编码，显示将是乱码，但是乱码和上述原因还不一样。</p>
<p>7. 发生编码的地方 ：<br>? 从数据库到java程序 byte——〉char<br>? 从java程序到数据库 char——〉byte<br>? 从文件到java程序 byte——〉char<br>? 从java程序到文件 char——〉byte<br>? 从java程序到页面显示 char——〉byte<br>? 从页面form提交数据到java程序byte——〉char<br>? 从流到java程序byte——〉char<br>? 从java程序到流char——〉byte</p>
<p>谢志钢的解决方法：<br>我是使用配置过滤器的方法解决中文乱码的：</p>
<p>＜web-app&gt;<br>＜filter&gt;<br>＜filter-name&gt;RequestFilter<br>＜filter-class&gt;net.golden.uirs.util.RequestFilter<br>＜init-param&gt;<br>＜param-name&gt;charset<br>＜param-value&gt;gb2312<br>＜/init-param&gt;<br>＜/filter&gt;<br>＜filter-mapping&gt;<br>＜filter-name&gt;RequestFilter<br>＜url-pattern&gt;*.jsp<br>＜/filter-mapping&gt;<br>＜/web-app&gt;</p>
<p><br>public void doFilter(ServletRequest req, ServletResponse res,<br>FilterChain fChain) throws IOException, ServletException {<br>HttpServletRequest request = (HttpServletRequest) req;<br>HttpServletResponse response = (HttpServletResponse) res;<br>HttpSession session = request.getSession();<br>String userId = (String) session.getAttribute("userid");<br>req.setCharacterEncoding(this.filterConfig.getInitParameter("charset")); // 设置字符集？<br>实际上是设置了byte ——〉char的encoding<br>try {<br>if (userId == null || userId.equals("")) {<br>if (!request.getRequestURL().toString().matches(<br>".*/uirs/logon/logon(Controller){0,1}\\x2Ejsp$")) {<br>session.invalidate();<br>response.sendRedirect(request.getContextPath() +<br>"/uirs/logon/logon.jsp");<br>}<br>}<br>else { // 看看是否具有信息上报系统的权限<br>if (!net.golden.uirs.util.UirsChecker.check(userId, "信息上报系统",<br>net.golden.uirs.util.UirsChecker.ACTION_DO)) {<br>if (!request.getRequestURL().toString().matches(<br>".*/uirs/logon/logon(Controller){0,1}\\x2Ejsp$")) {<br>response.sendRedirect(request.getContextPath() +<br>"/uirs/logon/logonController.jsp");<br>}<br>}<br>}<br>}<br>catch (Exception ex) {<br>response.sendRedirect(request.getContextPath() +<br>"/uirs/logon/logon.jsp");<br>}<br>fChain.doFilter(req, res);<br>}<br></p>
<img src ="http://www.cppblog.com/qywyh/aggbug/39316.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/qywyh/" target="_blank">豪</a> 2007-12-23 03:24 <a href="http://www.cppblog.com/qywyh/articles/39316.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】使用 Java Reflection </title><link>http://www.cppblog.com/qywyh/articles/34523.html</link><dc:creator>豪</dc:creator><author>豪</author><pubDate>Thu, 18 Oct 2007 03:05:00 GMT</pubDate><guid>http://www.cppblog.com/qywyh/articles/34523.html</guid><wfw:comment>http://www.cppblog.com/qywyh/comments/34523.html</wfw:comment><comments>http://www.cppblog.com/qywyh/articles/34523.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/qywyh/comments/commentRss/34523.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/qywyh/services/trackbacks/34523.html</trackback:ping><description><![CDATA[<div style="MARGIN-TOP: 20px; LINE-HEIGHT: 18px">
<p>Reflection 是 Java 程序开发语言的特征之一，它允许运行中的 Java 程序对自身进行检查，或者说&#8220;自审&#8221;，并能直接操作程序的内部属性。例如，使用它能获得 Java 类中各成员的名称并显示出来。</p>
<p>Java 的这一能力在实际应用中也许用得不是很多，但是在其它的程序设计语言中根本就不存在这一特性。例如，Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息。</p>
<p>JavaBean 是 reflection 的实际应用之一，它能让一些工具可视化的操作软件组件。这些工具通过 reflection 动态的载入并取得 Java 组件(类) 的属性。</p>
<p>一个简单的例子</p>
<p>考虑下面这个简单的例子，让我们看看 reflection 是如何工作的。</p>
<p>import java.lang.reflect.*;</p>
<p>public class DumpMethods {</p>
<p>&nbsp;&nbsp;&nbsp; public static void main(String args[]) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class c = Class.forName(args[0]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Method m[] = c.getDeclaredMethods();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; m.length; i++)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(m[i].toString());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (Throwable e) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.err.println(e);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>按如下语句执行：</p>
<p>java DumpMethods java.util.Stack</p>
<p>它的结果输出为：</p>
<p>public java.lang.Object java.util.Stack.push(java.lang.Object)</p>
<p>public synchronized java.lang.Object java.util.Stack.pop()</p>
<p>public synchronized java.lang.Object java.util.Stack.peek()</p>
<p>public boolean java.util.Stack.empty()</p>
<p>public synchronized int java.util.Stack.search(java.lang.Object)</p>
<p>这样就列出了java.util.Stack 类的各方法名以及它们的限制符和返回类型。</p>
<p>这个程序使用 Class.forName 载入指定的类，然后调用 getDeclaredMethods 来获取这个类中定义了的方法列表。java.lang.reflect.Methods 是用来描述某个类中单个方法的一个类。</p>
<p>开始使用 Reflection</p>
<p>用于 reflection 的类，如 Method，可以在 java.lang.relfect 包中找到。使用这些类的时候必须要遵循三个步骤：第一步是获得你想操作的类的 java.lang.Class 对象。在运行中的 Java 程序中，用 java.lang.Class 类来描述类和接口等。</p>
<p>下面就是获得一个 Class 对象的方法之一：</p>
<p>Class c = Class.forName("java.lang.String");</p>
<p>这条语句得到一个 String 类的类对象。还有另一种方法，如下面的语句：</p>
<p>Class c = int.class;</p>
<p>或者</p>
<p>Class c = Integer.TYPE;</p>
<p>它们可获得基本类型的类信息。其中后一种方法中访问的是基本类型的封装类 (如 Integer) 中预先定义好的 TYPE 字段。</p>
<p>第二步是调用诸如 getDeclaredMethods 的方法，以取得该类中定义的所有方法的列表。</p>
<p>一旦取得这个信息，就可以进行第三步了——使用 reflection API 来操作这些信息，如下面这段代码：</p>
<p>Class c = Class.forName("java.lang.String");</p>
<p>Method m[] = c.getDeclaredMethods();</p>
<p>System.out.println(m[0].toString());</p>
<p>它将以文本方式打印出 String 中定义的第一个方法的原型。</p>
<p>在下面的例子中，这三个步骤将为使用 reflection 处理特殊应用程序提供例证。</p>
<p>模拟 instanceof 操作符</p>
<p>得到类信息之后，通常下一个步骤就是解决关于 Class 对象的一些基本的问题。例如，Class.isInstance 方法可以用于模拟 instanceof 操作符：</p>
<p>class A {}</p>
<p>public class instance1 {</p>
<p>&nbsp;&nbsp;&nbsp; public static void main(String args[])</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class cls = Class.forName("A");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; boolean b1</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = cls.isInstance(new Integer(37));</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(b1);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; boolean b2 = cls.isInstance(new A());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(b2);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (Throwable e) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.err.println(e);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>在这个例子中创建了一个 A 类的 Class 对象，然后检查一些对象是否是 A 的实例。Integer(37) 不是，但 new A() 是。</p>
<p>找出类的方法</p>
<p>找出一个类中定义了些什么方法，这是一个非常有价值也非常基础的 reflection 用法。下面的代码就实现了这一用法：</p>
<p>import java.lang.reflect.*;</p>
<p>public class method1 {</p>
<p>&nbsp;&nbsp;&nbsp; private int f1(Object p, int x) throws NullPointerException</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (p == null)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new NullPointerException();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return x;</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public static void main(String args[])</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class cls = Class.forName("method1");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Method methlist[]</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = cls.getDeclaredMethods();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; methlist.length;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i++) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Method m = methlist[i];</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("name</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " + m.getName());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("decl class = " +</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m.getDeclaringClass());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class pvec[] = m.getParameterTypes();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int j = 0; j &lt; pvec.length; j++)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; param #" + j + " " + pvec[j]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class evec[] = m.getExceptionTypes();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int j = 0; j &lt; evec.length; j++)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("exc #" + j</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + " " + evec[j]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("return type = " +</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m.getReturnType());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("-----");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (Throwable e) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.err.println(e);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>这个程序首先取得 method1 类的描述，然后调用 getDeclaredMethods 来获取一系列的 Method 对象，它们分别描述了定义在类中的每一个方法，包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程序中使用 getMethods 来代替 getDeclaredMethods，你还能获得继承来的各个方法的信息。</p>
<p>取得了 Method 对象列表之后，要显示这些方法的参数类型、异常类型和返回值类型等就不难了。这些类型是基本类型还是类类型，都可以由描述类的对象按顺序给出。</p>
<p>输出的结果如下：</p>
<p>name = f1</p>
<p>decl class = class method1</p>
<p>param #0 class java.lang.Object</p>
<p>param #1 int</p>
<p>exc #0 class java.lang.NullPointerException</p>
<p>return type = int</p>
<p>-----</p>
<p>name = main</p>
<p>decl class = class method1</p>
<p>param #0 class [Ljava.lang.String;</p>
<p>return type = void</p>
<p>-----<br>&nbsp;<br>获取构造器信息</p>
<p>获取类构造器的用法与上述获取方法的用法类似，如：</p>
<p>import java.lang.reflect.*;</p>
<p>public class constructor1 {</p>
<p>&nbsp;&nbsp;&nbsp; public constructor1()</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; protected constructor1(int i, double d)</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public static void main(String args[])</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class cls = Class.forName("constructor1");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Constructor ctorlist[]</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = cls.getDeclaredConstructors();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; ctorlist.length; i++) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Constructor ct = ctorlist[i];</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("name</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " + ct.getName());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("decl class = " +</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ct.getDeclaringClass());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class pvec[] = ct.getParameterTypes();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int j = 0; j &lt; pvec.length; j++)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("param #"</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + j + " " + pvec[j]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class evec[] = ct.getExceptionTypes();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int j = 0; j &lt; evec.length; j++)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "exc #" + j + " " + evec[j]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("-----");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (Throwable e) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.err.println(e);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>这个例子中没能获得返回类型的相关信息，那是因为构造器没有返回类型。</p>
<p>这个程序运行的结果是：</p>
<p>name = constructor1</p>
<p>decl class = class constructor1</p>
<p>-----</p>
<p>name = constructor1</p>
<p>decl class = class constructor1</p>
<p>param #0 int</p>
<p>param #1 double</p>
<p>-----</p>
<p>获取类的字段(域)</p>
<p>找出一个类中定义了哪些数据字段也是可能的，下面的代码就在干这个事情：</p>
<p>import java.lang.reflect.*;</p>
<p>public class field1 {</p>
<p>&nbsp;&nbsp;&nbsp; private double d;</p>
<p>&nbsp;&nbsp;&nbsp; public static final int i = 37;</p>
<p>&nbsp;&nbsp;&nbsp; String s = "testing";</p>
<p>&nbsp;&nbsp;&nbsp; public static void main(String args[])</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class cls = Class.forName("field1");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Field fieldlist[]</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = cls.getDeclaredFields();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0; i &lt; fieldlist.length; i++) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Field fld = fieldlist[i];</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("name</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " + fld.getName());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("decl class = " +</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fld.getDeclaringClass());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("type</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " + fld.getType());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int mod = fld.getModifiers();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("modifiers = " +</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Modifier.toString(mod));</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("-----");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (Throwable e) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.err.println(e);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>这个例子和前面那个例子非常相似。例中使用了一个新东西 Modifier，它也是一个 reflection 类，用来描述字段成员的修饰语，如&#8220;private int&#8221;。这些修饰语自身由整数描述，而且使用 Modifier.toString 来返回以&#8220;官方&#8221;顺序排列的字符串描述 (如&#8220;static&#8221;在&#8220;final&#8221;之前)。这个程序的输出是：</p>
<p>name = d</p>
<p>decl class = class field1</p>
<p>type = double</p>
<p>modifiers = private</p>
<p>-----</p>
<p>name = i</p>
<p>decl class = class field1</p>
<p>type = int</p>
<p>modifiers = public static final</p>
<p>-----</p>
<p>name = s</p>
<p>decl class = class field1</p>
<p>type = class java.lang.String</p>
<p>modifiers =</p>
<p>-----</p>
<p>和获取方法的情况一下，获取字段的时候也可以只取得在当前类中申明了的字段信息 (getDeclaredFields)，或者也可以取得父类中定义的字段 (getFields) 。</p>
<p>根据方法的名称来执行方法</p>
<p>文本到这里，所举的例子无一例外都与如何获取类的信息有关。我们也可以用 reflection 来做一些其它的事情，比如执行一个指定了名称的方法。下面的示例演示了这一操作：</p>
<p>import java.lang.reflect.*;</p>
<p>public class method2 {</p>
<p>&nbsp;&nbsp;&nbsp; public int add(int a, int b)</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return a + b;</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public static void main(String args[])</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class cls = Class.forName("method2");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class partypes[] = new Class[2];</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; partypes[0] = Integer.TYPE;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; partypes[1] = Integer.TYPE;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Method meth = cls.getMethod(</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "add", partypes);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method2 methobj = new method2();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object arglist[] = new Object[2];</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arglist[0] = new Integer(37);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arglist[1] = new Integer(47);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object retobj</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = meth.invoke(methobj, arglist);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Integer retval = (Integer)retobj;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(retval.intValue());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (Throwable e) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.err.println(e);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>假如一个程序在执行的某处的时候才知道需要执行某个方法，这个方法的名称是在程序的运行过程中指定的 (例如，JavaBean 开发环境中就会做这样的事)，那么上面的程序演示了如何做到。</p>
<p>上例中，getMethod 用于查找一个具有两个整型参数且名为 add 的方法。找到该方法并创建了相应的 Method 对象之后，在正确的对象实例中执行它。执行该方法的时候，需要提供一个参数列表，这在上例中是分别包装了整数 37 和 47 的两个 Integer 对象。执行方法的返回的同样是一个 Integer 对象，它封装了返回值 84。</p>
<p>创建新的对象</p>
<p>对于构造器，则不能像执行方法那样进行，因为执行一个构造器就意味着创建了一个新的对象 (准确的说，创建一个对象的过程包括分配内存和构造对象)。所以，与上例最相似的例子如下：</p>
<p>import java.lang.reflect.*;</p>
<p>public class constructor2 {</p>
<p>&nbsp;&nbsp;&nbsp; public constructor2()</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public constructor2(int a, int b)</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "a = " + a + " b = " + b);</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public static void main(String args[])</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class cls = Class.forName("constructor2");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class partypes[] = new Class[2];</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; partypes[0] = Integer.TYPE;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; partypes[1] = Integer.TYPE;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Constructor ct</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = cls.getConstructor(partypes);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object arglist[] = new Object[2];</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arglist[0] = new Integer(37);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arglist[1] = new Integer(47);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object retobj = ct.newInstance(arglist);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (Throwable e) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.err.println(e);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>根据指定的参数类型找到相应的构造函数并执行它，以创建一个新的对象实例。使用这种方法可以在程序运行时动态地创建对象，而不是在编译的时候创建对象，这一点非常有价值。</p>
<p>改变字段(域)的值</p>
<p>reflection 的还有一个用处就是改变对象数据字段的值。reflection 可以从正在运行的程序中根据名称找到对象的字段并改变它，下面的例子可以说明这一点：</p>
<p>import java.lang.reflect.*;</p>
<p>public class field2 {</p>
<p>&nbsp;&nbsp;&nbsp; public double d;</p>
<p>&nbsp;&nbsp;&nbsp; public static void main(String args[])</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class cls = Class.forName("field2");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Field fld = cls.getField("d");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; field2 f2obj = new field2();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("d = " + f2obj.d);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fld.setDouble(f2obj, 12.34);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("d = " + f2obj.d);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (Throwable e) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.err.println(e);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>这个例子中，字段 d 的值被变为了 12.34。</p>
<p>使用数组</p>
<p>本文介绍的 reflection 的最后一种用法是创建的操作数组。数组在 Java 语言中是一种特殊的类类型，一个数组的引用可以赋给 Object 引用。观察下面的例子看看数组是怎么工作的：</p>
<p>import java.lang.reflect.*;</p>
<p>public class array1 {</p>
<p>&nbsp;&nbsp;&nbsp; public static void main(String args[])</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class cls = Class.forName(</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "java.lang.String");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object arr = Array.newInstance(cls, 10);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Array.set(arr, 5, "this is a test");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String s = (String)Array.get(arr, 5);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(s);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (Throwable e) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.err.println(e);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>例中创建了 10 个单位长度的 String 数组，为第 5 个位置的字符串赋了值，最后将这个字符串从数组中取得并打印了出来。</p>
<p>下面这段代码提供了一个更复杂的例子：</p>
<p>import java.lang.reflect.*;</p>
<p>public class array2 {</p>
<p>&nbsp;&nbsp;&nbsp; public static void main(String args[])</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int dims[] = new int[]{5, 10, 15};</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object arr</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = Array.newInstance(Integer.TYPE, dims);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object arrobj = Array.get(arr, 3);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class cls =</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arrobj.getClass().getComponentType();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(cls);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arrobj = Array.get(arrobj, 5);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Array.setInt(arrobj, 10, 37);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int arrcast[][][] = (int[][][])arr;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(arrcast[3][5][10]);</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>例中创建了一个 5 x 10 x 15 的整型数组，并为处于 [3][5][10] 的元素赋了值为 37。注意，多维数组实际上就是数组的数组，例如，第一个 Array.get 之后，arrobj 是一个 10 x 15 的数组。进而取得其中的一个元素，即长度为 15 的数组，并使用 Array.setInt 为它的第 10 个元素赋值。</p>
<p>注意创建数组时的类型是动态的，在编译时并不知道其类型。</p>
<p>小结</p>
<p>Java reflection 非常有用，它使类和数据结构能按名称动态检索相关信息，并允许在运行着的程序中操作这些信息。Java 的这一特性非常强大，并且是其它一些常语言，如 C、C++、Fortran 或者 Pascal 等都不具备的。&nbsp; <br></p>
</div>
<img src ="http://www.cppblog.com/qywyh/aggbug/34523.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/qywyh/" target="_blank">豪</a> 2007-10-18 11:05 <a href="http://www.cppblog.com/qywyh/articles/34523.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>