??xml version="1.0" encoding="utf-8" standalone="yes"?>麻豆久久久9性大片,久久久久99精品成人片试看,久久发布国产伦子伦精品http://www.shnenglu.com/qywyh/category/5408.html?>blogzh-cnMon, 19 May 2008 19:10:48 GMTMon, 19 May 2008 19:10:48 GMT60【{】Java 多线E入门大?/title><link>http://www.shnenglu.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.shnenglu.com/qywyh/articles/39317.html</guid><wfw:comment>http://www.shnenglu.com/qywyh/comments/39317.html</wfw:comment><comments>http://www.shnenglu.com/qywyh/articles/39317.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/qywyh/comments/commentRss/39317.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/qywyh/services/trackbacks/39317.html</trackback:ping><description><![CDATA[<p>Java 多线E入门大?br>2007-03-15 14:18:41 / 个h分类Qjava </p> <p><br>作者:qlampskyface </p> <p>和作者联p:<a href="mailto:djb_skyface@tom.com">djb_skyface@tom.com</a></p> <p> </p> <p>--------------------------------------------------------------------------------</p> <p><br>    接触多线E已l不时间了,也做了不事?但是一直觉得用h不那么顺?在debug的时?往往会比较担心在同步上出什么问?惌v"E序员最怕的是自己写的代?q句?觉得真是不假.<br>    l于有一?我觉得是时候把q个问题弄清楚了,所?我就在网上找相关的内容看,l果竟然是找不到在我q个阶段应该看的,不是太简?是一W带q?不知所?<br>    废了九牛二虎之力,l于差不多弄清楚?其中有不误?以前认ؓ的和真理相差甚大.惌v自己p的时?真是觉得有点?所以把它写出来,一是防止自׃后又会忘?二是l像我一L似懂非懂者留下一点可以参考的东东.<br>    闲话说,转入正题!</p> <p> </p> <p>--------------------------------------------------------------------------------</p> <p> </p> <p>    先从U程的创?U程的创Z共有两种形式:</p> <p> </p> <p>--------------------------------------------------------------------------------</p> <p>    一U是l承自Threadc?Thread cL一个具体的c,即不是抽象类Q该cd装了U程的行为。要创徏一个线E,E序员必dZ个从 Thread cd出的新类。程序员通过覆盖 Thread ?run() 函数来完成有用的工作。用户ƈ不直接调用此函数Q而是通过调用 Thread ?start() 函数Q该函数再调?run()?br>    <br>    例如: </p> <p>    public class Test extends Thread{<br>      public Test(){<br>      }<br>      public static void main(String args[]){<br>        Test t1 = new Test();<br>        Test t2 = new Test();<br>        t1.start();<br>        t2.start();<br>      }<br>      public void run(){<br>        //do thread's things<br>      }<br>    }</p> <p> </p> <p>--------------------------------------------------------------------------------</p> <p>    <br>    另一U是实现Runnable接口,此接口只有一个函敎ͼrun()Q此函数必须由实C此接口的cd现?br>    <br>    例如: </p> <p>    public class Test implements Runnable{<br>      Thread thread1;<br>      Thread thread2;<br>      public Test(){<br>        thread1 = new Thread(this,"1");<br>        thread2 = new Thread(this,"2");<br>      }<br>      public static void main(String args[]){<br>        Test t = new Test();<br>        t.startThreads();<br>      }<br>      public void run(){<br>        //do thread's things<br>      }<br>      public void startThreads(){<br>        thread1.start();<br>        thread2.start();<br>      }<br>    }</p> <p>    两种创徏方式看v来差别不?但是弄不清楚的话Q也怼你的程序弄得一团糟。两者区别有以下几点Q?/p> <p>1.当你想承某一其它cL,你只能用后一U方?</p> <p>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> <p>    而你如果用第二种Ҏ使用U程的话Q就不会有上q的情况Q因为此Ӟ你只创徏了一个自w对象,所以,自n对象的属性和Ҏ对于U程来说是共有的?/p> <p>    因此Q我Q最好用后一U方法来使用U程?/p> <p>public class mainThread extends Thread{<br>  int i=0;<br>  public static void main(String args[]){<br>    mainThread m1 = new mainThread();<br>    mainThread m2 = new mainThread();<br>    mainThread m3 = new mainThread();<br>    mainThread m4 = new mainThread();<br>    mainThread m5 = new mainThread();<br>    mainThread m6 = new mainThread();<br>    m1.start();<br>    m2.start();<br>    m3.start();<br>    m4.start();<br>    m5.start();<br>    m6.start();<br>  }<br>  public synchronized void t1(){<br>    i=++i;<br>    try{<br>      Thread.sleep(500);<br>    }<br>    catch(Exception e){}<br>    //每个U程都进入各自的t1()ҎQ分别打印各自的i<br>    System.out.println(Thread.currentThread().getName()+" "+i);<br>  }<br>  public void run(){<br>    synchronized(this){<br>      while (true) {<br>        t1();<br>      }<br>    }<br>  }<br>}</p> <p> </p> <p><br>--------------------------------------------------------------------------------</p> <p> </p> <p>    下面我们来讲synchronized?U用法吧:</p> <p>    1.Ҏ声明时?攑֜范围操作W?public{?之后,q回cd声明(void{?之前.即一ơ只能有一个线E进入该Ҏ,其他U程要想在此时调用该Ҏ,只能排队{?当前U程(是在synchronizedҎ内部的线E?执行完该Ҏ?别的U程才能q入.<br> <br>      例如:</p> <p>      public synchronized void synMethod() {<br>        //Ҏ?br>      }</p> <p>    2.Ҏ一代码块?synchronized后跟括号,括号里是变量,q样,一ơ只有一个线E进入该代码?例如:</p> <p>      public int synMethod(int a1){<br>        synchronized(a1) {<br>          //一ơ只能有一个线E进?br>        }<br>      }<br>    3.synchronized后面括号里是一对象,此时,U程获得的是对象?例如:</p> <p>public class MyThread implements Runnable {<br>  public static void main(String args[]) {<br>    MyThread mt = new MyThread();<br>    Thread t1 = new Thread(mt, "t1");<br>    Thread t2 = new Thread(mt, "t2");<br>    Thread t3 = new Thread(mt, "t3");<br>    Thread t4 = new Thread(mt, "t4");<br>    Thread t5 = new Thread(mt, "t5");<br>    Thread t6 = new Thread(mt, "t6");<br>    t1.start();<br>    t2.start();<br>    t3.start();<br>    t4.start();<br>    t5.start();<br>    t6.start();<br>  }</p> <p>  public void run() {<br>    synchronized (this) {<br>      System.out.println(Thread.currentThread().getName());<br>    }<br>  }<br>} </p> <p><br> <br>    对于3,如果U程q入,则得到对象锁,那么别的U程在该cL有对象上的Q何操作都不能q行.在对象使用锁通常是一U比较粗p的Ҏ。ؓ什么要整个对象都上锁Q而不允许其他U程短暂C用对象中其他同步Ҏ来访问共享资源?如果一个对象拥有多个资源,׃需要只Z让一个线E用其中一部分资源Q就所有线E都锁在外面。由于每个对象都有锁Q可以如下所CZ用虚拟对象来上锁Q?/p> <p>class FineGrainLock {</p> <p>   MyMemberClass x, y;<br>   Object xlock = new Object(), ylock = new Object();</p> <p>   public void foo() {<br>      synchronized(xlock) {<br>         //access x here<br>      }</p> <p>      //do something here - but don't use shared resources</p> <p>      synchronized(ylock) {<br>         //access y here<br>      }<br>   }</p> <p>   public void bar() {<br>      synchronized(this) {<br>         //access both x and y here<br>      }<br>      //do something here - but don't use shared resources<br>   }<br>}</p> <p> </p> <p>    4.synchronized后面括号里是c?例如:</p> <p>class ArrayWithLockOrder{<br>  private static long num_locks = 0;<br>  private long lock_order;<br>  private int[] arr;</p> <p>  public ArrayWithLockOrder(int[] a)<br>  {<br>    arr = a;<br>    synchronized(ArrayWithLockOrder.class) {//-----------------------------------------q里<br>      num_locks++;             // 锁数?1?br>      lock_order = num_locks;  // 为此对象实例讄唯一?lock_order?br>    }<br>  }<br>  public long lockOrder()<br>  {<br>    return lock_order;<br>  }<br>  public int[] array()<br>  {<br>    return arr;<br>  }<br>}</p> <p>class SomeClass implements Runnable<br>{<br>  public int sumArrays(ArrayWithLockOrder a1,<br>                       ArrayWithLockOrder a2)<br>  {<br>    int value = 0;<br>    ArrayWithLockOrder first = a1;       // 保留数组引用的一?br>    ArrayWithLockOrder last = a2;        // 本地副本?br>    int size = a1.array().length;<br>    if (size == a2.array().length)<br>    {<br>      if (a1.lockOrder() > a2.lockOrder())  // 定q设|对象的锁定<br>      {                                     // 序?br>        first = a2;<br>        last = a1;<br>      }<br>      synchronized(first) {              // 按正的序锁定对象?br>        synchronized(last) {<br>          int[] arr1 = a1.array();<br>          int[] arr2 = a2.array();<br>          for (int i=0; i<size; i++)<br>            value += arr1[i] + arr2[i];<br>        }<br>      }<br>    }<br>    return value;<br>  }<br>  public void run() {<br>    //...<br>  }<br>}</p> <p> </p> <p>    对于4,如果U程q入,则线E在该类中所有操作不能进?包括静态变量和静态方?实际?对于含有静态方法和静态变量的代码块的同步,我们通常?来加?</p> <p>以上4U之间的关系Q?/p> <p>    锁是和对象相兌的,每个对象有一把锁Qؓ了执行synchronized语句Q线E必能够获得synchronized语句中表辑ּ指定的对象的锁,一个对象只有一把锁Q被一个线E获得之后它׃再拥有这把锁Q线E在执行完synchronized语句后,获得锁交还l对象?br>    在方法前面加上synchronized修饰W即可以一个方法声明ؓ同步化方法。同步化Ҏ在执行之前获得一个锁。如果这是一个类ҎQ那么获得的锁是和声明方法的cȝ关的Classcd象的锁。如果这是一个实例方法,那么此锁是this对象的锁?/p> <p> </p> <p><br>--------------------------------------------------------------------------------</p> <p><br>  下面谈一谈一些常用的Ҏ:</p> <p>  wait(),wait(long),notify(),notifyAll(){方法是当前cȝ实例Ҏ,<br>    <br>        wait()是持有对象锁的U程释放?<br>        wait(long)是持有对象锁的U程释放锁时间ؓlong(毫秒)?再次获得?wait()和wait(0){h;<br>        notify()是唤醒一个正在等待该对象锁的U程,如果{待的线E不止一?那么被唤醒的U程由jvm定;<br>        notifyAll是唤醒所有正在等待该对象锁的U程.<br>        在这里我也重申一?我们应该优先使用notifyAll()Ҏ,因ؓ唤醒所有线E比唤醒一个线E更Ҏ让jvm扑ֈ最适合被唤醒的U程.</p> <p>    对于上述Ҏ,只有在当前线E中才能使用,否则报运行时错误java.lang.IllegalMonitorStateException: current thread not owner.</p> <p> </p> <p>--------------------------------------------------------------------------------</p> <p><br>    下面,我谈一下synchronized和wait()、notify(){的关系:</p> <p>1.有synchronized的地方不一定有wait,notify</p> <p>2.有wait,notify的地方必有synchronized.q是因ؓwait和notify不是属于U程c,而是每一个对象都h的方法,而且Q这两个Ҏ都和对象锁有养I有锁的地方,必有synchronized?/p> <p>另外Q请注意一点:如果要把notify和waitҎ攑֜一L的话Q必d调用notify后调用waitQ因为如果调用完waitQ该U程已l不是current thread了。如下例Q?/p> <p>/**<br> * Title:        Jdeveloper's Java Projdect<br> * Description:  n/a<br> * Copyright:    Copyright (c) 2001<br> * Company:      soho  <a >http://www.ChinaJavaWorld.com</a><br> * @author <a href="mailto:jdeveloper@21cn.com">jdeveloper@21cn.com</a><br> * @version 1.0<br> */<br>import java.lang.Runnable;<br>import java.lang.Thread;</p> <p>public class DemoThread<br>    implements Runnable {</p> <p>  public DemoThread() {<br>    TestThread testthread1 = new TestThread(this, "1");<br>    TestThread testthread2 = new TestThread(this, "2");</p> <p>    testthread2.start();<br>    testthread1.start();</p> <p>  }</p> <p>  public static void main(String[] args) {<br>    DemoThread demoThread1 = new DemoThread();</p> <p>  }</p> <p>  public void run() {</p> <p>    TestThread t = (TestThread) Thread.currentThread();<br>    try {<br>      if (!t.getName().equalsIgnoreCase("1")) {<br>        synchronized (this) {<br>          wait();<br>        }<br>      }<br>      while (true) {</p> <p>        System.out.println("@time in thread" + t.getName() + "=" +<br>                           t.increaseTime());</p> <p>        if (t.getTime() % 10 == 0) {<br>          synchronized (this) {<br>            System.out.println("****************************************");<br>            notify();<br>            if (t.getTime() == 100)<br>              break;<br>            wait();<br>          }<br>        }<br>      }<br>    }<br>    catch (Exception e) {<br>      e.printStackTrace();<br>    }<br>  }</p> <p>}</p> <p>class TestThread<br>    extends Thread {<br>  private int time = 0;<br>  public TestThread(Runnable r, String name) {<br>    super(r, name);<br>  }</p> <p>  public int getTime() {<br>    return time;<br>  }</p> <p>  public int increaseTime() {<br>    return++time;<br>  }</p> <p>}</p> <p>    下面我们用生产?消费者这个例子来说明他们之间的关p?</p> <p>    public class test {<br>  public static void main(String args[]) {<br>    Semaphore s = new Semaphore(1);<br>    Thread t1 = new Thread(s, "producer1");<br>    Thread t2 = new Thread(s, "producer2");<br>    Thread t3 = new Thread(s, "producer3");<br>    Thread t4 = new Thread(s, "consumer1");<br>    Thread t5 = new Thread(s, "consumer2");<br>    Thread t6 = new Thread(s, "consumer3");<br>    t1.start();<br>    t2.start();<br>    t3.start();<br>    t4.start();<br>    t5.start();<br>    t6.start();<br>  }<br>}</p> <p>class Semaphore<br>    implements Runnable {<br>  private int count;<br>  public Semaphore(int n) {<br>    this.count = n;<br>  }</p> <p>  public synchronized void acquire() {<br>    while (count == 0) {<br>      try {<br>        wait();<br>      }<br>      catch (InterruptedException e) {<br>        //keep trying<br>      }<br>    }<br>    count--;<br>  }</p> <p>  public synchronized void release() {<br>    while (count == 10) {<br>      try {<br>        wait();<br>      }<br>      catch (InterruptedException e) {<br>        //keep trying<br>      }<br>    }<br>    count++;<br>    notifyAll(); //alert a thread that's blocking on this semaphore<br>  }</p> <p>  public void run() {<br>    while (true) {<br>      if (Thread.currentThread().getName().substring(0,8).equalsIgnoreCase("consumer")) {<br>        acquire();<br>      }<br>      else if (Thread.currentThread().getName().substring(0,8).equalsIgnoreCase("producer")) {<br>        release();<br>      }<br>      System.out.println(Thread.currentThread().getName() + " " + count);<br>    }<br>  }<br>}</p> <p>       生者生?消费者消?一般没有冲H?但当库存??消费者要消费是不行的,但当库存Z?q里?0)?生者也不能生.请好好研M面的E序,你一定会比以前进步很?</p> <p>      上面的代码说明了synchronized和wait,notify没有l对的关p?在synchronized声明的方法、代码块?你完全可以不用wait,notify{方?但是,如果当线E对某一资源存在某种争用的情况下,你必适时得将U程攑օ{待或者唤?</p> <p> </p> <p>--------------------------------------------------------------------------------</p> <p><br>文章l于写完?基本上将我学习所得全部写出来?不过也留下些讔R?比如synchronized后是cL的深入的说明及讨?而且,文章׃自己能力有限,有些地方肯定会有错误,希望看过有何和批?请发帖在下面,我会修正该文.谢谢!<br> </p> <p> </p> <img src ="http://www.shnenglu.com/qywyh/aggbug/39317.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/qywyh/" target="_blank">?/a> 2007-12-23 03:25 <a href="http://www.shnenglu.com/qywyh/articles/39317.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【{】Java与XMLd之DOM?/title><link>http://www.shnenglu.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.shnenglu.com/qywyh/articles/39318.html</guid><wfw:comment>http://www.shnenglu.com/qywyh/comments/39318.html</wfw:comment><comments>http://www.shnenglu.com/qywyh/articles/39318.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.shnenglu.com/qywyh/comments/commentRss/39318.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/qywyh/services/trackbacks/39318.html</trackback:ping><description><![CDATA[Java与XMLd之DOM?br>DOM<br>初步<br>DOM是Document Object Model的羃写,x档对象模型。前面说q,XML数据组lؓ一颗树Q?br>所以DOM是对这颗树的一个对象描叙。通俗的说Q就是通过解析XML文档QؓXML文档?br>逻辑上徏立一个树模型Q树的节Ҏ一个个对象。我们通过存取q些对象p够存取XML文档<br>的内宏V?br>下面我们来看一个简单的例子Q看看在DOM中,我们是如何来操作一个XML文档的?br>q是一个XML文档Q也是我们要操作的对象:<br>下面Q我们需要把q个文档的内容解析到一个个的Java对象中去供程序用,利用JAXPQ我?br>只需几行代码p做到q一炏V首先,我们需要徏立一个解析器工厂Q以利用q个工厂来获?br>一个具体的解析器对象:<br>我们在这里用DocumentBuilderFacotry的目的是Z创徏与具体解析器无关的程序,?br>DocumentBuilderFactorycȝ静态方法newInstance()被调用时Q它Ҏ一个系l变量来军_具体<br>使用哪一个解析器。又因ؓ所有的解析器都服从于JAXP所定义的接口,所以无论具体用哪一<br>个解析器Q代码都是一L。所以当在不同的解析器之间进行切换时Q只需要更改系l变量的<br>|而不用更改Q何代码。这是工厂所带来的好处。这个工厂模式的具体实现Q可以参看下<br>面的cd?br>当获得一个工厂对象后Q?使用它的静态方法newDocumentBuilder() Ҏ可以获得一?br>DocumentBuilder对象Q这个对象代表了具体的DOM解析器。但具体是哪一U解析器Q微软的?br>者IBM的,对于E序而言q不重要?br>然后Q我们就可以利用q个解析器来对XML文档q行解析了:<br>DocumentBuilder的parse()Ҏ接受一个XML文档名作入参敎ͼq回一个Document对象Q这<br><?xml version="1.0" encoding="UTF-8"?><br><messages><br><message>Good-bye serialization, hello Java!</message><br></messages><br>DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();<br>DocumentBuilder db = dbf.newDocumentBuilder();<br>Document doc = db.parse("c:/xml/message.xml");<br>个Document对象׃表了一个XML文档的树模型。以后所有的对XML文档的操作,都与解析?br>无关Q直接在q个Document对象上进行操作就可以了。而具体对Document操作的方法,是?br>DOM所定义的了?br>Jaxp支持W3C所推荐的DOM 2。如果你对DOM很熟悉,那么下面的内容就很简单了Q只需要按<br>照DOM的规范来q行Ҏ调用可以。当Ӟ如果你对DOM不清楚,也不用着急,后面我们<br>会有详细的介l。在q儿Q你所要知道ƈ牢记的是QDOM是用来描叙XML文档中的数据的模型,<br>引入DOM的全部原因就是ؓ了用q个模型来操作XML文档的中的数据。DOM规范中定义有?br>点(卛_象)、属性和ҎQ我们通过q些节点的存取来存取XML的数据?br>从上面得到的Document对象开始,我们可以开始我们的DOM之旅了。用Document对象?br>getElementsByTagName()ҎQ我们可以得C个NodeList对象Q一个Node对象代表了一个XML<br>文档中的一个标{օ素,而NodeList对象Q观其名而知其意Q所代表的是一个Node对象的列表:<br>我们通过q样一条语句所得到的是XML文档中所?lt;message>标签对应的Node对象的一个列<br>表。然后,我们可以使用NodeList对象的item()Ҏ来得到列表中的每一个Node对象Q?br>当一个Node对象被徏立之后,保存在XML文档中的数据p提取出来q封装在q个Node中了?br>NodeList nl = doc.getElementsByTagName("message");<br>Node my_node = nl.item(0);<br>在这个例子中Q要提取Message标签内的内容Q我们通常会用Node对象的getNodeValue()ҎQ?br>h意,q里q用了一个getFirstChild()Ҏ来获得message下面的第一个子Node对象。虽然在<br>message 标签下面除了文本外ƈ没有其它子标{或者属性, 但是我们坚持在这里?br>getFirseChild()ҎQ这主要和W3C对DOM的定义有兟뀂W3C把标{ֆ的文本部分也定义成一<br>个NodeQ所以先要得C表文本的那个NodeQ我们才能够使用getNodeValue()来获取文本的?br>宏V?br>现在Q既然我们已l能够从XML文g中提取出数据了,我们可以把q些数据用在合适的地方Q?br>来构{应用程序?br>下面的内容,我们更多的xDOMQؓDOM作一个较l的解析Q我们使用h更ؓ<br>得心应手?br>DOM<br>详解<br>DOM的基本对象有5个:DocumentQNodeQNodeListQElement和Attr。下面就q些对象的功<br>能和实现的方法作一个大致的介绍?br>Document对象代表了整个XML的文档,所有其它的NodeQ都以一定的序包含在Document<br>String message = my_node.getFirstChild().getNodeValue();<br>1Q基本的DOM对象<br>对象之内Q排列成一个树形的l构Q程序员可以通过遍历q颗树来得到XML文档的所有的内容Q?br>q也是对XML文档操作的v炏V我们L先通过解析XML源文件而得C个Document对象Q?br>然后再来执行后箋的操作。此外,Documentq包含了创徏其它节点的方法,比如createAttribut()<br>用来创徏一个Attr对象。它所包含的主要的Ҏ有:<br>createAttribute(String)Q用l定的属性名创徏一个Attr对象Qƈ可在其后使用setAttributeNode<br>Ҏ来放|在某一个Element对象上面?br>createElement(String)Q用l定的标{֐创徏一个Element对象Q代表XML文档中的一个标{,<br>然后可以在q个Element对象上添加属性或q行其它的操作?br>createTextNode(String)Q用l定的字W串创徏一个Text对象QText对象代表了标{或者属性中<br>所包含的纯文本字符丌Ӏ如果在一个标{ֆ没有其它的标{,那么标签内的文本所代表的Text<br>对象是这个Element对象的唯一子对象?br>getElementsByTagName(String)Q返回一个NodeList对象Q它包含了所有给定标{֐字的标签?br>getDocumentElement()Q返回一个代表这个DOM树的根节点的Element对象Q也是代表XML<br>文档根元素的那个对象?br>Node对象是DOMl构中最为基本的对象Q代表了文档树中的一个抽象的节点。在实际使用?br>时候,很少会真正的用到Nodeq个对象Q而是用到诸如Element、Attr、Text{Node对象的子<br>对象来操作文档。Node对象些对象提供了一个抽象的、公q栏V虽然在Node对象中定<br>义了对其子节点进行存取的ҎQ但是有一些Node子对象,比如Text对象Q它q不存在子节点,<br>q一Ҏ要注意的。Node对象所包含的主要的Ҏ有:<br>appendChild(org.w3c.dom.Node)Qؓq个节点d一个子节点Qƈ攑֜所有子节点的最后,<br>如果q个子节点已l存在,则先把它删掉再添加进厅R?br>getFirstChild()Q如果节点存在子节点Q则q回W一个子节点Q对{的Q还有getLastChild()Ҏ<br>q回最后一个子节点?br>getNextSibling() Q?q回在DOM 树中q个节点的下一个兄弟节点, 对等的, q有<br>getPreviousSibling()Ҏq回其前一个兄弟节炏V?br>getNodeName()Q根据节点的cdq回节点的名U?br>getNodeType()Q返回节点的cd?br>getNodeValue()Q返回节点的倹{?br>hasChildNodes()Q判断是不是存在有子节点?br>hasAttributes()Q判断这个节Ҏ否存在有属性?br>getOwnerDocument()Q返回节Ҏ处的Document对象?br>insertBefore(org.w3c.dom.Node newQorg.w3c.dom.Node ref)Q在l定的一个子对象前再插入<br>一个子对象?br>removeChild(org.w3c.dom.Node)Q删除给定的子节点对象?br>replaceChild(org.w3c.dom.Node newQorg.w3c.dom.Node old)Q用一个新的Node对象代替l?br>定的子节点对象?br>NodeList对象Q顾名思义Q就是代表了一个包含了一个或者多个Node的列表。可以简单的把它<br>看成一个Node的数l,我们可以通过Ҏ来获得列表中的元素:<br>GetLength()Q返回列表的长度?br>Item(int)Q返回指定位|的Node对象?br>Element对象代表的是XML文档中的标签元素Q承于NodeQ亦是Node的最主要的子对象。在<br>标签中可以包含有属性,因而Element对象中有存取其属性的ҎQ而Q何Node中定义的ҎQ?br>也可以用在Element对象上面?br>getElementsByTagName(String)Q返回一个NodeList对象Q它包含了在q个标签中其下的子孙<br>节点中具有给定标{֐字的标签?br>getTagName()Q返回一个代表这个标{֐字的字符丌Ӏ?br>getAttribute(String)Q返回标{中l定属性名U的属性的倹{在q儿需要主要的是,应ؓXML<br>文档中允许有实体属性出玎ͼ而这个方法对q些实体属性ƈ不适用。这时候需要用?br>getAttributeNodes()Ҏ来得C个Attr对象来进行进一步的操作?br>getAttributeNode(String)Q返回一个代表给定属性名U的Attr对象?br>Attr对象代表了某个标{中的属性。Attrl承于NodeQ但是因为Attr实际上是包含在Element?br>的,它ƈ不能被看作是Element的子对象Q因而在DOM中Attrq不是DOM树的一部分Q所以Node<br>中的getparentNode()QgetpreviousSibling()和getnextSibling()q回的都是null。也是_<br>Attr其实是被看作包含它的Element对象的一部分Q它q不作ؓDOM树中单独的一个节点出现?br>q一点在使用的时候要同其它的Node子对象相区别?br>需要说明的是,上面所说的DOM对象在DOM中都是用接口定义的,在定义的时候用的是与<br>具体语言无关的IDL语言来定义的。因而,DOM其实可以在Q何面向对象的语言中实玎ͼ只要<br>它实CDOM所定义的接口和功能可以了。同Ӟ有些Ҏ在DOM中ƈ没有定义Q是用IDL<br>的属性来表达的,当被映射到具体的语言Ӟq些属性被映射为相应的Ҏ?br>有了上面的介l,怿你对DOM理解的更多了吧。下面的例子让你对DOM更加熟悉h?br>先说说这个例子到底要做的是什么吧Q我们希望在一个名为link.xml文g中保存了一些URL?br>址Q通过一个简单的E序Q我们可以通过DOM把这些URLdq显C出来,也可以反q来向这<br>个XML文g中写入加入的URL地址。很单,却很实用Q也_来例CDOM的绝大部分用法了?br>XML文g本n不复杂,׃l出它的DTD了。link.xml:<br>2QDOM实例<br><?xml version="1.0" standalone="yes"?><br><links><br><link><br><text>JSP Insider</text><br><url newWindow="no">http://www.jspinsider.com</url><br><author>JSP Insider</author><br><date><br><day>2</day><br><month>1</month><br><year>2001</year><br></date><br><description>A JSP information site.</description><br></link><br><link><br><text>The makers of Java</text><br><url newWindow="no">http://java.sun.com</url><br><author>Sun Microsystems</author><br><date><br><day>3</day><br><month>1</month><br><year>2001</year><br></date><br><description>Sun Microsystem's website.</description><br></link><br><link><br>W一个程序我们称为xmldisplay.javaQ具体的E序清单可以在附件中扑ֈ。主要的功能是?br>取这个XML文g中各个节点的内容Q然后在格式化输出在System.out上,我们来看看这个程序:<br>q是引入必要的类Q?因ؓ在这里用的是Sun 所提供的XML 解析器, 因而需要引?br>java.xml.parsers包,其中包含了有DOM解析器和SAX解析器的具体实现。org.w3c.dom包中?br>义了w3c所制定的DOM接口?br>除了上面讲到的,q有一个小技巧,对Document对象调用normalize()Q可以去掉XML文档中作<br>为格式化内容的空白而映在DOM树中的不必要的Text Node对象。否则你得到的DOM树可?br>q不如你所惌的那栗特别是在输出的时候,q个normalize()更ؓ有用?br>刚才说过QXML文档中的I白W也会被作ؓ对象映射在DOM树中。因而,直接调用NodeҎ<br>的getChildNodesҎ有时候会有些问题Q有时不能够q回所期望的NodeList对象。解决的办法<br>是用Element的getElementByTagName(String)Q返回的NodeLise是所期待的对象了。然<br>后,可以用item()Ҏ提取惌的元素?br><text>The standard JSP container</text><br><url newWindow="no">http://jakarta.apache.org</url><br><author>Apache Group</author><br><date><br><day>4</day><br><month>1</month><br><year>2001</year><br></date><br><description>Some great software.</description><br></link><br></links><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<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文档内容的格式化输出。只要注意到一些细节的问题Q比?br>getFirstChile()Ҏ和getElementsByTagName()Ҏ的用,q些q是比较Ҏ的?br>下面的内容,是在修改了DOM树后重新写入到XML文档中去的问题了。这个程序名?br>xmlwrite.java。在JAXP1.0版本中,q没有直接的cdҎ能够处理XML文档的写入问题,需<br>要借助其它包中的一些辅助类。而在JAXP1.1版本中,引入了对XSLT的支持,所谓XSLTQ就<br>是对XML文档q行变换QTranslationQ后Q得C个新的文档结构。利用这个新加入的功能,<br>我们p够很方便的把新生成或者修改后的DOM树从新写回到XML文g中去了,下面我们来看<br>看代码的实现Q这D代码的主要功能是向links.xml文g中加入一个新的link节点Q?br>新引入的java.xml.transform包中的几个类Q就是用来处理XSLT变换的?br>我们希望在上面的XML文g中加入一个新的link节点Q因而首先还是要dlinks.xml文gQ构?br>一个DOM树,然后再对q个DOM树进行修改(d节点Q,最后把修改后的DOM写回到links.xml<br>文g中:<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>Z看清重点Q简化程序,我们把要加入的内容硬~码到记忆String对象中,而实际操作中Q往<br>往利用一个界面来提取用户输入Q或者通过JDBC从数据库中提取想要的内容?br>首先应该明了的是Q无Z么类型的NodeQText型的也好QAttr型的也好QElement型的也好Q?br>它们的创建都是通过Document对象中的createXXX()Ҏ来创建的QXXX代表具体要创建的c?br>型)Q因此,我们要向XML文档中添加一个link目Q首先要创徏一个link对象Q?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 ;<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>创徏节点的过E可能有些千一律,但需要注意的地方是,对Element中所包含的textQ在DOM<br>中,q些text也是代表了一个Node的,因此也必Mؓ它们创徏相应的nodeQ,不能直接用Element<br>对象的setNodeValue()Ҏ来设|这些text的内容,而需要用创徏的Text对象的setNodeValue()<br>Ҏ来设|文本,q样才能够把创徏的Element和其文本内容d到DOM树中。看看前面的?br>码,你会更好的理解这一点:<br>最后,不要忘记把创建好的节Ҏ加到DOM树中。DocumentcȝgetDocumentElement()ҎQ?br>q回代表文档根节点的Element对象。在XML文档中,根节点一定是唯一的?br>然后是用XSLT把DOM树输Z。这里的TransformerFactory也同样应用了工厂模式Q得具<br>体的代码同具体的变换器无兟뀂实现的Ҏ和DocumentBuilderFactory相同Q这儿就不赘qC?br>TransformercȝtransfromҎ接受两个参数、一个数据源Source和一个输出目标Result。这?br>分别使用的是DOMSource和StreamResultQ这样就能够把DOM的内容输出到一个输出流中,<br>当这个输出流是一个文件的时候,DOM的内容就被写入到文g中去了?br>好了Q关于DOM的话题就先到q里Q下文章将介绍的内ҎSAX。请看Java与XMLd?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.shnenglu.com/qywyh/aggbug/39318.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/qywyh/" target="_blank">?/a> 2007-12-23 03:25 <a href="http://www.shnenglu.com/qywyh/articles/39318.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【{】javaq解决Ҏhttp://www.shnenglu.com/qywyh/articles/39316.html?/dc:creator>?/author>Sat, 22 Dec 2007 19:24:00 GMThttp://www.shnenglu.com/qywyh/articles/39316.htmlhttp://www.shnenglu.com/qywyh/comments/39316.htmlhttp://www.shnenglu.com/qywyh/articles/39316.html#Feedback0http://www.shnenglu.com/qywyh/comments/commentRss/39316.htmlhttp://www.shnenglu.com/qywyh/services/trackbacks/39316.html1.字节和unicode
java内核是unicode的,pclass文g也是Q但是很多媒体,包括文g/的保存方式是用字节流的。因此java要对q些字节经行{化。char是unicode的,而byte是字节。java中byte/char互{的函数在sun.io的包中间有?br>其中ByteToCharConvertercL中调度,可以用来告诉你,你用的convertor。其中两个很常用的静态函数是
public static ByteToCharConverter getDefault();
public static ByteToCharConverter getConverter(String encoding);
如果你不指定converterQ则pȝ会自动用当前的encoding,gbq_上用gbk,enq_上用8859_1?/p>


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);
}



]]>
【{】?Java Reflection http://www.shnenglu.com/qywyh/articles/34523.html?/dc:creator>?/author>Thu, 18 Oct 2007 03:05:00 GMThttp://www.shnenglu.com/qywyh/articles/34523.htmlhttp://www.shnenglu.com/qywyh/comments/34523.htmlhttp://www.shnenglu.com/qywyh/articles/34523.html#Feedback0http://www.shnenglu.com/qywyh/comments/commentRss/34523.htmlhttp://www.shnenglu.com/qywyh/services/trackbacks/34523.html

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;



]]>
Ʒþþþþ| 99鶹þþùƷ | ˹ھƷþþþӰԺ| ˼˼þ99ֻƵƷ66| ٸþþþþñŪ߳ | ĻþþƷAPP| þ99žŹѿС˵| ҹƷþþþ| һɫۺþ| 99þ99ֻѷѾƷ| þþþƷѹĻ| ӰɫۺϾþ| þþƷ99þ˿ | ޹þþþƷ| Ʒþþø| þþƷŷպ| þþƷҹҹŷ| þùһ| ľþþƷ| þ޹ҹƷƬ| þþƷ?Ļ| ޹Ʒþþ| þԭۺ| þþþѾƷ| Ʒgzþþ| ҹƷþþþþëƬ| þþƷƷް| þþþþþþþþþƷ| Ʒžžžþþž| ޹˾Ʒ91þþ| ŷ߽˾þ| ޹þþþƷС˵| ƷþƷ| 91Ʒþþþþù۲| þۺɫ| þþþþþþþѾƷ| þù޾Ʒ鶹| þ͵wcŮ | ĻþþƷAPP| þþƷֻоƷ66| һþ|