﻿<?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>BlogJava-花-随笔分类-Java</title><link>http://www.blogjava.net/hua/category/12539.html</link><description>即使世界明天毁灭，我也要在今天种下我的葡萄树。 </description><language>zh-cn</language><lastBuildDate>Fri, 21 Sep 2007 11:17:23 GMT</lastBuildDate><pubDate>Fri, 21 Sep 2007 11:17:23 GMT</pubDate><ttl>60</ttl><item><title>ANT 下载,ant的配法 </title><link>http://www.blogjava.net/hua/archive/2007/09/18/146061.html</link><dc:creator>花</dc:creator><author>花</author><pubDate>Tue, 18 Sep 2007 01:44:00 GMT</pubDate><guid>http://www.blogjava.net/hua/archive/2007/09/18/146061.html</guid><wfw:comment>http://www.blogjava.net/hua/comments/146061.html</wfw:comment><comments>http://www.blogjava.net/hua/archive/2007/09/18/146061.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hua/comments/commentRss/146061.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hua/services/trackbacks/146061.html</trackback:ping><description><![CDATA[<p>下载http://www.apache.org/dist/ant/binaries下的apache-ant-1.6.1-bin.zip <br />
接压安装。 <br />
<br />
ant的配法： <br />
1。解压ant的包到本地目录。 <br />
2。在环境变量中设置ANT_HOME，值为你的安装目录。 <br />
3。在环境变量中设置JAVA_HOME，值为你的jdk安装目录。 <br />
4。把ANT_HOME/bin加到你系统的path目录中去。 </p>
<p>SET ANT_HOME=D:\jakarta-ant-1.5.1 //注意是Ant的安装目录，不是bin子目录 <br />
SET PATH=%PATH%;%ANT_HOME%\bin; </p>
<p><br />
在cmd模式下输入 ant -version回车，看到输出说明配置成功。 </p>
<img src ="http://www.blogjava.net/hua/aggbug/146061.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hua/" target="_blank">花</a> 2007-09-18 09:44 <a href="http://www.blogjava.net/hua/archive/2007/09/18/146061.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入 JAVA对象的复制与比较</title><link>http://www.blogjava.net/hua/archive/2007/01/10/92836.html</link><dc:creator>花</dc:creator><author>花</author><pubDate>Wed, 10 Jan 2007 02:46:00 GMT</pubDate><guid>http://www.blogjava.net/hua/archive/2007/01/10/92836.html</guid><wfw:comment>http://www.blogjava.net/hua/comments/92836.html</wfw:comment><comments>http://www.blogjava.net/hua/archive/2007/01/10/92836.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hua/comments/commentRss/92836.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hua/services/trackbacks/92836.html</trackback:ping><description><![CDATA[1.对象的复制<br />    <br />String str1 = "This is a string!"  //这里是 "对象引用" 的复制 <br />String str2 = new String(str1);  //这里是 "对象实例" 的复制 
<p>浅复制: 只复制复合对象本身.<br />深复制: 除了复制复合对象本身, 还复制了复合对象的引用的对象实例.</p><p>例如:</p><p>class Pupil{ <br />    public Pupil(String sno, String name, int age){ <br />        this.sno = new String(sno); <br />        this.name = new String(name); <br />        this.age = age; <br />    } </p><p>    public String getSno() { <br />        return sno; <br />    } </p><p>    public String getName() { <br />        return name; <br />    } </p><p>    public int getAge() { <br />        return age; <br />    } </p><p>    public void setAge(int age) { <br />        this.age = age; <br />    } </p><p>    private String sno; <br />    private String name; <br />    private int age; <br />} </p><p>public class CopyDemo { <br />    public static Pupil[] shallowCopy(Pupil[] aClass) { <br />        Pupil[] newClass = new Pupil[aClass.length]; </p><p>        //此时newClass 与aClass 指向同一块内存 <br />        for(int i=0; i&lt;aClass.length; i++) <br />            newClass[i] = aClass[i]; <br />        return newClass; <br />    } <br />     <br />    public static Pupil[] deepCopy(Pupil[] aClass) { <br />        Pupil[] newClass = new Pupil[aClass.length]; </p><p>        //此时newClass 与aClass 的相应sno , name 指向同一块内存 <br />        for(int i=0; i&lt;aClass.length; i++) { <br />            String sno = aClass[i].getSno(); <br />            String name = aClass[i].getName(); <br />            int age = aClass[i].getAge(); <br />            newClass[i] = new Pupil(sno, name, age); <br />        } </p><p>        return newClass; <br />    } </p><p>    public static Pupil[] deeperCopy(Pupil[] aClass) { <br />        Pupil[] newClass = new Pupil[aClass.length]; </p><p>        //完全的复制 <br />        for(int i=0; i&lt;aClass.length; i++) { <br />            String sno = new String(aClass[i].getSno()); <br />            String name = new String(aClass[i].getName()); <br />            int age = aClass[i].getAge(); <br />            newClass[i] = new Pupil(sno, name, age); <br />        } </p><p>        return newClass; <br />    } <br />} </p><p>2.clone()的使用</p><p>* Object.clone()<br />* Cloneable 接口<br />* CloneNotSupportedException</p><p>a. 使用Object.clone 进行复制<br />两个必须条件:<br />1.一定要将重定义后的clone() 方法定义为公有方法(在Object 类中, 它是受保护的成员,    不能直接使用)<br />2.该后代类声明实现接口 Cloneable 接口(当类实现该接口, 其任何子类也会继承该接口), 该接口实际上没有任何<br />  内容, 只是一个标识, 标志实现该接口的类提供clone() 方法.(这是接口的一种非典型用法)</p><p>public class Fraction implements Cloneable { <br />    public Object clone() { <br />        try{ <br />            return super.clone();  //call protected method <br />        } catch (CloneNotSupportedException e) { <br />            return null; <br />        } <br />    } <br />    //other methods ... <br />} </p><p><br />b.重写Object.clone()<br />例如对   private char[] cb; character buffer 进行复制<br />  <br />// add in class Cirbuf <br />        public Object clone() { <br />        try{ <br />            Cirbuf copy = (Cirbuf)super.clone(); <br />            copy.cb = (char[])cb.clone(); <br />            return copy; <br />        }catch (CloneNotSupportedException e){ <br />            throw new InternalError(e.toString()); <br />        } <br />    } </p><p>c.复制数组<br />  数组是在方法调用重以引用的形式传递的对象. 下述情况下非常适合引用来传递数组:<br />  *正在接收的方法不修改数组<br />  *正在调用的方法不必关心是否修改数组<br />  *正在调用的方法想要得到数组中的修改结果 <br />  否则, 就应该在方法调用中传递数组对象的副本. 只需调用 arrObj.clone() 方法即可完成数组arrObj 的复制操作. 随后将该数组副本强制转换为其正确类型:<br />      (type[])arrObj.clone();<br />   System.arraycopy 方法提供一种用于在数组间复制多个元素的有效方式.<br />        System.arraycopy(source, i, target, j, len)</p><p>3.对象实例的比较</p><p>例如:</p><p>    Pupil p1 = new Pupil("99184001", "zhang3", 18); <br />    Pupil p2 = new Pupil("99184001", "zhang3", 18); </p><p>a. "==" <br />   if(p1 == p2)...<br />  此次测试的是对象引用, 其结果肯定是false, 只要两个对象引用不是互为别名就不会相等.<br />b. 浅比较  false</p><p>   if(p1.getSno() == p2.getSno() &amp;&amp; p1.getName() == p2.getName() <br />     &amp;&amp; p1.getAge() == p2.getAge()) ...; </p><p>c. 深比较   true[/code]   <br />  if(p1.getSno().equals(p2.getSno()) &amp;&amp; p1.getName().equals(p2.getName())<br />     &amp;&amp; p1.getAge() == p2.getAge()) ...;[/code]<br />    JAVA API 的跟类Object 也提供了equals() 方法, 但它只是比较两个对象引用, 而非比较两个对象实例.<br />    不管怎样, 如果需要比较Pupil 类的对象(例如要将它们放入对象容器), 应该为Pupil 类重定义equals() 方法:<br />    <br />    public boolean equals(Object otherobj) { <br />        //检查otherobj 是否为空 <br />        if(otherobj == null) return false; </p><p>        //检查otherobj 是否就是当前对象 <br />        if(otherobj == this) return true; </p><p>        //检查otherobj 是否具有正确的类型, 即检查是否可与当前对象比较 <br />        if(!(otherobj instanceof Pupil)) return false; </p><p>        //将otherobj 转换为Pupil 类的对象引用 <br />        Pupil tmpObj = (Pupil)otherobj; <br />        //关于学生是否相等的逻辑检查 <br />        if(sno.equals(tmpObj.sno) &amp;&amp; name.equals(tmpObj.name) <br />             &amp;&amp; age == tmpObj.age) return true; <br />         <br />        return false; <br />    } </p><p>   JAVA API 所提供的每个类几乎都提供了采用深比较策略的equals() 方法, 例如String 类equals() 方法. 一般来说, 用户自己定义的类也应当提供合适的equals() 方法, 特别是当程序要将其对象放入JAVA API 所提供的对象容器类的时候.  <br />   按照约定, 任何类所提供的equals() 方法所实现的相等比较应该是等价关系, 即满足自反性, 对称性和传递性. 另外一个类重定义了equals() 方法, 也应该重定义相应hashCode() 方法, 否则将这个类的对象放入映射对象容器时也会发生以外.<br /></p><img src ="http://www.blogjava.net/hua/aggbug/92836.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hua/" target="_blank">花</a> 2007-01-10 10:46 <a href="http://www.blogjava.net/hua/archive/2007/01/10/92836.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java源码分析：深入探讨Iterator模式 </title><link>http://www.blogjava.net/hua/archive/2006/12/30/90946.html</link><dc:creator>花</dc:creator><author>花</author><pubDate>Sat, 30 Dec 2006 01:43:00 GMT</pubDate><guid>http://www.blogjava.net/hua/archive/2006/12/30/90946.html</guid><wfw:comment>http://www.blogjava.net/hua/comments/90946.html</wfw:comment><comments>http://www.blogjava.net/hua/archive/2006/12/30/90946.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hua/comments/commentRss/90946.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hua/services/trackbacks/90946.html</trackback:ping><description><![CDATA[java.util包中包含了一系列重要的集合类。本文将从分析源码入手，深入研究一个集合类的内部结构，以及遍历集合的迭代模式的源码实现内幕。 <br /><br />　　下面我们先简单讨论一个根接口Collection，然后分析一个抽象类AbstractList和它的对应Iterator接口，并仔细研究迭代子模式的实现原理。<br /><br />　　本文讨论的源代码版本是JDK 1.4.2，因为JDK 1.5在java.util中使用了很多泛型代码，为了简化问题，所以我们还是讨论1.4版本的代码。<br /><br />　　<b>集合类的根接口Collection</b><br /><br />　　Collection接口是所有集合类的根类型。它的一个主要的接口方法是：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>boolean add(Object c)</td></tr></tbody></table><br />　　add()方法将添加一个新元素。注意这个方法会返回一个boolean，但是返回值不是表示添加成功与否。仔细阅读doc可以看到，Collection规定：如果一个集合拒绝添加这个元素，无论任何原因，都必须抛出异常。这个返回值表示的意义是add()方法执行后，集合的内容是否改变了（就是元素有无数量，位置等变化），这是由具体类实现的。即：如果方法出错，总会抛出异常；返回值仅仅表示该方法执行后这个Collection的内容有无变化。<br /><br />　　类似的还有：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>boolean addAll(Collection c);<br />boolean remove(Object o);<br />boolean removeAll(Collection c);<br />boolean remainAll(Collection c);</td></tr></tbody></table><br />　　Object[] toArray()方法很简单，把集合转换成数组返回。Object[] toArray(Object[] a)方法就有点复杂了，首先，返回的Object[]仍然是把集合的所有元素变成的数组，但是类型和参数a的类型是相同的，比如执行：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>String[] o = (String[])c.toArray(new String[0]);</td></tr></tbody></table><br />　　得到的o实际类型是String[]。<br /><br />　　其次，如果参数a的大小装不下集合的所有元素，返回的将是一个新的数组。如果参数a的大小能装下集合的所有元素，则返回的还是a，但a的内容用集合的元素来填充。尤其要注意的是，如果a的大小比集合元素的个数还多，a后面的部分全部被置为null。<br /><br />　　最后一个最重要的方法是iterator()，返回一个Iterator（迭代子），用于遍历集合的所有元素。<br /><br />　　<b>用Iterator模式实现遍历集合</b><br />　<br />　　Iterator模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来，从而避免向客户端暴露集合的内部结构。<br /><br />　　例如，如果没有使用Iterator，遍历一个数组的方法是使用索引：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>for(int i=0; i&lt;array.size(); i++) { ... get(i) ... }</td></tr></tbody></table><br />　　而访问一个链表（LinkedList）又必须使用while循环：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>while((e=e.next())!=null) { ... e.data() ... }</td></tr></tbody></table><br />　　以上两种方法客户端都必须事先知道集合的内部结构，访问代码和集合本身是紧耦合，无法将访问逻辑从集合类和客户端代码中分离出来，每一种集合对应一种遍历方法，客户端代码无法复用。<br /><br />　　更恐怖的是，如果以后需要把ArrayList更换为LinkedList，则原来的客户端代码必须全部重写。<br /><br />　　为解决以上问题，Iterator模式总是用同一种逻辑来遍历集合：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>for(Iterator it = c.iterater(); it.hasNext(); ) { ... }</td></tr></tbody></table><br />　　奥秘在于客户端自身不维护遍历集合的"指针"，所有的内部状态（如当前元素位置，是否有下一个元素）都由Iterator来维护，而这个Iterator由集合类通过工厂方法生成，因此，它知道如何遍历整个集合。<br /><br />　　客户端从不直接和集合类打交道，它总是控制Iterator，向它发送"向前"，"向后"，"取当前元素"的命令，就可以间接遍历整个集合。<br /><br />　　首先看看java.util.Iterator接口的定义：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>public interface Iterator {<br />　boolean hasNext();<br />　Object next();<br />　void remove();<br />}</td></tr></tbody></table><br />　　依赖前两个方法就能完成遍历，典型的代码如下：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>for(Iterator it = c.iterator(); it.hasNext(); ) {<br />　Object o = it.next();<br />　// 对o的操作...<br />}</td></tr></tbody></table><br />　　在JDK1.5中，还对上面的代码在语法上作了简化：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>// Type是具体的类型，如String。<br />for(Type t : c) {<br />// 对t的操作...<br />}</td></tr></tbody></table><br />　　每一种集合类返回的Iterator具体类型可能不同，Array可能返回ArrayIterator，Set可能返回SetIterator，Tree可能返回TreeIterator，但是它们都实现了Iterator接口，因此，客户端不关心到底是哪种Iterator，它只需要获得这个Iterator接口即可，这就是面向对象的威力。<br /><br />　<b>Iterator源码剖析</b><br /><br />　　让我们来看看AbstracyList如何创建Iterator。首先AbstractList定义了一个内部类（inner class）：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>private class Itr implements Iterator {<br />...<br />}</td></tr></tbody></table><br />　　而iterator()方法的定义是：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>public Iterator iterator() {<br />　return new Itr();<br />}</td></tr></tbody></table><br />　　因此客户端不知道它通过Iterator it = a.iterator();所获得的Iterator的真正类型。<br /><br />　　现在我们关心的是这个申明为private的Itr类是如何实现遍历AbstractList的：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>private class Itr implements Iterator {<br />　int cursor = 0;<br />　int lastRet = -1;<br />　int expectedModCount = modCount;<br />}</td></tr></tbody></table><br />　　Itr类依靠3个int变量（还有一个隐含的AbstractList的引用）来实现遍历，cursor是下一次next()调用时元素的位置，第一次调用next()将返回索引为0的元素。lastRet记录上一次游标所在位置，因此它总是比cursor少1。<br /><br />　　变量cursor和集合的元素个数决定hasNext()：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>public boolean hasNext() {<br />　return cursor != size();<br />}</td></tr></tbody></table><br />　　方法next()返回的是索引为cursor的元素，然后修改cursor和lastRet的值：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>public Object next() {<br />　checkForComodification();<br />　try {<br />　　Object next = get(cursor);<br />　　lastRet = cursor++;<br />　　return next;<br />　} catch(IndexOutOfBoundsException e) {<br />　　checkForComodification();<br />　　throw new NoSuchElementException();<br />　}<br />}</td></tr></tbody></table><br />　　expectedModCount表示期待的modCount值，用来判断在遍历过程中集合是否被修改过。AbstractList包含一个modCount变量，它的初始值是0，当集合每被修改一次时（调用add，remove等方法），modCount加1。因此，modCount如果不变，表示集合内容未被修改。<br /><br />　　Itr初始化时用expectedModCount记录集合的modCount变量，此后在必要的地方它会检测modCount的值：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>final void checkForComodification() {<br />　if (modCount != expectedModCount)<br />　　throw new ConcurrentModificationException();<br />}</td></tr></tbody></table><br />　　如果modCount与一开始记录在expectedModeCount中的值不等，说明集合内容被修改过，此时会抛出ConcurrentModificationException。<br /><br />　　这个ConcurrentModificationException是RuntimeException，不要在客户端捕获它。如果发生此异常，说明程序代码的编写有问题，应该仔细检查代码而不是在catch中忽略它。<br /><br />　　但是调用Iterator自身的remove()方法删除当前元素是完全没有问题的，因为在这个方法中会自动同步expectedModCount和modCount的值：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>public void remove() {<br />...<br />AbstractList.this.remove(lastRet);<br />...<br />// 在调用了集合的remove()方法之后重新设置了expectedModCount：<br />expectedModCount = modCount;<br />...<br />}</td></tr></tbody></table><br />　　要确保遍历过程顺利完成，必须保证遍历过程中不更改集合的内容（Iterator的remove()方法除外），因此，确保遍历可靠的原则是只在一个线程中使用这个集合，或者在多线程中对遍历代码进行同步。<br /><br />　　最后给个完整的示例：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>Collection c = new ArrayList();<br />c.add("abc");<br />c.add("xyz");<br />for(Iterator it = c.iterator(); it.hasNext(); ) {<br />　String s = (String)it.next();<br />　System.out.println(s);<br />}</td></tr></tbody></table><br />　　如果你把第一行代码的ArrayList换成LinkedList或Vector，剩下的代码不用改动一行就能编译，而且功能不变，这就是针对抽象编程的原则：对具体类的依赖性最小<img src ="http://www.blogjava.net/hua/aggbug/90946.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hua/" target="_blank">花</a> 2006-12-30 09:43 <a href="http://www.blogjava.net/hua/archive/2006/12/30/90946.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JS的正则表达式</title><link>http://www.blogjava.net/hua/archive/2006/12/22/89504.html</link><dc:creator>花</dc:creator><author>花</author><pubDate>Fri, 22 Dec 2006 05:51:00 GMT</pubDate><guid>http://www.blogjava.net/hua/archive/2006/12/22/89504.html</guid><wfw:comment>http://www.blogjava.net/hua/comments/89504.html</wfw:comment><comments>http://www.blogjava.net/hua/archive/2006/12/22/89504.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hua/comments/commentRss/89504.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hua/services/trackbacks/89504.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: //校验是否全由数字组成 								代码																																																																																						function isDigit(s)   																															...&nbsp;&nbsp;<a href='http://www.blogjava.net/hua/archive/2006/12/22/89504.html'>阅读全文</a><img src ="http://www.blogjava.net/hua/aggbug/89504.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hua/" target="_blank">花</a> 2006-12-22 13:51 <a href="http://www.blogjava.net/hua/archive/2006/12/22/89504.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Java中文问题及最优解决方法</title><link>http://www.blogjava.net/hua/archive/2006/09/26/72057.html</link><dc:creator>花</dc:creator><author>花</author><pubDate>Tue, 26 Sep 2006 08:54:00 GMT</pubDate><guid>http://www.blogjava.net/hua/archive/2006/09/26/72057.html</guid><wfw:comment>http://www.blogjava.net/hua/comments/72057.html</wfw:comment><comments>http://www.blogjava.net/hua/archive/2006/09/26/72057.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hua/comments/commentRss/72057.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hua/services/trackbacks/72057.html</trackback:ping><description><![CDATA[
		<font style="BACKGROUND-COLOR: #ffffff" color="#000000">深入Java中文问题及最优解决方法</font>
		<div id="msgcns!AADDE4734A6787F!116">
				<div>
						<font style="BACKGROUND-COLOR: #ffffff" color="#000000">　Abstract：本文深入分析了Java程序设计中Java编译器对java源文件和JVM对class类文件的编码/解码过程，通过此过程的解析透视出了Java编程中中文问题产生的根本原因，最后给出了建议的最优化的解决Java中文问题的方法。  </font>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff">
										<font color="#000000">　　<strong>1、中文问题的来源 </strong></font>
								</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    计算机最初的操作系统支持的编码是单字节的字符编码，于是，在计算机中一切处理程序最初都是以单字节编码的英文为准进行处理。随着计算机的发展，为了适应世界其它民族的语言（当然包括我们的汉字），人们提出了UNICODE编码，它采用双字节编码，兼容英文字符和其它民族的双字节字符编码，所以，目前，大多数国际***的软件内部均采用UNICODE编码，在软件运行时，它获得本地支持系统（多数时间是操作系统）默认支持的编码格式，然后再将软件内部的UNICODE转化为本地系统默认支持的格式显示出来。Java的JDK和JVM即是如此，我这里说的JDK是指国际版的JDK，我们大多数程序员使用的是国际化的JDK版本，以下所有的JDK均指国际化的JDK版本。我们的汉字是双字节编码语言，为了能让计算机处理中文，我们自己制定的gb2312、GBK、GBK2K等标准以适应计算机处理的需求。所以，大部分的操作系统为了适应我们处理中文的需求，均定制有中文操作系统，它们采用的是GBK,GB2312编码格式以正确显示我们的汉字。如：中文Win2K默认采用的是GBK编码显示，在中文WIN2k中保存文件时默认采用的保存文件的编码格式也是GBK的，即，所有在中文WIN2K中保存的文件它的内部编码默认均采用GBK编码，注意：GBK是在GB2312基础上扩充来的。</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    由于Java语言内部采用UNICODE编码，所以在JAVA程序运行时，就存在着一个从UNICODE编码和对应的操作系统及浏览器支持的编码格式转换输入、输出的问题，这个转换过程有着一系列的步骤，如果其中任何一步出错，则显示出来的汉字就会出是乱码，这就是我们常见的JAVA中文问题。</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    同时，Java是一个跨平台的编程语言，也即我们编写的程序不仅能在中文windows上运行，也能在中文Linux等系统上运行，同时也要求能在英文等系统上运行（我们经常看到有人把在中文win2k上编写的JAVA程序，移植到英文Linux上运行）。这种移植操作也会带来中文问题。</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    还有，有人使用英文的操作系统和英文的IE等浏览器，来运行带中文字符的程序和浏览中文网页，它们本身就不支持中文，也会带来中文问题。</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    几乎所有的浏览器默认在传递参数时都是以UTF-8编码格式来传递，而不是按中文编码传递，所以，传递中文参数时也会有问题，从而带来乱码现象。</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    总之，以上几个方面是JAVA中的中文问题的主要来源，我们把以上原因造成的程序不能正确运行而产生的问题称作：JAVA中文问题。</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff">
										<font color="#000000">　<strong>　2、JAVA编码转换的详细过程 </strong></font>
								</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    我们常见的JAVA程序包括以下类别：<br />     *直接在console上运行的类(包括可视化界面的类)<br />     *JSP代码类（注：JSP是Servlets类的变型）<br />     *Servelets类<br />     *EJB类<br />     *其它不可以直接运行的支持类</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    这些类文件中，都有可能含有中文字符串，并且我们常用前三类JAVA程序和用户直接交互，用于输出和输入字符，如：我们在JSP和Servlet中得到客户端送来的字符，这些字符也包括中文字符。无论这些JAVA类的作用如何，这些JAVA程序的生命周期都是这样的：</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    *编程人员在一定的操作系统上选择一个合适的编辑软件来实现源程序代码并以.java扩展名保存在操作系统中，例如我们在中文win2k中用记事本编辑一个java源程序；<br />     *编程人员用JDK中的javac.exe来编译这些源代码，形成.class类(JSP文件是由容器调用JDK来编译的)；<br />     *直接运行这些类或将这些类布署到WEB容器中去运行，并输出结果。<br />    那么，在这些过程中，JDK和JVM是如何将这些文件如何编码和解码并运行的呢？</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">这里，我们以中文win2k操作系统为例说明JAVA类是如何来编码和被解码的。 </font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">
								</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    <strong>第一步，</strong>我们在中文win2k中用编辑软件如记事本编写一个Java源程序文件(包括以上五类JAVA程序)，程序文件在保存时默认采用了操作系统默认支持GBK编码格式(操作系统默认支持的格式为file.encoding格式)形成了一个.java文件，也即，java程序在被编译前，我们的JAVA源程序文件是采用操作系统默认支持的file.encoding编码格式保存的，java源程序中含有中文信息字符和英文程序代码；要查看系统的file.encoding参数，可以用以下代码：<br />　　public class ShowSystemDefaultEncoding {<br />　　public static void main(String[] args) {<br />　　String encoding = System.getProperty("file.encoding");<br />　　System.out.println(encoding);<br />　　}}</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">   <strong> 第二步，</strong>我们用JDK的javac.exe文件编译我们的Java源程序，由于JDK是国际版的，在编译的时候，如果我们没有用-encoding参数指定我们的JAVA源程序的编码格式，则javac.exe首先获得我们操作系统默认采用的编码格式，也即在编译java程序时，若我们不指定源程序文件的编码格式，JDK首先获得操作系统的file.encoding参数(它保存的就是操作系统默认的编码格式，如WIN2k，它的值为GBK)，然后JDK就把我们的java源程序从file.encoding编码格式转化为JAVA内部默认的UNICODE格式放入内存中。然后，javac把转换后的unicode格式的文件进行编译成.class类文件，此时.class文件是UNICODE编码的，它暂放在内存中，紧接着，JDK将此以UNICODE编码的编译后的class文件保存到我们的操作系统中形成我们见到的.class文件。对我们来说，我们最终获得的.class文件是内容以UNICODE编码格式保存的类文件，它内部包含我们源程序中的中文字符串，只不过此时它己经由file.encoding格式转化为UNICODE格式了。</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    这一步中，对于JSP源程序文件是不同的，对于JSP，这个过程是这样的：即WEB容器调用JSP编译器，JSP编译器先查看JSP文件中是否设置有文件编码格式，如果JSP文件中没有设置JSP文件的编码格式，则JSP编译器调用JDK先把JSP文件用JVM默认的字符编码格式(也即WEB容器所在的操作系统的默认的file.encoding)转化为临时的Servlet类，然后再把它编译成UNICODE格式的class类，并保存在临时文件夹中。如：在中文win2k上，WEB容器就把JSP文件从GBK编码格式转化为UNICODE格式，然后编译成临时保存的Servlet类，以响应用户的请求。</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff">
										<font color="#000000">    <strong>第三步，运行第二步编译出来的类，分为三种情况：</strong></font>
								</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff">
										<font color="#000000">    A、 直接在console上运行的类<br />    B、 EJB类和不可以直接运行的支持类(如JavaBean类)<br />    C、 JSP代码和Servlet类<br />    D、 JAVA程序和数据库之间<br />    下面我们分这四种情况来看。<br />   <strong> A、直接在console上运行的类</strong></font>
								</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    这种情况，运行该类首先需要JVM支持，即操作系统中必须安装有JRE。运行过程是这样的：首先java启动JVM，此时JVM读出操作系统中保存的class文件并把内容读入内存中，此时内存中为UNICODE格式的class类，然后JVM运行它，如果此时此类需要接收用户输入，则类会默认用file.encoding编码格式对用户输入的串进行编码并转化为unicode保存入内存（用户可以设置输入流的编码格式）。程序运行后，产生的字符串（UNICODE编码的）再回交给JVM，最后JRE把此字符串再转化为file.encoding格式(用户可以设置输出流的编码格式)传递给操作系统显示接口并输出到界面上。</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    对于这种直接在console上运行的类，它的转化过程可用图1更加明确的表示出来：</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">图1</font>
						</p>
						<p align="center">
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">
										<img style="CURSOR: pointer" src="http://www.pconline.com.cn/pcedu/empolder/gj/java/0404/pic/0430java_1.gif" />
								</font>
						</p>
						<p align="left">
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">以上每一步的转化都需要正确的编码格式转化，才能最终不出现乱码现象。<br /><br />    </font>
								<font style="BACKGROUND-COLOR: #ffffff">
										<font color="#000000">
												<strong>B、EJB类和不可以直接运行的支持类(如JavaBean类)<br /><br /></strong>    由于EJB类和不可以直接运行的支持类，它们一般不与用户直接交互输入和输出，它们常常与其它的类进行交互输入和输出，所以它们在第二步被编译后，就形成了内容是UNICODE编码的类保存在操作系统中了，以后只要它与其它的类之间的交互在参数传递过程中没有丢失，则它就会正确的运行。<br />这种EJB类和不可以直接运行的支持类, 它的转化过程可用图2更加明确的表示出来： </font>
								</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">
								</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">图2</font>
						</p>
						<p align="center">
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">
										<img style="CURSOR: pointer" src="http://www.pconline.com.cn/pcedu/empolder/gj/java/0404/pic/0430java_2.gif" />
								</font>
						</p>
						<p>
								<br />
								<font style="BACKGROUND-COLOR: #ffffff">
										<font color="#000000">   <strong> C、JSP代码和Servlet类</strong></font>
								</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    经过第二步后，JSP文件也被转化为Servlets类文件，只不过它不像标准的Servlets一校存在于classes目录中，它存在于WEB容器的临时目录中，故这一步中我们也把它做为Servlets来看。</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    对于Servlets，客户端请求它时，WEB容器调用它的JVM来运行Servlet，首先，JVM把Servlet的class类从系统中读出并装入内存中，内存中是以UNICODE编码的Servlet类的代码，然后JVM在内存中运行该Servlet类，如果Servlet在运行的过程中，需要接受从客户端传来的字符如：表单输入的值和URL中传入的值，此时如果程序中没有设定接受参数时采用的编码格式，则WEB容器会默认采用ISO-8859-1编码格式来接受传入的值并在JVM中转化为UNICODE格式的保存在WEB容器的内存中。Servlet运行后生成输出，输出的字符串是UNICODE格式的，紧接着，容器将Servlet运行产生的UNICODE格式的串（如html语法，用户输出的串等）直接发送到客户端浏览器上并输出给用户，如果此时指定了发送时输出的编码格式，则按指定的编码格式输出到浏览器上，如果没有指定，则默认按ISO-8859-1编码发送到客户的浏览器上。这种JSP代码和Servlet类，它的转化过程可用图3更加明确地表示出来：</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">图3</font>
						</p>
						<p align="center">
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">
										<img style="CURSOR: pointer" src="http://www.pconline.com.cn/pcedu/empolder/gj/java/0404/pic/0430java_3.gif" />
								</font>
						</p>
						<p align="left">
								<font style="BACKGROUND-COLOR: #ffffff">
										<font color="#000000">
												<strong>D、Java程序和数据库之间</strong>
										</font>
								</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">
								</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    对于几乎所有数据库的JDBC驱动程序，默认的在JAVA程序和数据库之间传递数据都是以ISO-8859-1为默认编码格式的，所以，我们的程序在向数据库内存储包含中文的数据时，JDBC首先是把程序内部的UNICODE编码格式的数据转化为ISO-8859-1的格式，然后传递到数据库中，在数据库保存数据时，它默认即以ISO-8859-1保存，所以，这是为什么我们常常在数据库中读出的中文数据是乱码。<br />    对于JAVA程序和数据库之间的数据传递，我们可以用图4清晰地表示出来</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">图4</font>
						</p>
						<p align="center">
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">
										<img style="CURSOR: pointer" src="http://www.pconline.com.cn/pcedu/empolder/gj/java/0404/pic/0430java_4.gif" />
								</font>
						</p>
						<p>
								<br />
								<font style="BACKGROUND-COLOR: #ffffff">
										<font color="#000000">
												<strong>    3、分析常见的JAVA中文问题几个必须清楚的原则<br /></strong>
												<br />    首先，经过上面的详细分析，我们可以清晰地看到，任何JAVA程序的生命期中，其编码转换的关键过程是在于：最初编译成class文件的转码和最终向用户输出的转码过程。<br />    其次，我们必须了解JAVA在编译时支持的、常用的编码格式有以下几种：<br />    *ISO-8859-1，8-bit, 同8859_1,ISO-8859-1,ISO_8859_1等编码<br />    *Cp1252，美国英语编码，同ANSI标准编码<br />    *UTF-8，同unicode编码<br />    *GB2312，同gb2312-80,gb2312-1980等编码<br />    *GBK , 同MS936，它是gb2312的扩充<br />    及其它的编码，如韩文、日文、繁体中文等。同时，我们要注意这些编码间的兼容关体系如下：<br />    unicode和UTF-8编码是一一对应的关系。GB2312可以认为是GBK的子集，即GBK编码是在gb2312上扩展来的。同时，GBK编码包含了20902个汉字，编码范围为：0x8140-0xfefe，所有的字符可以一一对应到UNICODE2.0中来。</font>
								</font>
						</p>
						<p>
								<font style="BACKGROUND-COLOR: #ffffff" color="#000000">    再次，对于放在操作系统中的.java源程序文件，在编译时，我们可以指定它内容的编码格式，具体来说用-encoding来指定。注意：如果源程序中含有中文字符，而你用-encoding指定为其它的编码字符，显然是要出错的。用-encoding指定源文件的编码方式为GBK或gb2312，无论我们在什么系统上编译含有中文字符的JAVA源程序都不会有问题，它都会正确地将中文转化为UNICODE存储在class文件中。<br />    <br />    然后，我们必须清楚，几乎所有的WEB容器在其内部默认的字符编码格式都是以ISO-8859-1为默认值的，同时，几乎所有的浏览器在传递参数时都是默认以UTF-8的方式来传递参数的。所以，虽然我们的Java源文件在出入口的地方指定了正确的编码方式，但其在容器内部运行时还是以ISO-8859-1来处理的。</font>
						</p>
				</div>
		</div>
<img src ="http://www.blogjava.net/hua/aggbug/72057.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hua/" target="_blank">花</a> 2006-09-26 16:54 <a href="http://www.blogjava.net/hua/archive/2006/09/26/72057.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>javamail总结经验</title><link>http://www.blogjava.net/hua/archive/2006/08/31/66915.html</link><dc:creator>花</dc:creator><author>花</author><pubDate>Thu, 31 Aug 2006 09:39:00 GMT</pubDate><guid>http://www.blogjava.net/hua/archive/2006/08/31/66915.html</guid><wfw:comment>http://www.blogjava.net/hua/comments/66915.html</wfw:comment><comments>http://www.blogjava.net/hua/archive/2006/08/31/66915.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hua/comments/commentRss/66915.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hua/services/trackbacks/66915.html</trackback:ping><description><![CDATA[
		<h2>
				<a id="viewpost1_TitleUrl" href="/gf7/archive/2005/10/15/15569.html">javamail总结经验（一）</a>
		</h2>环境配置。为了运行javamail。我们需要在classpath里导入两个包：mail.jar和activation.jar，这是在纯DOS里或者其它简单工具使用时才需要这个classpath。如果使用像eclipes这样的工具就不需要配置。因为myeclipes已经帮你做好了。<br />为了要发送邮件和接收邮件，我们必须要遵守smtp和pop3协议，不过还有其它协议也可执行(如IMAP)。如果把本机当作服务器来发送邮件请在<a href="http://jakrata.apache.org/">http://jakrata.apache.org</a>网站里下载james邮件服务器，然后解压在本机上。运行james/bin里的run.bat就可以启动了服务器。<br />运行后在DOS里登录邮件服务器。具体命令如下：<br />系统管理员：root<br />密码：root<br />telnet localhost 4555<br />然后就可以增加邮箱用户名了。命令如下：<br />adduser 用户名 密码<br />例：adduser test test<br />这样就建立了这样一个邮箱test@localhost 密码为：test <br />如果想要更多功能用help命令<br />注意：如果在本机装了邮件服务器的话，只能在本机局域网里申请邮箱，发送和接收。不能发送到外部的邮箱里去。如果用外部的邮件服务器像比如163和126的服务器等就可以随心所欲的发邮件了。哈哈是不是很爽呢？<br />先看看用james邮件服务器在本机上发邮件吧。先简单看一个例子。<br />import javax.mail.*;<br />import java.util.*;<br />import javax.mail.internet.*;<br />public class MyFirstMail <br />{<br />    protected Session mailSession;<br />    public MyFirstMail()throws Exception<br />    {<br />        init();<br />    }<br />    public static void main(String[] args) <br />    {<br />        try<br />        {<br />            new MyFirstMail().sendMail();<br />            System.out.print("邮件已发");<br />        }<br />        catch(Exception e)<br />        {<br />            e.printStackTrace();<br />        }<br />    }<br />//初始化服务器环境<br />    public void init()throws Exception<br />    {<br />        Properties props=new Properties();<br />        props.put("mail.transport.protocol","smtp");<br />        props.put("mail.smtp.host","localhost");<br />        props.put("mail.smtp.port","25");<br />        mailSession=Session.getDefaultInstance(props, null);;<br />    }<br />    public void sendMail()throws Exception<br />    {<br />        try<br />        {<br />            <br />//从哪里发的邮件            <br />msg.setFrom(new InternetAddress("test@localhost"));<br />    //发送到目标邮件        msg.setRecipients(Message.RecipientType.TO,InternetAddress.parse("wang@localhost"));<br />    //抄送的接收者        msg.setRecipients(Message.RecipientType.CC,InternetAddress.parse("wang@localhost"));<br />//暗送的接收者<br />msg.setRecipients(Message.RecipientType.BCC,InternetAddress.parse("wang@localhost"));<br />//设置发送时间<br />msg.setSentDate(new java.util.Date());<br />//设置邮件标题<br />msg.setSubject("a test mail");<br />//设置邮件内容<br />msg.setText("this is the email content");<br />//指定协议发送消息的对像<br />Transport transport=mailSession.getTransport("smtp");<br />//发送消息<br />Transport.send(msg);<br />}<br />catch(Exception e)<br />{<br />throw e;<br />}<br />}<br />发送完后想要看邮件就要到james-&gt;apps-&gt;james-&gt;var-&gt;mail-&gt;inboxes就可以看见每个邮箱里的邮件数了。由于发过来的是字节流，邮件包括一个邮件属性和消息，所以每一封邮件是由二个文件组成的。<br />            <br />也许没有学过的javamail的人看上去会一头雾水。不要紧，现在一一详解。先说初始化的内容的吧！<br />1、    mail.transport.protocol=smtp 这里主要说明的是邮件传输协议。<br />2、    mail.smtp.host=localhost 发送邮件的主机如果用外部邮件服务器的话，可以这样写<br />mail.smtp.host=smtp.163.com这样就以163.为服务器了。<br />3、mail.smtp.prot=25    smtp端口可以省略，缺省为25<br /><br />javax.mail.session<br />session类定义了与远程邮件系统通信的邮件会话。需要注意的是这个session不同servlet中的会话。Servlet中的会话需要共享一些信息，而mail里的session里没有这个功能，它只是用于存储与服务器建立连接会话相关信息和逻辑。Session类是javamail api最高层入口类，所有其它类都必须经由session对象生效。Session对象它管理配置选项和用于与邮件系统交互的用认证信息息。它通过使用java.util.properties对象配置邮件会话的属性如邮件服务器，用户名，密码，及整个应用程序中共享的其它信息。<br />Session并不处现任何授权操作，它只是存储这些授权信息。Session类的构造器是私有的，它不能被继承，也不能用new语名创建实例。但它提供了两个静态方法getInstance 和getDefaultInstance来获取session实例，在创建session实例时需要提供一些属性。具体实现如下：<br />    Properties props=new Properties()<br />    Props.put(“mail.transport.protocol”,”smtp”)<br />    Props.put(“mail.smtp.host”,”localhost”);<br />  Props.put(“mail.smtp.prot”,”25”)<br />//不加以认证也可以这样写<br />//Session session=Session.getDefaultInstance(props,null)<br />//如果认证传入null那么它和不使用认证调用方式一样 <br /> Session session=Session.getDefaultInstance(props)<br />Javax.mail. Message<br />经过session配置后就可以进行发送消息任务了。这由Message类来完成。Message实现了Part接口，它表示一个邮件消息，包含一系列的属性和一个消息内容。消息属性包括了消息址址消息，定义了消息内容的结构（包括内容类型），消息内容使用DataHandler对象包装实际数据。当邮件消息位于目录(folder)中时，系统还使用了一个标志位集合来描述它的状态。<br />Message是抽象类，实际使用时必须用一个子类代替以表示具体的邮件格式。比如说javamail api提供了MimeMessage类，该类扩展了Message，实现了RFC822和MIME标准。有两个构造方法：<br />//一般使用第一种<br />    Message msg=new MimeMessage(Session session)<br />Message msg=new MimeMessage(MimeMessage msg)<br />获得消息后，就可以设置消息各个部份了。在设置之前因为要涉及地址。所以讲一下地址类<br />javax.mail.Address<br />Address类表示电子邮件类，它是一个抽象类，它的子类InternetAddress提供具体实现且通常可串行化。若创建的地址只包含电子邮件，只要传递电子邮件到构造器即可如<br />InternetAddress addr=new InternetAddress();<br />addr.setAddress(“wang@126.com”)<br />或者是：InterntAddress addr=new InternetAddress(“wang@126.com”)<br />另外，InternetAddress类还提供了地址解析方法如<br />Address[] addrs=InternetAddress.parse(“wang@163.com,f@126.com,zuo@126.com”);<br />地址之后就可以发送消息了。<br />//设置发送者<br />Msg.setFrom(new InternetAddress(“test@126.com”));<br />//设置接收者,并还具有解析功能<br />Msg.setRecipients(Message.RecipientType.To,InternetAddress.parse(“zuolin0806@163.com”));<br />//抄送的接收者<br />Msg.setRecipients(Message.RecipientType.CC,InternetAddress.parse(“zuolin0806@163.com”));<br />//暗送的接收者<br />Msg.setRecipients(Message.RecipientType.CC,InternetAddress.parse(“zuolin0806@163.com”));<br />//设置消息主题<br /> msg.setSubject(“我的爱”)<br />//设置内容的基本机制，其参数Mime类型<br /> msg.setContent(“这是我的内容”,”text/html;charset=gb2312”)<br />其中的text/html表示消息内容采用的是HTML格式。如果消息格式是(text/plain)，而且使用的是MimeMessage,那么setText()方法设置邮件内容的参数（Mime类型默认为text/plain）<br />//设置邮件内容<br />msg.setText();<br />//设置发送时间<br />msg.setSentDate(new java.util.Date());<br />最后一步是就是发送了<br />javax.mail.Transport<br />该类也是抽象类，它可通过静态方法和实便方法发送消息。Transport断承service类。所以它提供了通用方法，如命名传输，连接服务器和监听传输事件等。<br />//默认<br />Transport.send(msg);<br /><br />最后看一下使用外部邮件服务器的使用方法。先看程序再讲解，注意在使用外部邮件服务器时一定要有一个类来认证。到时在讲。<br />public class MySecondMail<br />{<br />protected Session session = null;<br />//邮件用户名<br />    String mailUser = "zuolin0806";<br />    String host = "smtp.163.com";<br />    String pwd = "用户名的密码";<br />    public MySecondMail()<br />    {<br />        init();<br />    }<br /><br />    public void init()<br />    {<br />        Properties props = new Properties();<br />        props.put("mail.transpost.protocol", "smtp");<br />        props.put("mail.smtp.host", "smtp.163.com");<br />        //必须要有一个类来认证<br />props.put("mail.smtp.auth", "true");<br />props.put("mail.smpt.port", "25")<br />Email_Autherticatorbean auth =  new Email_Autherticatorbean(mailUser,pwd);<br />        //session认证<br />session = Session.getInstance(props,auth);<br />        //这个是跟踪后台消息。打印在控制台<br />session.setDebug(true);<br />   }<br />    public static void main(String[] args)<br />    {<br />        new MySecondMail().sendMails();<br />        System.out.println("send mail success!");<br />    }<br />    public void sendMails()<br />    {<br />        try<br />        {<br />            Message msg = new MimeMessage(session);<br />         msg.setFrom(new InternetAddress("zuolin0806@163.com"));<br />        msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("javaboy@126.com"));<br />       msg.setRecipients(Message.RecipientType.BCC, InternetAddress.parse("javaboy@126.com"));<br />      msg.setSentDate(new Date());<br />      msg.setSubject("this is my Threee mail");<br />      msg.setContent("this is my mail","text/html");<br />      msg.setText("我成功了。哈哈");<br /><br />     Transport transport = session.getTransport("smtp");<br />      与发送者的邮箱相连<br />transport.connect(host,mailUser,pwd);<br />      transport.send(msg);<br />        }<br />        catch (SendFailedException e)<br />        {<br />            e.printStackTrace();<br />        }<br />        catch (Exception ee)<br />        {<br />            ee.printStackTrace();<br />        }<br /><br />    }<br />}<br /><br />认证类必须继承Authenticator  例子如下:<br />import javax.mail.*;<br />public class Email_Autherticatorbean extends Authenticator<br />{<br />    private String m_username = null;<br />    private String m_userpass = null;<br />    public void setUsername(String username)<br />    {<br />        m_username = username;<br />    }<br />    public void setUserpass(String userpass)<br />    {<br />        m_userpass = userpass;<br />    }<br />    public Email_Autherticatorbean(String username, String userpass)<br />    {<br />        super();<br />        setUsername(username);<br />        setUserpass(userpass);<br />    }<br />    public PasswordAuthentication getPasswordAuthentication()<br />    {<br />        return new PasswordAuthentication(m_username,m_userpass);<br />    }<br />}<br />经过成功发送邮件后是不是有点成就感呢！但还有很多疑问比如怎么读邮件呢？看了下面章节你就会知道了<br /><img src ="http://www.blogjava.net/hua/aggbug/66915.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hua/" target="_blank">花</a> 2006-08-31 17:39 <a href="http://www.blogjava.net/hua/archive/2006/08/31/66915.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正则表达式regular expression详述 </title><link>http://www.blogjava.net/hua/archive/2006/08/30/66738.html</link><dc:creator>花</dc:creator><author>花</author><pubDate>Wed, 30 Aug 2006 15:16:00 GMT</pubDate><guid>http://www.blogjava.net/hua/archive/2006/08/30/66738.html</guid><wfw:comment>http://www.blogjava.net/hua/comments/66738.html</wfw:comment><comments>http://www.blogjava.net/hua/archive/2006/08/30/66738.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hua/comments/commentRss/66738.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hua/services/trackbacks/66738.html</trackback:ping><description><![CDATA[
		<p>正则表达式是regular expression，看来英文比中文要好理解多了，就是检查表达式符 <br />不符合规定！！正则表达式有一个功能十分强大而又十分复杂的对象RegExp，在Javascript1.2 版本以 <br />上提供。 <br />下面我们看看有关正则表达式的介绍： <br />正则表达式对象用来规范一个规范的表达式(也就是表达式符不符合特定的要求，比如是不是Email <br />地址格式等)，它具有用来检查给出的字符串是否符合规则的属性和方法。 <br />除此之外，你用RegExp构造器建立的个别正则表达式对象的属性，就已经预先定义好了正则表达式 <br />对象的静态属性，你可以随时使用它们。 <br />核心对象： <br />在Javascript 1.2, NES 3.0以上版本提供。 <br />在Javascript 1.3以后版本增加了toSource方法。 <br />建立方法： <br />文字格式或RegExp构造器函数。 <br />文字建立格式使用以下格式： <br />/pattern/flags即/模式/标记 </p>
		<p>构造器函数方法使用方法如下： <br />new RegExp("pattern"[, "flags"])即new RegExp("模式"[,"标记"]) </p>
		<p>参数： <br />pattern(模式) <br />表示正则表达式的文本 </p>
		<p>flags(标记) <br />如果指定此项，flags可以是下面值之一： <br />g: global match(全定匹配) <br />i: ignore case(忽略大小写) <br />gi: both global match and ignore case(匹配所有可能的值，也忽略大小写) </p>
		<br />
		<a name="content">
		</a>
		<p> </p>
		<p>注意：文本格式中的参数不要使用引号标记，而构造器函数的参数则要使用引号标记。所以下面的 <br />表达式建立同样的正则表达式： <br />/ab+c/i <br />　　new RegExp("ab+c", "i") </p>
		<p>描述： <br />当使用构造函数的时候，必须使用正常的字符串避开规则(在字符串中加入前导字符 )是必须的。 <br />例如，下面的两条语句是等价的： <br />re = new RegExp("<a href="file://w/"><font color="#000080">\w</font></a>+") <br />re = /w+/ </p>
		<p>下面的提供了在正则表达式中能够使用的完整对特殊字符的一个完整的列表和描述。 </p>
		<p>表1.3：正则表达式中的特殊字符： </p>
		<p>字符 <br />意义：对于字符，通常表示按字面意义，指出接着的字符为特殊字符，不作解释。 <br />例如：/b/匹配字符'b',通过在b 前面加一个反斜杠，也就是/b/，则该字符变成特殊字符，表示 <br />匹配一个单词的分界线。 <br />或者：  <br />对于几个字符，通常说明是特殊的，指出紧接着的字符不是特殊的，而应该按字面解释。 <br />例如：*是一个特殊字符，匹配任意个字符(包括0个字符)；例如：/a*/意味匹配0个或多个a。 <br />为了匹配字面上的*，在a前面加一个反斜杠；例如：/a*/匹配'a*'。 </p>
		<p>字符^ <br />意义：表示匹配的字符必须在最前边。 <br />例如：/^A/不匹配"an A,"中的'A'，但匹配"An A."中最前面的'A'。 </p>
		<p>字符$ <br />意义：与^类似，匹配最末的字符。 <br />例如：/t$/不匹配"eater"中的't'，但匹配"eat"中的't'。 </p>
		<p>字符* <br />意义：匹配*前面的字符0次或n次。 <br />例如:/bo*/匹配"A ghost booooed"中的'boooo'或"A bird warbled"中的'b'，但不匹配"A goat g <br />runted"中的任何字符。 </p>
		<p>字符+ <br />意义：匹配+号前面的字符1次或n次。等价于。 <br />例如：/a+/匹配"candy"中的'a'和"caaaaaaandy."中的所有'a'。 </p>
		<p>字符? <br />意义：匹配?前面的字符0次或1次。 <br />例如：/e?le?/匹配"angel"中的'el'和"angle."中的'le'。 </p>
		<p>字符. <br />意义：(小数点)匹配除换行符外的所有单个的字符。 <br />例如：/.n/匹配"nay, an apple is on the tree"中的'an'和'on'，但不匹配'nay'。 </p>
		<p>
				<br />字符(x) <br />意义：匹配'x'并记录匹配的值。 <br />例如：/(foo)/匹配和记录"foo bar."中的'foo'。匹配子串能被结果数组中的素[1], ..., [n] 返 <br />回，或被RegExp对象的属性$1, ..., $9返回。 </p>
		<p>字符x|y <br />意义：匹配'x'或者'y'。 <br />例如：/green|red/匹配"green apple"中的'green'和"red apple."中的'red'。 </p>
		<p>字符 <br />意义：这里的n是一个正整数。匹配前面的n个字符。 <br />例如：/a/不匹配"candy,"中的'a'，但匹配"caandy," 中的所有'a'和"caaandy."中前面的两个 <br />'a'。 </p>
		<p>字符 <br />意义：这里的n是一个正整数。匹配至少n个前面的字符。 <br />例如：/a不匹配"candy"中的'a'，但匹配"caandy"中的所有'a'和"caaaaaaandy."中的所有'a' </p>
		<p>字符 <br />意义：这里的n和m都是正整数。匹配至少n个最多m个前面的字符。 <br />例如：/a/不匹配"cndy"中的任何字符，但匹配 "candy,"中的'a'，"caandy," 中的前面两个 <br />'a'和"caaaaaaandy"中前面的三个'a'，注意：即使"caaaaaaandy" 中有很多个'a'，但只匹配前面的三 <br />个'a'即"aaa"。 </p>
		<p>字符[xyz] <br />意义：一字符列表，匹配列出中的任一字符。你可以通过连字符-指出一个字符范围。 <br />例如：[abcd]跟[a-c]一样。它们匹配"brisket"中的'b'和"ache"中的'c'。 </p>
		<p>字符[^xyz] <br />意义：一字符补集，也就是说，它匹配除了列出的字符外的所有东西。 你可以使用连字符-指出一 <br />字符范围。 <br />例如：[^abc]和[^a-c]等价，它们最早匹配"brisket"中的'r'和"chop."中的'h'。 </p>
		<p>字符[b] <br />意义：匹配一个空格(不要与b混淆) </p>
		<p>字符b <br />意义：匹配一个单词的分界线，比如一个空格(不要与[b]混淆) <br />例如：/bnw/匹配"noonday"中的'no'，/wyb/匹配"possibly yesterday."中的'ly'。 </p>
		<p>字符B <br />意义：匹配一个单词的非分界线 <br />例如：/wBn/匹配"noonday"中的'on'，/yBw/匹配"possibly yesterday."中的'ye'。 </p>
		<p>字符cX <br />意义：这里的X是一个控制字符。匹配一个字符串的控制字符。 <br />例如：/cM/匹配一个字符串中的control-M。 </p>
		<p>字符d <br />意义：匹配一个数字，等价于[0-9]。 <br />例如：/d/或/[0-9]/匹配"B2 is the suite number."中的'2'。 </p>
		<p>字符D <br />意义：匹配任何的非数字，等价于[^0-9]。 <br />例如：/D/或/[^0-9]/匹配"B2 is the suite number."中的'B'。 </p>
		<p>字符f <br />意义：匹配一个表单符 </p>
		<p>字符n <br />意义：匹配一个换行符 </p>
		<p>字符r <br />意义：匹配一个回车符 </p>
		<p>字符s <br />意义：匹配一个单个white空格符，包括空格，tab，form feed，换行符，等价于[ fnrtv]。 <br />例如：/sw*/匹配"foo bar."中的' bar'。 </p>
		<p>字符S <br />意义：匹配除white空格符以外的一个单个的字符，等价于[^ fnrtv]。 <br />例如：/S/w*匹配"foo bar."中的'foo'。 </p>
		<p>字符t <br />意义：匹配一个制表符 </p>
		<p>字符v <br />意义：匹配一个顶头制表符 </p>
		<p>字符w <br />意义：匹配所有的数字和字母以及下划线，等价于[A-Za-z0-9_]。 <br />例如：/w/匹配"apple,"中的'a'，"$5.28,"中的'5'和"3D."中的'3'。 </p>
		<p>字符W  <br />意义：匹配除数字、字母外及下划线外的其它字符，等价于[^A-Za-z0-9_]。 <br />例如：/W/或者/[^$A-Za-z0-9_]/匹配"50%."中的'%'。 </p>
		<p>字符n <br />意义：这里的n是一个正整数。匹配一个正则表达式的最后一个子串的n的值(计数左圆括号)。 </p>
		<p>例如：/apple(,)sorange1/匹配"apple, orange, cherry, peach."中的'apple, orange'，下面 <br />有一个更加完整的例子。 <br />注意：如果左圆括号中的数字比n指定的数字还小，则n取下一行的八进制escape作为描述。 </p>
		<p>字符ooctal和xhex <br />意义：这里的ooctal是一个八进制的escape值，而xhex是一个十六进制的escape值，允许在一个 <br />正则表达式中嵌入ASCII码。 </p>
		<p>
				<br />当表达式被检查的时候，文字符号提供了编辑正则表达式的方法。利用文字符号可以使到正则表达 <br />式保持为常数。例如，如果你在一个循环中使用文字符号来构造一个正则表达式，正则表达式不需进行 <br />反复编译。 <br />正则表达式对象构造器，例如，new RegExp("ab+c")，提供正则表达式的运行时编译。当你知道正 <br />则表达式的模式会变化的时候，应该使用构造函数，或者你不知道正则表达式的模式，而它们是从另外 <br />的源获得的时候，比如由用户输入时。一旦你定义好了正则表达式，该正则表达式可在任何地方使用， <br />并且可以改变，你可以使用编译方法来编译一个新的正则表达式以便重新使用。 <br />一个分离预先定义的RegExp对象可以在每个窗口中使用；也就是说，每个分离的Javascript线程运 <br />行以获得自己的RegExp对象。因为每个脚本在一个线程中是不可中断的，这就确保了不同的脚本不会覆 <br />盖RegExp对象的值。 <br />预定义的RegExp对象包含的静态属性：input, multiline, lastMatch,lastParen, leftContext,  <br />rightContext, 以及从$1到$9。input和multiline属性能被预设。其它静态属性的值是在执行个别正则 <br />表达式对象的exec和test方法后，且在执行字符串的match和replace方法后设置的。 </p>
		<p>属性 <br />注意RegExp对象的几个属性既有长名字又有短名字(象Perl)。这些名字都是指向相同的值。Perl是 <br />一种编程语言，而Javascript模仿了它的正则表达式。 </p>
		<p>属性$1, ..., $9 <br />取得匹配的子串，如果有的话 </p>
		<p>属性$_ <br />参考input </p>
		<p>属性$* <br />参考multiline </p>
		<p>属性$&amp; <br />参考lastMatch </p>
		<p>属性$+ <br />参考lastParen </p>
		<p>属性$` <br />参考leftContext </p>
		<p>属性$' <br />参考rightContext </p>
		<p>属性constructor <br />指定用来建立对象原型函 </p>
		<p>属性global <br />决定是否测试正则表达式是否不能匹配所有的字符串，或者只是与最先的冲突。 </p>
		<p>属性ignoreCase <br />决定试图匹配字符串的时候是否忽略大小写 </p>
		<p>属性input <br />当正则表达式被匹配的时候，为相反的字符串。 </p>
		<p>属性lastIndex <br />决定下一次匹配从那里开始 </p>
		<p>属性lastMatch <br />最后一个匹配的字符 </p>
		<p>属性lastParen <br />子串匹配的时候，最后一个parenthesized，如果有的话。 </p>
		<p>属性leftContext <br />最近一次匹配前的子串。 </p>
		<p>属性multiline <br />是否在串的多行中搜索。 </p>
		<p>属性prototype <br />允许附加属性到所有的对象 </p>
		<p>属性rightContext <br />最近一次匹配后的的子串。 </p>
		<p>属性source <br />模式文本 </p>
		<p> </p>
		<p> </p>
		<p>方法 <br />compile方法 <br />编译一个正则表达式对象 </p>
		<p>exec方法 <br />运行正则表达式匹配 </p>
		<p>test方法 <br />测试正则达式匹配 </p>
		<p>toSource方法 <br />返回一个对象的文字描述指定的对象；你可以使用这个值来建立一个新的对象。不考虑Object.toS <br />ource方法。 </p>
		<p>toString方法 <br />返回一个字符串描述指定的对象，不考虑Object.toString对象。 </p>
		<p>valueOf方法 <br />返回指定对角的原始值。不考虑Object.valueOf方法。 </p>
		<p>　另外，这个对象继承了对象的watch和unwatch方法 </p>
		<p> </p>
		<p>　　例子： <br />　　例１、下述示例脚本使用replace方法来转换串中的单词。在替换的文本中，脚本使用全局 RegExp <br />对象的$1和$2属性的值。注意，在作为第二个参数传递给replace方法的时候，RegExp对象的$属性的名 <br />称。 <br />&lt;script LANGUAGE="Javascript1.2"&gt; <br />re = /(w+)s(w+)/; <br />str = "John Smith"; <br />newstr=str.replace(re,"$2, $1"); <br />document.write(newstr) <br />&lt;/script&gt; <br />显示结果："Smith, John".  </p>
		<p>　　例２、下述示例脚本中，RegExp.input由Change事件处理句柄设置。在getInfo函数中，exec 方法 <br />使用RegExp.input的值作为它的参数，注意RegExp预置了$属性。 </p>
		<p>
				<br />&lt;script LANGUAGE="Javascript1.2"&gt; <br />function getInfo(abc) <br />{ <br />re = /(w+)s(d+)/; <br />re.exec(abc.value); <br />window.alert(RegExp.$1 + ", your age is " + RegExp.$2); <br />} <br />&lt;/script&gt; </p>
		<p>　　请输入你的姓和年龄，输入完后按回车键。 <br />　　&lt;FORM&gt;&lt;INPUT TYPE="TEXT" NAME="NameAge" onChange="getInfo(this);"&gt;&lt;/FORM&gt; <br />　　&lt;/HTML&gt; </p>
		<p>
				<br />$1, ..., $9属性 <br />用圆括号括着的匹配子串，如果有的话。 <br />是RegExp的属性 <br />静态，只读 </p>
		<p>在Javascript 1.2, NES 3.0以上版本提供 <br />描述：因为input是静态属性，不是个别正则表达式对象的属性。你可以使用RegExp.input 访问该 <br />属性。 </p>
		<p>能加上圆括号的子串的数量不受限制，但正则表达式对象只能保留最后9 条。如果你要访问所有的 <br />圆括号内的匹配字串，你可以使用返回的数组。 </p>
		<p>这些属性能用在RegExp.replace方法替换后的字符串(输出结果)。当使用这种方式的时候，不用预 <br />先考虑RegExp对象。下面给出例子。当正则表达式中没有包含圆括号的时候，该脚本解释成$n的字面意 <br />义。(这里的n是一个正整数)。 </p>
		<p>
				<br />例如： <br />下例脚本使用replace 方法来交换串中单词的位置。在替换后的文本字串中，脚本使用正则表达式 <br />RegExp对象的$1和$2属性的值。注意：当它们向replace方法传递参数的时候，这里没有考虑 $ 属性的 <br />RegExp对象的名称。 <br />&lt;script LANGUAGE="Javascript1.2"&gt; <br />re = /(w+)s(w+)/; <br />str = "John Smith"; <br />newstr=str.replace(re,"$2, $1"); <br />document.write(newstr) <br />&lt;/script&gt; <br />显示的输出结果为：Smith, John。 <br />  <br /></p>正则表达式详述（二）  
<p>以下这些不是正则表达式的新增对象请参阅对应的Javascript对象的属性 $_属性 参考input $*属性 <br />参考multiline $&amp;属性 参考lastMatch $+属性 参考lastParen $`属性 <br />参考leftContext $'属性 参考rightContext compile方法 在脚本运行期间编译正则表达式对象 <br />属于RegExp的方法 在Javascript 1.2, NES 3.0以上版本提供 语法： <br />regexp.compile(pattern[, flags]) 以数： regexp 正则表达式的名称，可以是变量名或文字串。  <br />pattern 正则表达式的定义文本。 flags 如果指定的话，可以是下面其中的一个： "g": 匹配所有可能的字串 <br />"i": 忽略大小写 "gi": 匹配所有可能的字串及忽略大小写 描述： <br />使用compile方法来编译一个正则表达式 created with the RegExp constructor function。这样 <br />就强制正则表达式只编译一次，而不是每次遇到正则表达式的时候都编译一次。当你确认正则表达式能 <br />保持不变的时候可使用compile 方法来编译它(在获得它的匹配模式后)，这样就可以在脚本中重复多次使用它。 <br />你亦可以使用compile 方法来改变在运行期间改变正则表达式。例如，假如正则表达式发生变化， <br />你可以使用compile方法来重新编译该对象来提高使用效率。 <br />使用该方法将改变正则表达式的source, global和ignoreCasesource属性的值。 constructor  <br />指出建立对象原型的function。注意这个属性的值由函数本身提供，而不是一个字串包含RegExp的name.Property提供。  <br />在Javascript 1.1, NES 2.0以上版本提供 ECMA版本ECMA-262 描述：参考Object.constructor. <br />exec方法 在指定的字符串运行匹配搜索。返回一个结果数组。 是RegExp的方法  <br />在Javascript 1.2, NES 3.0以上版本提供 语法： regexp.exec([str])regexp([str]) <br />参数： regexp，正则表达式的名称，可以是一个变量名或文字定义串。  <br />str，要匹配正则表达式的字符串，如果省略，将使用RegExp.input的值。  <br />描述：就如在语法描述中的一样，正则表达工的exec方法能够被直接调用(使用regexp.exec(str))或者间接调用(使用regexp(str))。 <br />假如你只是运行以找出是否匹配，可以使用String搜索方法。 <br />假如匹配成功，exec方法返回一个数组并且更新正则表达式对象属性的值和预先定义的正则表达式对象、RegExp。如果匹配失败，exec方法返回null。 <br />请看下例： &lt;script LANGUAGE="Javascript1.2"&gt; //匹配一个b接着一个或多个d，再接着一个b <br />//忽略大小写 myRe=/d(b+)(d)/ig; myArray = myRe.exec("cdbBdbsbz"); <br />&lt;/script&gt; 下面是该脚本的返回值：对象 属性/Index 描述 例子  <br />myArray </p><p>myArray的内容 ["dbBd", "bB", "d"]  <br />index <br />基于0的匹配index 1  <br />input <br />原始字符串 cdbBdbsbz  <br />[0] <br />最后匹配的字符 dbBd  <br />[1], ...[n] <br />用圆括号括住的匹配字符串，如果有的话。不限制括号的个数。 [1] = bB <br />[2] = d  <br />myRe <br />lastIndex <br />开始下次匹配操作的index值 5  <br />ignoreCase <br />指出"i"是否使用以忽略大小写 true  <br />global <br />指出是否使用"g"标记来进行匹配所有可能的字串 true  <br />source <br />定义模式的文本字符串 d(b+)(d)  <br />RegExp <br />lastMatch$&amp; <br />最后匹配的字符 dbBd  <br />leftContext$Q <br />最新匹配前面的子串 c  <br />rightContext$' <br />最新匹配后面的子串 bsbz  <br />$1, ...$9 <br />圆括号内的匹配子串，如果有的话。圆括号的个数不受限制，但RegExp只能保留最后9个 $1 = bB  <br />$2 = d  <br />lastParen $+ <br />最后一个加上圆括号的匹配子串，如果有的话 d  </p><p>假如你的正则表达式使用了"g"标记，你可以多次使用exec 方法来连续匹配相同的串。当你这样做 <br />的时候，新的匹配将从由正则表达式的lastIndex 属性值确定的子串中开始。例如，假定你使用下面的脚本： <br />&lt;script LANGUAGE="Javascript1.2"&gt; myRe=/ab*/g;str = "abbcdefabh" <br />myArray = myRe.exec(str); <br />document.writeln("Found "+myArray[0]+". Next match starts at "+myRe.lastIndex) <br />mySecondArray = myRe.exec(str); <br />document.writeln("Found "+mySecondArray[0]+". Next match starts at "+myRe.lastIndex) <br />&lt;/script&gt; 这个脚本显示如下结果： Found abb. Next match starts at 3 <br />Found ab. Next match starts at 9 例子： <br />在下面的例子中，用户输入一个名字，脚本根据输入执行匹配操作。接着检查数组看是否和其它用户的名字匹配。 <br />本脚本假定已注册的用户的姓已经存进了数组A中，或许从一个数据库中取得。 &lt;HTML&gt; <br />&lt;script LANGUAGE="Javascript1.2"&gt; A = ["zhao","qian","sun","li","liang"] <br />function lookup() { firstName = /w+/i(); if (!firstName) <br />window.alert (RegExp.input + "非法输入"); else { count=0; <br />for (i=0;i 输入你的姓然后按回车键。 <br />&lt;FORM&gt;&lt;INPUT TYPE:"TEXT" NAME="FirstName" onChange="lookup(this);"&gt;&lt;/FORM&gt; <br />&lt;/HTML&gt; global属性 正则表达式中是否使用了"g"标记。 RegExp属性，只读 <br />在Javascript 1.2, NES 3.0以上版本提供 描述： global是一个个别正则表达式对象的属性 <br />如果使用了"g"标记，global的值为true；否则为 false。"g"标记指定正则表达式测试所有可能的匹配。 <br />你不能直接改变该属性的值，但可以调用compile方法来改变它。 ignoreCase 检查正则表达式是否使用了"i"标记 <br />RegExp属性，只读 在Javascript 1.2, NES 3.0以上版本提供 描述： <br />ignoreCase是个别正则表达式对象的一个属性。 <br />如果使用了"i"标记，则返回true，否则返回false。"i"标记指示在进行匹配的时候忽略大小写。 <br />你不能直接改变该属性的值，但可以通过调用compile方法来改变它 input 指出正则表达式要测试那个字串。$_是这个属性的另一个名字。 <br />RegExp的属性，静态 在Javascript 1.2, NES 3.0以上版本提供  <br />描述：因为input是静态的，不是某个个别的正则表达式对象的属性。你也可以使用 RegExp.input来表示。 <br />如果没有给正则表达式的exec或test方法提供字符串，并且RegExp.input中有值，则使用它的值来调用该方法。 <br />脚本或浏览器能够预置input属性。如果被预置了值且调用exec或 test方法的时候没有提供字符串 <br />则调用exec或test的时候使用input的值。input可以被浏览器以下面的方式设置： <br />当text表单域处理句柄被调用的时候，input被设置为该text输入的字串。 <br />当textarea表单域处理句柄被调用的时候，input被设置为textarea域内输入的字串。注意multili <br />ne亦被设置成true从而能匹配多行文本。 当select表单域处理句柄被调用的时候，input被设置成selected text的值。 <br />当链接对象的处理句柄被调用的时候，input被设置成&lt;A HREF=...&gt;和&lt;/A&gt;之间的字符串。 <br />事件理现句柄处理完毕后，input属性的值被清除。 lastIndex 可读/可写的一个整数属性，指出下一次匹配从哪里开始。 <br />RegExp的属性 在Javascript 1.2, NES 3.0以上版本提供  <br />描述：lastIndex 是个别的正则表达式对象的属性。 这个属性只有当正则表达式的"g"标记被使用以进行全串匹配的时候才被设置。实行以下规则： <br />如果lastIndex大小字符串的长度，regexp.test和regexp.exec失败，且lastIndex被设为0。 <br />如果lastIndex等于字串的长度且正则表达式匹配空字符串，则正则表达式从lastIndex的位置开始匹配。 <br />如果lastIndex等于字符串的长度且正则表达式不匹配空字符串，则正则表达式不匹配input，且lastIndex被置为0。 <br />否则，lastIndex被设置成最近一次匹配的下一点。 例如，按下面的顺序执行脚本： re = /(hi)?/g 匹配空字符串  <br />re("hi") 返回["hi", "hi"]，lastIndex置为2  <br />re("hi") 返回[""]，一个空数组，它的下标为0的元素就是匹配字符串。在这种情况下，返回空 <br />串是因为lastIndex等于2(且仍然是2)，并且"hi"的长度也是2。 lastMatch 最后一次匹配字符串，$&amp;是同样的意思。 <br />RegExp的属性，静态，只读 在Javascript 1.2, NES 3.0以上版本提供  <br />描述：因为lastMatch是静态的，所以它不是个别指定正则表达式的属性。你也可以使用RegExp.lastMatch。 lastParen <br />最后一次加上括号的匹配字符串，如果有的话。$+是同样的意思。 RegExp属性，静态，只读 <br />在Javascript 1.2, NES 3.0以上版本提供  <br />描述：因为lastParen是静态的，它不是某个个别正则式的属性，你可以使用RegExp.lastParen 表达同样的意思。 <br />leftContext 最近一次匹配前面的子串，$`具有相同的意思。 RegExp的属性，静态，只读 <br />在Javascript 1.2, NES 3.0以上版本提供  <br />描述：因为leftContext是静态的，不是某一个正则表达式的属性，所以可以使用RegExp.leftContext来表达想同的意思。 <br />multiline 反映是否匹配多行文本，$*是相同的意思。 RegExp的属性，静态 <br />在Javascript 1.2, NES 3.0以上版本提供  <br />描述：因为multiline是静态的，而不是某个个别正则表达式的属性，所以能够用RegExp.multiline表达相同的意思。 <br />如果允许匹配多行文本，则multiline为true，如果搜索必须在换行时停止，则为false。 <br />脚本或浏览器能够设置multiline属性。当一个textarea的事件处理句柄被调用的时候，multiline <br />被置为true。在事件处理句柄处理完毕后，multiline属性值被清除。也就是说，如果你设置了multili <br />ne为true，则执行任何的事件处理句柄后，multiline被置为false。 prototype  <br />描绘类的原型。你可以根据要求使用prototype来增加类的属性或方法。为了获得prototypes 的资 <br />料，请参阅RegExp的function.prototype.Property属性。 从Javascript 1.1, NES 2.0版本开始提供 <br />ECMA版本ECMA-262 rightContext 最后一次匹配的右边的字符串，$'是同样的效果。 <br />RegExp的属性，静态，只读 从 Javascript 1.2, NES 3.0以上版本开始提供  <br />描述：因为rightContext是静态的，不是某个个别正则表达工的属性，可以使用RegExp.rightContext来达到相同的效果。 <br />source 一个只读属性，包含正则表达式定义的模式，不包侨forward slashes和"g"或"i"标记。 RegExp的属性，只读 <br />从Javascript 1.2, NES 3.0以上版本开始提供  <br />描述：source是个别正则表达式对象的属性，你不能直接改变它的值，但可以通过调用compile 方法来改变它。 test <br />执行指定字符串的正则表达式匹配搜索，返回true或false。 RegExp的方法 <br />从Javascript 1.2, NES 3.0以上版本开始提供 语法：regexp.test([str]) <br />参数：regexp，正则表达式的名称，可以是变量名或正则表达式定义文字串 <br />str，要匹配的字符串，如果省略，将使用RegExp.input的值为作参数 <br />描述：当你需要知道一个字符串能否匹配某个正则表达工，可以使用test方法(与String.search方 <br />法类似)； 为了获得更多的信息(但速度将变慢)，可以使用exec方法(与String.match方法类似)。 例子：下面的例子显示test是否成功的提示： <br />function testinput(re, str){ <br />if (re.test(str)) midstring = " contains "; <br />else midstring = " does not contain "; <br />document.write (str + midstring + re.source); } toSource  <br />返回一个字符串象征对象的源码 RegExp的方法 从Javascript 1.3以上版本开始提供 语法：toSource() <br />参数：没有 描述：toSource方法返回下述的值： 对于内置的RegExp对象，toSource返回下面的字符象征源码不可用： <br />function Boolean(){ [native code] } <br />在RegExp场合中, toSource返回象征源码的字符串，通常这个方法是由Javascript内部自动调用而不是不代码中显式调用。 <br />更多请看Object.toSource toString 返回描绘指定对象的字符串。 RegExp的方法 <br />从Javascript 1.1, NES 2.0开始提供 ECMA版本ECMA-262 语法：toString() 参数：无 <br />描述：RegExp对象不考虑Object对象的toString方法；它不继承Object.toString，对于RegExp 对 <br />象，toString方法返回一个代表该对象的字符串。 例如：下面的例子显示象征RegExp对象的字符串 <br />myExp = new RegExp("a+b+c"); alert(myExp.toString()) <br />displays "/a+b+c/" 更多请看：Object.toString valueOf 返回一个RegExp对象的原始值 <br />RegExp的方法 从Javascript 1.1版本开始提供 ECMA版本：ECMA-262 语法：valueOf() <br />参数：无 描述：RegExp的valueOf方法以字符串形式返回RegExp对象的原始值，这个值与RegExp.toString相等。 <br />该方法通常由Javascript内部自动调用而不是显式调用 例子： myExp = new RegExp("a+b+c"); <br />alert(myExp.valueOf()) displays "/a+b+c/" </p><img src ="http://www.blogjava.net/hua/aggbug/66738.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hua/" target="_blank">花</a> 2006-08-30 23:16 <a href="http://www.blogjava.net/hua/archive/2006/08/30/66738.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA中正则表达式的应用 (一) </title><link>http://www.blogjava.net/hua/archive/2006/08/30/66737.html</link><dc:creator>花</dc:creator><author>花</author><pubDate>Wed, 30 Aug 2006 15:15:00 GMT</pubDate><guid>http://www.blogjava.net/hua/archive/2006/08/30/66737.html</guid><wfw:comment>http://www.blogjava.net/hua/comments/66737.html</wfw:comment><comments>http://www.blogjava.net/hua/archive/2006/08/30/66737.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hua/comments/commentRss/66737.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hua/services/trackbacks/66737.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<font size="1">
						<p>
								<a name="1">
										<span class="atitle">
												<font face="Arial" size="4">正则表达式：</font>
										</span>
								</a>
						</p>
						<p>正则表达式是一种可以用于模式匹配和替换的强有力的工具，一个正则表达式就是由普通的字符（例如字符 a 到 z）以及特殊字符（称为元字符）组成的文字模式，它描述在查找文字主体时待匹配的一个或多个字符串。正则表达式作为一个模板，将某个字符模式与所搜索的字符串进行匹配。</p>
						<p>正则表达式在字符数据处理中起着非常重要的作用，我们可以用正则表达式完成大部分的数据分析处理工作，如:判断一个串是否是数字、是否是有效的Email地址，从海量的文字资料中提取有价值的数据等等，如果不使用正则表达式，那么实现的程序可能会很长，并且容易出错。对这点本人深有体会，面对大量工具书电子档资料的整理工作，如果不懂得应用正则表达式来处理，那么将是很痛苦的一件事情，反之则将可以轻松地完成，获得事半功倍的效果。</p>
						<p>由于本文目的是要介绍如何在JAVA里运用正则表达式，因此对刚接触正则表达式的读者请参考有关资料，在此因篇幅有限不作介绍。</p>
						<br />
						<table cellspacing="0" cellpadding="0" width="100%" border="0">
								<tbody>
										<tr>
												<td>
														<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
														<br />
														<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
												</td>
										</tr>
								</tbody>
						</table>
						<table class="no-print" cellspacing="0" cellpadding="0" align="right">
								<tbody>
										<tr align="right">
												<td>
														<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
														<br />
														<table cellspacing="0" cellpadding="0" border="0">
																<tbody>
																		<tr>
																				<td valign="center">
																						<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																						<br />
																				</td>
																				<td valign="top" align="right">
																						<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-regp/part1/index.html#main">
																								<b>
																										<font color="#996699">回页首</font>
																										<font color="#000080">
																										</font>
																								</b>
																						</a>
																				</td>
																		</tr>
																</tbody>
														</table>
												</td>
										</tr>
								</tbody>
						</table>
						<br />
						<br />
						<p>
								<a name="2">
										<span class="atitle">
												<font face="Arial" size="4">JAVA对正则表达式的支持：</font>
										</span>
								</a>
						</p>
						<p>在JDK1.3或之前的JDK版本中并没有包含正则表达式库可供JAVA程序员使用，之前我们一般都在使用第三方提供的正则表达式库，这些第三方库中有源代码开放的，也有需付费购买的，而现时在JDK1.4的测试版中也已经包含有正则表达式库---java.util.regex。</p>
						<p>故此现在我们有很多面向JAVA的正则表达式库可供选择，以下我将介绍两个较具代表性的 <b>Jakarta-ORO</b>和 <b>java.util.regex</b>，首先当然是本人一直在用的 <b>Jakarta-ORO：</b></p>
						<br />
						<table cellspacing="0" cellpadding="0" width="100%" border="0">
								<tbody>
										<tr>
												<td>
														<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
														<br />
														<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
												</td>
										</tr>
								</tbody>
						</table>
						<table class="no-print" cellspacing="0" cellpadding="0" align="right">
								<tbody>
										<tr align="right">
												<td>
														<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
														<br />
														<table cellspacing="0" cellpadding="0" border="0">
																<tbody>
																		<tr>
																				<td valign="center">
																						<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																						<br />
																				</td>
																				<td valign="top" align="right">
																						<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-regp/part1/index.html#main">
																								<b>
																										<font color="#996699">回页首</font>
																										<font color="#000080">
																										</font>
																								</b>
																						</a>
																				</td>
																		</tr>
																</tbody>
														</table>
												</td>
										</tr>
								</tbody>
						</table>
						<br />
						<br />
						<p>
								<a name="3">
										<span class="atitle">
												<font face="Arial" size="4">Jakarta-ORO正则表达式库</font>
										</span>
								</a>
						</p>
						<p>
								<font face="Arial" size="4">
								</font>
						</p>
						<p>
								<a name="N10067">
										<span class="smalltitle">
												<strong>
														<font face="Arial" size="3">1．简介：</font>
												</strong>
										</span>
								</a>
						</p>
						<p>Jakarta-ORO是最全面以及优化得最好的正则表达式API之一，Jakarta-ORO库以前叫做OROMatcher，是由Daniel F. Savarese编写，后来他将其赠与Jakarta Project，读者可在Apache.org的网站 <a href="http://jakarta.apache.org/oro/"><font color="#5c81a7">下载</font></a>该API包。 </p>
						<p>许多源代码开放的正则表达式库都是支持Perl5兼容的正则表达式语法，Jakarta-ORO正则表达式库也不例外，他与Perl 5正则表达式完全兼容。</p>
						<p>
								<a name="N10077">
										<span class="smalltitle">
												<strong>
														<font face="Arial" size="3">2．对象与其方法：</font>
												</strong>
										</span>
								</a>
						</p>
						<p>★PatternCompiler对象： <br />我们在使用Jakarta-ORO API包时，最先要做的是，创建一个Perl5Compiler类的实例，并把它赋值给PatternCompiler接口对象。Perl5Compiler是PatternCompiler接口的一个实现，允许你把正则表达式编译成用来匹配的Pattern对象。 </p>
						<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
								<tbody>
										<tr>
												<td>
														<pre>
																<code class="section">
																		<font face="Lucida Console">PatternCompiler compiler=new Perl5Compiler();</font>
																</code>
														</pre>
												</td>
										</tr>
								</tbody>
						</table>
						<br />
						<p>★Pattern对象： <br />要把所对应的正则表达式编译成Pattern对象，需要调用compiler对象的compile()方法，并在调用参数中指定正则表达式。举个例子，你可以按照下面这种方式编译正则表达式"s[ahkl]y"： </p>
						<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
								<tbody>
										<tr>
												<td>
														<pre>
																<code class="section">
																		<font face="Lucida Console"> Pattern pattern=null;
        try {
                pattern=compiler.compile("s[ahkl]y ");
        } catch (MalformedPatternException e) {
                e.printStackTrace();
        }</font>
																</code>
														</pre>
												</td>
										</tr>
								</tbody>
						</table>
						<br />
						<p>在默认的情况下，编译器会创建一个对大小写敏感的模式（pattern）。因此，上面代码编译得到的模式只匹配"say"、"shy"、 "sky"和"sly"，但不匹配"Say"和"skY"。要创建一个大小写不敏感的模式，你应该在调用编译器的时候指定一个额外的参数: <br /><code>pattern=compiler.compile("s[ahkl]y",Perl5Compiler.CASE_INSENSITIVE_MASK);</code></p>
						<p>Pattern对象创建好之后，就可以通过PatternMatcher类用该Pattern对象进行模式匹配。</p>
						<p>★PatternMatcher对象:</p>
						<p>PatternMatcher对象依据Pattern对象和字符串展开匹配检查。你要实例化一个Perl5Matcher类并把结果赋值给PatternMatcher接口。Perl5Matcher类是PatternMatcher接口的一个实现，它根据Perl 5正则表达式语法进行模式匹配： <br /><code>PatternMatcher matcher=new Perl5Matcher();</code></p>
						<p>PatternMatcher对象提供了多个方法进行匹配操作，这些方法的第一个参数都是需要根据正则表达式进行匹配的字符串：</p>
						<ol>
								<li>boolean matches(String input, Pattern pattern)：当要求输入的字符串input和正则表达式pattern精确匹配时使用该方法。也就是说当正则表达式完整地描述输入字符串时返回真值。 
</li>
								<li>boolean matchesPrefix(String input, Pattern pattern)：要求正则表达式匹配输入字符串起始部分时使用该方法。也就是说当输入字符串的起始部分与正则表达式匹配时返回真值。 
</li>
								<li>boolean contains(String input, Pattern pattern)：当正则表达式要匹配输入字符串的一部分时使用该方法。当正则表达式为输入字符串的子串时返回真值。 </li>
						</ol>
						<p>但以上三种方法只会查找输入字符串中匹配正则表达式的第一个对象，如果当字符串可能有多个子串匹配给定的正则表达式时，那么你就可以在调用上面三个方法时用PatternMatcherInput对象作为参数替代String对象，这样就可以从字符串中最后一次匹配的位置开始继续进行匹配，这样就方便的多了。</p>
						<p>用PatternMatcherInput对象作为参数替代String时，上述三个方法的语法如下：</p>
						<ol>
								<li>boolean matches(PatternMatcherInput input, Pattern pattern) 
</li>
								<li>boolean matchesPrefix(PatternMatcherInput input, Pattern pattern) 
</li>
								<li>boolean contains(PatternMatcherInput input, Pattern pattern) </li>
						</ol>
						<p>★Util.substitute()方法: <br />查找后需要要进行替换，我们就要用到Util.substitute()方法，其语法如下： </p>
						<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
								<tbody>
										<tr>
												<td>
														<pre>
																<code class="section">
																		<font face="Lucida Console">public static String substitute(PatternMatcher matcher,
       Pattern pattern,Substitution sub,String input,
       int numSubs)
</font>
																</code>
														</pre>
												</td>
										</tr>
								</tbody>
						</table>
						<br />
						<p>前两个参数分别为PatternMatcher和Pattern对象。而第三个参数是个Substiution对象，由它来决定替换操作如何进行。第四个参数是要进行替换操作的目标字符串，最后一个参数用来指定是否替换模式的所有匹配子串（Util.SUBSTITUTE_ALL），或只进行指定次数的替换。</p>
						<p>在这里我相信有必要详细解说一下第三个参数Substiution对象，因为它将决定替换将怎样进行。</p>
						<p>
								<b>Substiution</b>: <br />Substiution是一个接口类，它为你提供了在使用Util.substitute()方法时控制替换方式的手段，它有两个标准的实现类：StringSubstitution与Perl5Substitution。当然，同时你也可以生成自己的实现类来定制你所需要的特殊替换动作。 </p>
						<p>
								<b>StringSubstitution：</b>
								<br />StringSubstitution 实现的是简单的纯文字替换手段，它有两个构造方法： </p>
						<p>StringSubstitution()-&gt;缺省的构造方法，初始化一个包含零长度字符串的替换对象。</p>
						<p>StringSubstitution(java.lang.String substitution)-&gt;初始化一个给定字符串的替换对象。</p>
						<p>
								<b>Perl5Substitution：</b>
								<br />Perl5Substitution 是StringSubstitution的子类，它在实现纯文字替换手段的同时也允许进行针对MATH类里各匹配组的PERL5变量的替换，所以他的替换手段比其直接父类StringSubstitution更为多元化。 </p>
						<p>它有三个构造器：</p>
						<p>
								<b>Perl5Substitution()</b>
						</p>
						<p>
								<b>Perl5Substitution</b>(java.lang.String substitution) </p>
						<p>
								<b>Perl5Substitution</b>(java.lang.String substitution, int numInterpolations) </p>
						<p>前两种构造方法与StringSubstitution一样，而第三种构造方法下面将会介绍到。</p>
						<p>在 <b>Perl5Substitution</b>的替换字符串中可以包含用来替代在正则表达式里由小扩号围起来的匹配组的变量，这些变量是由$1, $2,$3等形式来标识。我们可以用一个例子来解释怎样使用替换变量来进行替换： </p>
						<p>假设我们有正则表达式模式为b\d+:（也就是b[0-9]+:），而我们想把所有匹配的字符串中的"b"都改为"a",而"："则改为"-"，而其余部分则不作修改，如我们输入字符串为"EXAMPLE b123:"，经过替换后就应该变成"EXAMPLE a123-"。要做到这点，我们就首先要把不做替换的部分用分组符号小括号包起来，这样正则表达式就变为"b(\d+):"，而构造Perl5Substitution对象时其替换字符串就应该是"a$1-"，也就是构造式为Perl5Substitution（"a$1-"），表示在使用Util.substitute()方法时只要在目标字符串里找到和正则表达式" b(\d+): "相匹配的子串都用替换字符串来替换，而变量$1表示如果和正则表达式里第一个组相匹配的内容则照般原文插到$1所在的为置，如在"EXAMPLE b123："中和正则表达式相匹配的部分是"b123："，而其中和第一分组"(\d+)"相匹配的部分则是"123"，所以最后替换结果为"EXAMPLE a123-"。</p>
						<p>有一点需要清楚的是，如果你把构造器Perl5Substitution(java.lang.String substitution,int numInterpolations)</p>
						<p>中的numInterpolations参数设为INTERPOLATE_ALL，那么当每次找到一个匹配字串时，替换变量（$1，$2等）所指向的内容都根据目前匹配字串来更新，但是如果numInterpolations参数设为一个正整数N时，那么在替换时就只会在前N次匹配发生时替换变量会跟随匹配对象来调整所代表的内容，但N次之后就以一致以第N次替换变量所代表内容来做为以后替换结果。</p>
						<p>举个例子会更好理解：</p>
						<p>假如沿用以上例子中的正则表达式模式以及替换内容来进行替换工作，设目标字符串为"Tank b123: 85 Tank b256: 32 Tank b78: 22"，并且设numInterpolations参数为INTERPOLATE_ALL，而Util.substitute()方法中的numSub变量设为SUBSTITUTE_ALL（请参考上文Util.substitute()方法内容），那么你获得的替换结果将会是： <code>Tank a123- 85 Tank a256- 32 Tank a78- 22</code></p>
						<p>但是如果你把numInterpolations设为2，并且numSubs依然设为SUBSTITUTE_ALL，那么这时你获得的结果则会是： <code>Tank a123- 85 Tank a256- 32 Tank a256- 22</code></p>
						<p>你要注意到最后一个替换所用变量$1所代表的内容与第二个$1一样为"256"，而不是预期的"78"，因为在替换进行中，替换变量$1只根据匹配内容进行了两次更新，最后一次就使第二次匹配时所更新的结果，那么我们可以由此知道，如果numInterpolations设为1，那么结果将是： <code>Tank a123- 85 Tank a123- 32 Tank a123- 22</code></p>
						<p>
								<a name="N10134">
										<span class="smalltitle">
												<strong>
														<font face="Arial" size="3">3．应用示例：</font>
												</strong>
										</span>
								</a>
						</p>
						<p>刚好前段时间公司准备出一个《伊索预言》的英语学习互动教材，其中有电子档资料的整理工作，我们就以此为例来看一下Jakarta-ORO与JDBC2.0 API结合起来对数据库内的资料进行简单提取与整理的实现。假设由录入部的同事送过来的存放在MS SQLSERVER 7数据库里的电子档的表结构如下（注：或许在不同的DBMS中有相应的正则表达式的应用，但这不在本文讨论范围内）：</p>
						<p>表名：AESOP, 表中每条记录包含有三列: <br />ID（int）：单词索引号 <br />WORD（varchar）：单词 <br />CONTENT(varchar)：存放单词的相关解释与例句等内容 </p>
						<p>其中CONTENT列中内容的格式如下： <br />[音标] [词性] （解释）{(例句一/例句解释/例句中该词的词性: 单词在句中的意思) (例句二/例句解释/例句中该词的词性: 单词在句中的意思)} </p>
						<p>如对应单词Kevin,CONTENT中的内容如下： <br />['kevin] [名词]（人名凯文）{(Kevin loves comic./凯文爱漫画/名词: 凯文)( Kevin is living in ZhuHai now./凯文现住在珠海/名词: 凯文)} </p>
						<p>我们的例子主要针对CONTENT列中内容进行字符串处理。</p>
						<p>★查找单个匹配： <br />首先，让我们尝试把CONTNET列中的[音标]字段的内容列示出来，由于所有单词的记录中都有这一项并且都在字串开始位置，所以这个查找工作比较简单： </p>
						<ol>
								<li>
										<b>确定相应的正则表达式：\[[^]]+\]</b>
										<p>这个是很简单的正则表达式，其意思是要求相匹配的字符串必须为以一对中括号包含的所有内容，如['kevin] 、[名词]等，但内容中不包括"]"符号，也就是要避免出现"[][]"会作为一个匹配对象的情况出现（有关正则表达式的基础知识请参照有关资料，这里不再详述）。</p>
										<p>注意，在Java中，你必须对每一个向前的斜杠（"\"）进行转义处理。所以我们要在上面的正则表达式里每个"\"前面加上一个"\"以免出现编译错误，也就是在JAVA中初始化正则表达式的字符串的语句应该为：</p>
										<p>String restring=" \\[[^]]+\\]";</p>
										<p>并且在表达式里每个符号中间不能有空格，否则就会同样出现编译错误。</p>
								</li>
								<li>
										<b>实例化PatternCompiler对象，创建Pattern对象</b>
										<p>PatternCompiler compiler=new Perl5Compiler();</p>
										<p>Pattern pattern=compiler.compile(restring);</p>
								</li>
								<li>
										<b>创建PatternMatcher对象，调用PatternMatcher接口的contain()方法检查匹配情况：</b>
										<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
												<tbody>
														<tr>
																<td>
																		<pre>
																				<code class="section">
																						<font face="Lucida Console">  PatternMatcher matcher=new Perl5Matcher();
        if (matcher.contains(content,pattern)) {
                 //处理代码片段
        }
</font>
																				</code>
																		</pre>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<p>这里matcher.contains(content,pattern)中的参数 content是从数据库里取来的字符串变量。该方法只会查到第一个匹配的对象字符串，但是由于音标项均在CONETNET内容字符串中的起始位置，所以用这个方法就已经可以保证把每条记录里的音标项找出来了,但更为直接与合理的办法是使用boolean matchesPrefix(PatternMatcherInput input, Pattern pattern)方法，该方法验证目标字符串是否以正则表达式所匹配的字串为起始。</p>
										<p>具体实现的完整的程序代码如下：</p>
										<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
												<tbody>
														<tr>
																<td>
																		<pre>
																				<code class="section">
																						<font face="Lucida Console">package RegularExpressions;
//import……
import org.apache.oro.text.regex.*;
//使用Jakarta-ORO正则表达式库前需要把它加到CLASSPATH里面，如果用IDE是//JBUILDER，那么也可以在JBUILDER里直接自建新库。

public class yisuo{
  public static void main(String[] args){
  try{     
//使用JDBC DRIVER进行DBMS连接，这里我使用的是一个第三方JDBC 
//DRIVER，Microsoft本身也有一个面向SQLSERVER7/2000的免费JDBC //DRIVER，但其性能真的是奇差，不用也罢。
        Class.forName("com.jnetdirect.jsql.JSQLDriver");
          Connection con=DriverManager.getConnection
          ("jdbc:JSQLConnect://kevin:1433","kevin chen","re");
          Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
          ResultSet.CONCUR_UPDATABLE);
//为使用Jakarta-ORO库而创建相应的对象
String rsstring=" \\[[^]]+\\]"; 
          PatternCompiler orocom=new Perl5Compiler();
          Pattern pattern=orocom.compile(rsstring);
          PatternMatcher matcher=new Perl5Matcher();
          ResultSet uprs = stmt.executeQuery("SELECT * FROM aesop");
          while (uprs.next()) {
Stirng  word=uprs.getString("word");
          Stirng  content=uprs.getString("content");
            if(matcher.contains(content,pattern)){
          //或if(matcher.matchesPrefix(content,pattern)){
                MatchResult result=matcher.getMatch();
                Stirng pure=result.toString();
                System.out.println(word+"的音标为："+pure);
            }
          }
       }
  catch(Exception e) {
             System.out.println(e);
       }
  }
}</font>
																				</code>
																		</pre>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<p>输出结果为：kevin的音标为['kevin]</p>
								</li>
						</ol>
						<p>在这个处理中我是用toString()方法来取得结果，但是如果正则表达式里是用了分组符号（圆括号），那么就可以用group(int gid)的方法来取得相应各组匹配的结果，如正则表达式改为" (\[[^]]+\])"，那么就可以用以下方法来取得结果：pure=result.group(0); </p>
						<p>用程序验证，输出结果同样为：kevin的音标为['kevin]</p>
						<p>而如果正则表达式为（\[[^]]+\]）（\[[^]]+\]），则会查找到两个连续的方括号所包含的内容，也就找到[音标] [词性]两项，但是两项的结果分别在两个组里面，分别由下面语句获得结果：</p>
						<p>result.group(0)-&gt;返回[音标] [词性]两项内容，也就是与整个正则表达式相匹配的结果字符串，在这里也就为['kevin] [名词]</p>
						<p>result.group(1) -&gt;返回[音标]项内容，结果应是['kevin]</p>
						<p>result.group(2) -&gt;返回[词性]项内容，结果应是[名词]</p>
						<p>继续用程序验证，发现输出并不正确，主要是当内容有中文时就不能成功匹配，考虑到可能是Jakarta-ORO正则表达式库版本不支持中文的问题，回看一下原来我一直用的还是2.0.1的老版本，马上到Jakarta.org上下载最新的2.0.4版本装上再用程序验证，得出的结果就和预期一样正确。</p>
						<p>★查找多个匹配： <br />经过第一步的尝试使用Jakarta-ORO后，我们已经知道了如何正确使用该API包来查找目标字符串里一个匹配的子串，下面我们接着来看一看当目标字符串里包含不止一个匹配的子串时我们如何把它们一个接一个找出来进行相应的处理。 </p>
						<p>首先我们先试个简单的应用，假设我们想把CONTNET字段内容里所有用方括号包起来的字串都找出来，很清楚地，CONTNET字段的内容里面就只有两项匹配的内容：[音标]和 [词性]，刚才我们其实已经把它们分别找出来了，但是我们所用的方法是分组方法，把"[音标] [词性]"作为一整个正则表达式匹配的内容先找到，再根据分组把[音标]和 [词性]分别挑出来。但是现在我们需要做的是把[音标]和[词性]分别做为与同一个正则表达式匹配的内容，先找到一个接着再找下一个，也就是刚才我们的表达式为（\[[^]]+\]）（\[[^]]+\]）,而现在应为" \[[^]]+\] "。</p>
						<p>我们已经知道在匹配操作的三个方法里只要用PatternMatcherInput对象作为参数替代String对象就可以从字符串中最后一次匹配的位置开始继续进行匹配，实现的程序片段如下：</p>
						<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
								<tbody>
										<tr>
												<td>
														<pre>
																<code class="section">
																		<font face="Lucida Console">PatternMatcherInput input=new PatternMatcherInput(content);
            while (matcher.contains(input,pattern)) {
                result=matcher.getMatch();
                System.out.println(result.group(0)) 
            }
</font>
																</code>
														</pre>
												</td>
										</tr>
								</tbody>
						</table>
						<br />输出结果为:['kevin] <br />[名词] <br /><p>接着我们来做复杂一点的处理，就是我们要先把下面内容： <br />['kevin] [名词]（人名凯文）{(Kevin loves comic./凯文爱漫画/名词: 凯文)( Kevin is living in ZhuHai now. /凯文现住在珠海/名词: 凯文)}中的整个例句部分（也就是由大括号所包含的部分）找出来，再分别把例句一和例句二找出，而各例句中的各项内容（英文句、中文句、词性、解释）也要分项列出。 </p><p>第一步当然是要定出相应的正则表达式，需要有两个，一是和整个例句部分（也就是由大括号包起来的部分）匹配的正则表达式："\{.+\}",</p><p>另一个则要和每个例句部分匹配（也就是小括号中的内容），：\(([^)]+\)</p><br /><p>而且由于要把例句的各项分离出来，所以要再把里面的各部分用分组的方法匹配出来：" ([^(]+)/(.+)/(.+):([^)]+) "。</p><p>为了简便起见，我们不再和从数据库里读出，而是构造一个包含同样内容的字符串变量，程序片段如下：</p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">try{
         String content="['kevin] [名词]（人名凯文）{(Kevin loves comic./凯文爱漫画/名词:凯文) (Kevin is living in ZhuHai now./凯文现住在珠海/名词: 凯文)}";
         String ps1="\\{.+\\}";
         String ps2="\\([^)]+\\)";
         String ps3="([^(]+)/(.+)/(.+):([^)]+)";
         String sentence;
         PatternCompiler orocom=new Perl5Compiler();
         Pattern pattern1=orocom.compile(ps1);
         Pattern pattern2=orocom.compile(ps2);
         Pattern pattern3=orocom.compile(ps3);
         PatternMatcher matcher=new Perl5Matcher();
//先找出整个例句部分
            if (matcher.contains(content,pattern1)) {
            MatchResult result=matcher.getMatch();
            String example=result.toString();
            PatternMatcherInput input=new PatternMatcherInput(example);
        //分别找出例句一和例句二
            while (matcher.contains(input,pattern2)){
                result=matcher.getMatch();
                sentence=result.toString();
        //把每个例句里的各项用分组的办法分隔出来
                if (matcher.contains(sentence,pattern3)){
                  result=matcher.getMatch();
                  System.out.println("英文句: "+result.group(1));
                  System.out.println("句子中文翻译: "+result.group(2));
                  System.out.println("词性: "+result.group(3));
                  System.out.println("意思: "+result.group(4));
                }
            }
        }
       }
  catch(Exception e) {
             System.out.println(e);
       }
</font></code></pre></td></tr></tbody></table><br /><p>输出结果为： <br />英文句: Kevin loves comic. <br />句子中文翻译: 凯文爱漫画 <br />词性: 名词 <br />意思: 凯文 <br />英文句: Kevin is living in ZhuHai now. <br />句子中文翻译: 凯文现住在珠海 <br />词性: 名词 <br />意思: 凯文 </p><p>★查找替换： <br />以上的两个应用都是单纯在查找字符串匹配方面的，我们再来看一下查找后如何对目标字符串进行替换。 </p><p>例如我现在想把第二个例句进行改动，换为：Kevin has seen《LEON》seveal times,because it is a good film./ 凯文已经看过《这个杀手不太冷》几次了，因为它是一部好电影。/名词:凯文。</p><p>也就是把 <br />['kevin] [名词]（人名凯文）{(Kevin loves comic./凯文爱漫画/名词: 凯文)( Kevin is living in ZhuHai now. /凯文现住在珠海/名词: 凯文)} </p><p>改为： <br />['kevin] [名词]（人名凯文）{(Kevin loves comic./凯文爱漫画/名词: 凯文)( Kevin has seen《LEON》seveal times,because it is a good film./ 凯文已经看过《这个杀手不太冷》几次了，因为它是一部好电影。/名词:凯文。)} </p><p>之前，我们已经了解Util.substitute()方法与Substiution接口，以及Substiution的两个实现类StringSubstitution和Perl5Substitution，我们就来看看怎么用Util.substitute()方法配合Perl5Substitution来完成我们上面提出的替换要求，确定正则表达式：</p><p>我们要先找到其中的整个例句部分，也就是由大括号包起来的字串，并且把两个例句分别分组，所以正则表达式为："\{(\([^)]+\))(\([^)]+\))\}"，如果用替换变量来代替分组，那么上面的表达式可以看为"\{$1$2\}",这样就可以更容易看出替换变量与分组间的关系。</p><p>根据上面的正则表达式Perl5Substitution类可以这样构造： Perl5Substitution("{$1( Kevin has seen《LEON》seveal times,because it is a good film./ 凯文已经看过《这个杀手不太冷》几次了，因为它是一部好电影。/名词:凯文。)}")</p><p>再根据这个Perl5Substitution对象来使用Util.substitute()方法便可以完成替换了，实现的代码片段如下：</p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">try{
   String content="['kevin] [名词]（人名凯文）{(Kevin loves comic.
   /凯文爱漫画/名词: 凯文)(Kevin lives in ZhuHai now./凯文现住在珠海/名词: 凯文)}";
   String ps1="\\{(\\([^)]+\\))(\\([^)]+\\))\\}";
   String sentence;
   String pure;
   PatternCompiler orocom=new Perl5Compiler();
   Pattern pattern1=orocom.compile(ps1);
   PatternMatcher matcher=new Perl5Matcher();
       String result=Util.substitute(matcher,
        pattern1,new Perl5Substitution(
       "{$1( Kevin has seen《LEON》seveal times,because it is a good film./ 
       凯文已经看过《这个杀手不太冷》几次了，因为它是一部好电影。/名词:凯文。)}",1),
        content,Util.SUBSTITUTE_ALL);
        System.out.println(result);
   }
  catch(Exception e) {
             System.out.println(e);
       }
</font></code></pre></td></tr></tbody></table><br /><p>输出结果是正确的，为： <br />['kevin] [名词]（人名凯文）{(Kevin loves comic./凯文爱漫画/名词: 凯文)( Kevin has seen《LEON》seveal times,because it is a good film./ 凯文已经看过《这个杀手不太冷》几次了，因为它是一部好电影。/名词:凯文。)} </p><p>至于有关使用numInterpolations参数的构造器用法，读者只要根据上面的介绍自己动手试一下就会清楚了，在此就不再例述。</p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-regp/part1/index.html#main"><b><font color="#996699">回页首</font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="4"><span class="atitle"><font face="Arial" size="4">总结：</font></span></a></p><p>本文首先介绍了Jakarta-ORO正则表达式库的对象与方法，并且接着举例让读者对实际应用有进一步的了解，虽然例子都比较简单，但希望读者们在看了该文后对Jakarta-ORO正则表达式库有一定的认知，在实际工作中有所帮助与启发。</p><p>其实在Jakarta org里除了Jakarta-ORO外还有一个百分百的纯JAVA正则表达式库，就是由Jonathan Locke赠与Jakarta ORG的Regexp，在该包里面包含了完整的文档以及一个用于调试的Applet例子，对其有兴趣的读者可以到此 <a href="http://jakarta.apache.org/regexp/index.html"><font color="#5c81a7">下载</font></a>。 </p></font>
		</div>
<img src ="http://www.blogjava.net/hua/aggbug/66737.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hua/" target="_blank">花</a> 2006-08-30 23:15 <a href="http://www.blogjava.net/hua/archive/2006/08/30/66737.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>存取程序状态的几种方法--Java I/O应用杂谈</title><link>http://www.blogjava.net/hua/archive/2006/08/30/66590.html</link><dc:creator>花</dc:creator><author>花</author><pubDate>Wed, 30 Aug 2006 02:31:00 GMT</pubDate><guid>http://www.blogjava.net/hua/archive/2006/08/30/66590.html</guid><wfw:comment>http://www.blogjava.net/hua/comments/66590.html</wfw:comment><comments>http://www.blogjava.net/hua/archive/2006/08/30/66590.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hua/comments/commentRss/66590.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hua/services/trackbacks/66590.html</trackback:ping><description><![CDATA[
		<h3>
				<b>
						<u>文件I/O：文件流→序列化</u>
				</b>
		</h3>
		<p>
				<br />★<b>文件流</b><br />    文件操作是最简单最直接也是最容易想到的一种方式，我们说的文件操作不仅仅是通过FileInputStream/FileOutputStream这么“裸”的方式直接把数据写入到本地文件（像我以前写的一个扫雷的小游戏JavaMine就是这样保存一局的状态的），这样就比较“底层”了。 <br /><br />主要类与方法和描述 <br /></p>
		<div>
				<ol>
						<li>
								<font color="#ff0000">FileInputStream</font>.read() <i><font color="#339900">//从本地文件读取二进制格式的数据 </font></i></li>
						<li>
								<font color="#ff0000">FileReader</font>.read() <i><font color="#339900">//从本地文件读取字符（文本）数据 </font></i></li>
						<li>
								<font color="#ff0000">FileOutputStream</font>.write() <i><font color="#339900">//保存二进制数据到本地文件 </font></i></li>
						<li>
								<font color="#ff0000">FileWriter</font>.write() <i><font color="#339900">//保存字符数据到本地文件</font></i></li>
				</ol>
		</div>
		<p>
				<br />★<b>XML</b><br />    和上面的单纯的I/O方式相比，XML就显得“高档”得多，以至于成为一种数据交换的标准。以<b>DOM方式</b>为例，它关心的是首先<b>在内存中构造文档树</b>，数据保存在某个结点上（可以是叶子结点，也可以是标签结点的属性），构造好了以后一次性的写入到外部文件，但我们只需要知道文件的位置，并不知道I/O是怎么操作的，XML操作方式可能多数人也实践过，所以这里也只列出相关的方法，供初学者预先了解一下。主要的包是<a href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/parsers/package-summary.html"><u><font color="#0000ff">javax.xml.parsers</font></u></a>，<a href="http://java.sun.com/j2se/1.4.2/docs/api/org/w3c/dom/package-summary.html"><u><font color="#0000ff">org.w3c.dom</font></u></a>，<a href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/transform/package-summary.html"><u><font color="#0000ff">javax.xml.transform</font></u></a>。 <br /><br />主要类与方法和描述 <br /></p>
		<div>
				<ol>
						<li>
								<font color="#ff0000">DocumentBuilderFactory</font>.newDocumentBuilder().parse() <i><font color="#339900">//解析一个外部的XML文件，得到一个Document对象的DOM树 </font></i></li>
						<li>
								<font color="#ff0000">DocumentBuilderFactory</font>.newInstance().newDocumentBuilder().newDocument() <i><font color="#339900">//初始化一棵DOM树 </font></i></li>
						<li>
								<font color="#ff0000">Document</font>.getDocumentElement().appendChild() <i><font color="#339900">//为一个标签结点添加一个子结点 </font></i></li>
						<li>
								<font color="#ff0000">Document</font>.createTextNode() <i><font color="#339900">//生成一个字符串结点 </font></i></li>
						<li>
								<font color="#ff0000">Node</font>.getChildNodes() <i><font color="#339900">//取得某个结点的所有下一层子结点 </font></i></li>
						<li>
								<font color="#ff0000">Node</font>.removeChild()  <i><font color="#339900">//删除某个结点的子结点 </font></i></li>
						<li>
								<font color="#ff0000">Document</font>.getElementsByTagName() 查找所有指定名称的标签结点  
</li>
						<li>
								<font color="#ff0000">Document</font>.getElementById() <i><font color="#339900">//查找指定名称的一个标签结点，如果有多个符合，则返回某一个，通常是第一个 </font></i></li>
						<li>
								<font color="#ff0000">Element</font>.getAttribute() <i><font color="#339900">//取得一个标签的某个属性的的值 </font></i></li>
						<li>
								<font color="#ff0000">Element</font>.setAttribute() <i><font color="#339900">//设置一个标签的某个属性的的值 </font></i></li>
						<li>
								<font color="#ff0000">Element</font>.removeAttribute() <i><font color="#339900">//删除一个标签的某个属性 </font></i></li>
						<li>
								<font color="#ff0000">TransformerFactory</font>.newInstance().newTransformer().transform() <i><font color="#339900">//将一棵DOM树写入到外部XML文件</font></i></li>
				</ol>
		</div>
		<p>
				<br />★<b>序列化</b><br />    使用基本的文件读写方式存取数据，如果我们仅仅保存<b>相同类型</b>的数据，则可以用同一种格式保存，譬如在我的JavaMine中保存一个盘局时，需要保存每一个方格的坐标、是否有地雷，是否被翻开等，这些信息组合成一个“复合类型”；相反，如果有多种<b>不同类型</b>的数据，那我们要么把它分解成若干部分，以相同类型（譬如String）保存，要么我们需要在程序中添加解析不同类型数据格式的逻辑，这就很不方便。于是我们期望用一种比较“高”的层次上处理数据，程序员应该<b>花尽可能少的时间和代码对数据进行解析</b>，事实上，序列化操作为我们提供了这样一条途径。<br />    序列化（Serialization）大家可能都有所接触，它可以把对象以某种特定的编码格式写入或从外部字节流（即<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectInputStream.html"><u><font color="#0000ff">ObjectInputStream</font></u></a>/<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectOutputStream.html"><u><font color="#0000ff">ObjectOutputStream</font></u></a>）中读取。序列化一个对象非常之简单，仅仅实现一下Serializable接口即可，甚至都不用为它专门添加任何方法： <br /></p>
		<div>
				<ol>
						<li>
								<b>
										<font color="#0000ff">public</font>
								</b> <b><font color="#0000ff">class</font></b> MySerial <b><font color="#0000ff">implements</font></b> java.io.<b><a href="http://spaces.msn.com/source/jdk142/java/io/Serializable.java.html"><font color="#0000ff"><u>Serializable</u></font></a></b></li>
						<li>{ 
</li>
						<li>  <i><font color="#339900">//...</font></i></li>
						<li>} </li>
				</ol>
		</div>
		<p>
				<br />但有一个条件：即<b>你要序列化的类当中，它的每个属性都必须是是“可序列化”的</b>。这句话说起来有点拗口，其实所有<b>基本类型</b>（就是int，char，boolean之类的）<b>都是“可序列化”的</b>，而你可以看看JDK文档，会发现很多类其实已经实现了Serializable（即已经是“可序列化”的了），于是这些类的对象以及基本数据类型都可以直接作为你需要序列化的那个类的内部属性。如果碰到了不是“可序列化”的属性怎么办？对不起，那这个属性的类还需要事先实现Serializable接口，如此递归，<b>直到所有属性都是“可序列化”的</b>。 <br /><br />主要类与方法和描述 <br /></p>
		<div>
				<ol>
						<li>
								<font color="#ff0000">ObjectOutputStream</font>.writeObject() <i><font color="#339900">//将一个对象序列化到外部字节流 </font></i></li>
						<li>
								<font color="#ff0000">ObjectInputStream</font>.readObject() <i><font color="#339900">//从外部字节流读取并重新构造对象</font></i></li>
				</ol>
		</div>
		<p>
				<br />    从实际应用上看来，“Serializable”这个接口并没有定义任何方法，仿佛它只是一个标记（或者说像是Java的关键字）而已，一旦虚拟机看到这个“<b>标记</b>”，就会尝试调用自身<b>预定义的序列化机制</b>，除非你在实现Serializable接口的同时还定义了私有的readObject()或writeObject()方法。这一点很奇怪。不过你要是不愿意让系统使用缺省的方式进行序列化，那就必须定义上面提到的两个方法： <br /></p>
		<div>
				<ol>
						<li>
								<b>
										<font color="#0000ff">public</font>
								</b> <b><font color="#0000ff">class</font></b> MySerial <b><font color="#0000ff">implements</font></b> java.io.<b><a href="http://spaces.msn.com/source/jdk142/java/io/Serializable.java.html"><font color="#0000ff"><u>Serializable</u></font></a></b></li>
						<li>{ 
</li>
						<li>  <b><font color="#0000ff">private</font></b> <b><font color="#0000ff">void</font></b> writeObject(java.io.<b><a href="http://spaces.msn.com/source/jdk142/java/io/ObjectOutputStream.java.html"><font color="#0000ff"><u>ObjectOutputStream</u></font></a></b> out) <b><font color="#0000ff">throws</font></b> <font color="#ff0000">IOException</font></li>
						<li>  { 
</li>
						<li>    <i><font color="#339900">//...</font></i></li>
						<li>  } 
</li>
						<li>  <b><font color="#0000ff">private</font></b> <b><font color="#0000ff">void</font></b> readObject(java.io.<b><a href="http://spaces.msn.com/source/jdk142/java/io/ObjectInputStream.java.html"><font color="#0000ff"><u>ObjectInputStream</u></font></a></b> in) <b><font color="#0000ff">throws</font></b> <font color="#ff0000">IOException</font>, <b><a href="http://spaces.msn.com/source/jdk142/java/lang/ClassNotFoundException.java.html"><font color="#0000ff"><u>ClassNotFoundException</u></font></a></b></li>
						<li>  { 
</li>
						<li>    <i><font color="#339900">//...</font></i></li>
						<li>  } 
</li>
						<li>  <i><font color="#339900">//...</font></i></li>
						<li>}  </li>
				</ol>
		</div>
		<p>
				<br />    譬如你可以在上面的writeObject()里调用默认的序列化方法ObjectOutputStream.<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectOutputStream.html#defaultWriteObject()"><u><font color="#0000ff">defaultWriteObject</font></u></a>();譬如你不愿意将某些敏感的属性和信息序列化，你也可以调用ObjectOutputStream.writeObject()方法明确指定需要序列化那些属性。关于用户可定制的序列化方法，我们将在后面提到。 <br /><br />★<b>Bean</b><br />    上面的序列化只是一种基本应用，你把一个对象序列化到外部文件以后，用notepad打开那个文件，只能从为数不多的一些可读字符中猜到这是有关这个类的信息文件，这需要你熟悉序列化文件的字节编码方式，那将是比较痛苦的（在<a href="http://www.china-pub.com/computers/common/info.asp?id=14354"><u><font color="#0000ff">《Core Java 2》第一卷</font></u></a>里提到了相关编码方式，有兴趣的话可以查看参考资料），某些情况下我们可能需要被序列化的文件具有更好的可读性。另一方面，作为Java组件的核心概念“<a href="http://java.sun.com/j2se/1.4.2/docs/guide/beans/index.html"><u><font color="#0000ff">JavaBeans</font></u></a>”，从JDK 1.4开始，其规范里也要求支持文本方式的“<b>长期的持久化</b>”（long-term persistence）。<br />    打开<a href="http://java.sun.com/j2se/1.4.2/docs/api/index.html"><u><font color="#0000ff">JDK文档</font></u></a>，<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/beans/package-summary.html"><u><font color="#0000ff">java.beans包</font></u></a>里的有一个名为“<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/beans/Encoder.html"><u><font color="#0000ff">Encoder</font></u></a>”的类，这就是一个可以序列化bean的实用类。和它相关的两个主要类有<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/beans/XMLEncoder.html"><u><font color="#0000ff">XMLEcoder</font></u></a>和<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/beans/XMLDecoder.html"><u><font color="#0000ff">XMLDecoder</font></u></a>，显然，这是以XML文件的格式保存和读取bean的工具。他们的用法也很简单，和上面ObjectOutputStream/ObjectInputStream比较类似。 <br /><br />主要类与方法和描述 <br /></p>
		<div>
				<ol>
						<li>
								<font color="#ff0000">XMLEncoder</font>.writeObject() <i><font color="#339900">//将一个对象序列化到外部字节流 </font></i></li>
						<li>
								<font color="#ff0000">XMLDecoder</font>.readObject() <i><font color="#339900">//从外部字节流读取并重新构造对象 </font></i></li>
				</ol>
		</div>
		<p>
				<br />    如果一个bean是如下格式：<br /></p>
		<div>
				<ol>
						<li>
								<b>
										<font color="#0000ff">public</font>
								</b> <b><font color="#0000ff">class</font></b> MyBean 
</li>
						<li>{ 
</li>
						<li>  <b><font color="#0000ff">int</font></b> i; 
</li>
						<li>  <b><font color="#0000ff">char</font></b>[] c; 
</li>
						<li>  <b><a href="http://spaces.msn.com/source/jdk142/java/lang/String.java.html"><font color="#0000ff"><u>String</u></font></a></b> s; 
</li>
						<li>  <i><font color="#339900">//...(get和set操作省略)...</font></i></li>
						<li>}  </li>
				</ol>
		</div>
		<p>
				<br />那么通过XMLEcoder序列化出来的XML文件具有这样的形式： <br /><br />&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />&lt;java version="1.4.0" class="java.beans.XMLDecoder"&gt;<br />  &lt;object class="MyBean"&gt;<br />    &lt;void property="i"&gt;<br />      &lt;int&gt;1&lt;/int&gt;<br />    &lt;/void&gt;<br />    &lt;void property="c"&gt;<br />      &lt;array class="char" length="3"&gt;<br />        &lt;void index="0"&gt;<br />          &lt;int&gt;a&lt;/int&gt;<br />        &lt;/void&gt;<br />        &lt;void index="1"&gt;<br />          &lt;int&gt;b&lt;/int&gt;<br />        &lt;/void&gt;<br />        &lt;void index="2"&gt;<br />          &lt;int&gt;c&lt;/int&gt;<br />        &lt;/void&gt;<br />      &lt;/array&gt;<br />    &lt;/void&gt;<br />    &lt;void property="s"&gt;<br />      &lt;string&gt;fox jump!&lt;/string&gt; <br />    &lt;/void&gt;<br />  &lt;/object&gt;<br />&lt;/java&gt; <br /><br />    像<a href="http://java.sun.com/j2se/1.4.2/docs/guide/awt/index.html"><u><font color="#0000ff">AWT</font></u></a>和<a href="http://java.sun.com/j2se/1.4.2/docs/guide/swing/index.html"><u><font color="#0000ff">Swing</font></u></a>中很多可视化组件都是bean，当然也是可以用这种方式序列化的，下面就是从JDK文档中摘录的一个<a href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JFrame.html"><u><font color="#0000ff">JFrame</font></u></a>序列化以后的XML文件： <br /><br />&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />&lt;java version="1.0" class="java.beans.XMLDecoder"&gt;<br />  &lt;object class="javax.swing.JFrame"&gt;<br />    &lt;void property="name"&gt;<br />      &lt;string&gt;frame1&lt;/string&gt;<br />    &lt;/void&gt;<br />    &lt;void property="bounds"&gt;<br />      &lt;object class="java.awt.Rectangle"&gt;<br />        &lt;int&gt;0&lt;/int&gt;<br />        &lt;int&gt;0&lt;/int&gt;<br />        &lt;int&gt;200&lt;/int&gt;<br />        &lt;int&gt;200&lt;/int&gt;<br />      &lt;/object&gt;<br />    &lt;/void&gt;<br />    &lt;void property="contentPane"&gt;<br />      &lt;void method="add"&gt;<br />        &lt;object class="javax.swing.JButton"&gt;<br />          &lt;void property="label"&gt;<br />            &lt;string&gt;Hello&lt;/string&gt;<br />          &lt;/void&gt;<br />        &lt;/object&gt;<br />      &lt;/void&gt;<br />    &lt;/void&gt;<br />    &lt;void property="visible"&gt;<br />      &lt;boolean&gt;true&lt;/boolean&gt;<br />    &lt;/void&gt;<br />  &lt;/object&gt;<br />&lt;/java&gt; <br /><br />    因此但你想要保存的数据是一些不是太复杂的类型的话，把它做成bean再序列化也不失为一种方便的选择。 <br /><br />★<b>Properties</b><br />    在以前我总结的一篇关于集合框架的小文章里提到过，<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html"><u><font color="#800080">Properties</font></u></a>是历史集合类的一个典型的例子，这里主要不是介绍它的集合特性。大家可能都经常接触一些<b>配置文件</b>，如Windows的ini文件，Apache的conf文件，还有Java里的properties文件等，这些文件当中的数据以<b>“关键字-值”对</b>的方式保存。“<b>环境变量</b>”这个概念都知道吧，它也是一种“key-value”对，以前也常常看到版上问“如何取得系统某某信息”之类的问题，其实很多都保存在环境变量里，只要用一条<br /></p>
		<div>
				<ol>
						<li>
								<b>
										<a href="http://spaces.msn.com/source/jdk142/java/lang/System.java.html">
												<font color="#0000ff">
														<u>System</u>
												</font>
										</a>
								</b>.getProperties().list(<b><a href="http://spaces.msn.com/source/jdk142/java/lang/System.java.html"><font color="#0000ff"><u>System</u></font></a></b>.out);  </li>
				</ol>
		</div>
		<p>
				<br />就能获得全部环境变量的列表： <br /><br />-- listing properties --<br />java.runtime.name=Java(TM) 2 Runtime Environment, Stand...<br />sun.boot.library.path=C:\Program Files\Java\j2re1.4.2_05\bin<br />java.vm.version=1.4.2_05-b04<br />java.vm.vendor=Sun Microsystems Inc.<br />java.vendor.url=http://java.sun.com/<br />path.separator=;<br />java.vm.name=Java HotSpot(TM) Client VM<br />file.encoding.pkg=sun.io<br />user.country=CN<br />sun.os.patch.level=Service Pack 1<br />java.vm.specification.name=Java Virtual Machine Specification<br />user.dir=d:\my documents\项目\eclipse\SWTDemo<br />java.runtime.version=1.4.2_05-b04<br />java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment<br />java.endorsed.dirs=C:\Program Files\Java\j2re1.4.2_05\li...<br />os.arch=x86<br />java.io.tmpdir=C:\DOCUME~1\cn2lx0q0\LOCALS~1\Temp\<br />line.separator=<br /><br />java.vm.specification.vendor=Sun Microsystems Inc.<br />user.variant=<br />os.name=Windows XP<br />sun.java2d.fontpath=<br />java.library.path=C:\Program Files\Java\j2re1.4.2_05\bi...<br />java.specification.name=Java Platform API Specification<br />java.class.version=48.0<br />java.util.prefs.PreferencesFactory=java.util.prefs.WindowsPreferencesFac...<br />os.version=5.1<br />user.home=D:\Users\cn2lx0q0<br />user.timezone=<br />java.awt.printerjob=sun.awt.windows.WPrinterJob<br />file.encoding=GBK<br />java.specification.version=1.4<br />user.name=cn2lx0q0<br />java.class.path=d:\my documents\项目\eclipse\SWTDemo\bi...<br />java.vm.specification.version=1.0<br />sun.arch.data.model=32<br />java.home=C:\Program Files\Java\j2re1.4.2_05<br />java.specification.vendor=Sun Microsystems Inc.<br />user.language=zh<br />awt.toolkit=sun.awt.windows.WToolkit<br />java.vm.info=mixed mode<br />java.version=1.4.2_05<br />java.ext.dirs=C:\Program Files\Java\j2re1.4.2_05\li...<br />sun.boot.class.path=C:\Program Files\Java\j2re1.4.2_05\li...<br />java.vendor=Sun Microsystems Inc.<br />file.separator=\<br />java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport...<br />sun.cpu.endian=little<br />sun.io.unicode.encoding=UnicodeLittle<br />sun.cpu.isalist=pentium i486 i386<br /> <br /><br />主要类与方法和描述 <br /></p>
		<div>
				<ol>
						<li>load() <i><font color="#339900">//从一个外部流读取属性 </font></i></li>
						<li>store() <i><font color="#339900">//将属性保存到外部流（特别是文件） </font></i></li>
						<li>getProperty() <i><font color="#339900">//取得一个指定的属性 </font></i></li>
						<li>setProperty() <i><font color="#339900">//设置一个指定的属性 </font></i></li>
						<li>list() <i><font color="#339900">//列出这个Properties对象包含的全部“key-value”对 </font></i></li>
						<li>
								<b>
										<a href="http://spaces.msn.com/source/jdk142/java/lang/System.java.html">
												<font color="#0000ff">
														<u>System</u>
												</font>
										</a>
								</b>.getProperties() <i><font color="#339900">//取得系统当前的环境变量 </font></i></li>
				</ol>
		</div>
		<p>
				<br />
				<br />    你可以这样保存一个properties文件： <br /><br /></p>
		<div>
				<ol>
						<li>
								<font color="#ff0000">Properties</font> prop = <b><font color="#0000ff">new</font></b> <font color="#ff0000">Properties</font>(); 
</li>
						<li>prop.setProperty(<font color="#ff33ff">"key1"</font>, <font color="#ff33ff">"value1"</font>); 
</li>
						<li>... 
</li>
						<li>
								<font color="#ff0000">FileOutputStream</font> out = <b><font color="#0000ff">new</font></b> <font color="#ff0000">FileOutputStream</font>(<font color="#ff33ff">"config.properties"</font>); 
</li>
						<li>prop.store(out, <font color="#ff33ff">"--这里是文件头，可以加入注释--"</font>);  </li>
				</ol>
		</div>
		<p>
				<br />★<b>Preferences</b><br />    如果我说Java里面可以不使用JNI的手段操作Windows的注册表你信不信？很多软件的菜单里都有“Setting”或“Preferences”这样的选项用来设定或修改软件的配置，这些配置信息可以保存到一个像上面所述的配置文件当中，如果是Windows平台下，也可能会保存到系统注册表中。从JDK 1.4开始，Java在<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/package-summary.html"><u><font color="#0000ff">java.util</font></u></a>下加入了一个专门处理用户和系统配置信息的<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/prefs/package-summary.html"><u><font color="#0000ff">java.util.prefs</font></u></a>包，其中一个类<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/prefs/Preferences.html"><u><font color="#0000ff">Preferences</font></u></a>是一种比较“高级”的玩意。从本质上讲，Preferences本身是一个与平台无关的东西，但不同的OS对它的SPI（Service Provider Interface）的实现却是与平台相关的，因此，在不同的系统中你可能看到首选项保存为本地文件、LDAP目录项、数据库条目等，像在Windows平台下，它就保存到了系统注册表中。不仅如此，你还可以把首选项导出为XML文件或从XML文件导入。 <br /><br />主要类与方法和描述 <br /></p>
		<div>
				<ol>
						<li>systemNodeForPackage() <i><font color="#339900">//根据指定的Class对象得到一个Preferences对象，这个对象的注册表路径是从“HKEY_LOCAL_MACHINE\”开始的 </font></i></li>
						<li>systemRoot() <i><font color="#339900">//得到以注册表路径HKEY_LOCAL_MACHINE\SOFTWARE\Javasoft\Prefs 为根结点的Preferences对象 </font></i></li>
						<li>userNodeForPackage() <i><font color="#339900">//根据指定的Class对象得到一个Preferences对象，这个对象的注册表路径是从“HKEY_CURRENT_USER\”开始的 </font></i></li>
						<li>userRoot() <i><font color="#339900">//得到以注册表路径HKEY_CURRENT_USER\SOFTWARE\Javasoft\Prefs 为根结点的Preferences对象 </font></i></li>
						<li>putXXX() <i><font color="#339900">//设置一个属性的值，这里XXX可以为基本数值型类型，如int、long等，但首字母大写，表示参数为相应的类型，也可以不写而直接用put，参数则为字符串 </font></i></li>
						<li>getXXX() <i><font color="#339900">//得到一个属性的值 </font></i></li>
						<li>exportNode() <i><font color="#339900">//将全部首选项导出为一个XML文件 </font></i></li>
						<li>exportSubtree() <i><font color="#339900">//将部分首选项导出为一个XML文件 </font></i></li>
						<li>importPreferences() <i><font color="#339900">//从XML文件导入首选项 </font></i></li>
				</ol>
		</div>
		<p>
				<br />    你可以按如下步骤保存数据：<br /></p>
		<div>
				<ol>
						<li>
								<font color="#ff0000">Preferences</font> myPrefs1 = <font color="#ff0000">Preferences</font>.userNodeForPackage(<b><font color="#0000ff">this</font></b>);<i><font color="#339900">// 这种方法是在“HKEY_CURRENT_USER\”下按当前类的路径建立一个注册表项</font></i></li>
						<li>
								<font color="#ff0000">Preferences</font> myPrefs2 = <font color="#ff0000">Preferences</font>.systemNodeForPackage(<b><font color="#0000ff">this</font></b>);<i><font color="#339900">// 这种方法是在“HKEY_LOCAL_MACHINE\”下按当前类的路径建立一个注册表项</font></i></li>
						<li>
								<font color="#ff0000">Preferences</font> myPrefs3 = <font color="#ff0000">Preferences</font>.userRoot().node(<font color="#ff33ff">"com.jungleford.demo"</font>);<i><font color="#339900">// 这种方法是在“HKEY_CURRENT_USER\SOFTWARE\Javasoft\Prefs\”下按“com\jungleford\demo”的路径建立一个注册表项</font></i></li>
						<li>
								<font color="#ff0000">Preferences</font> myPrefs4 = <font color="#ff0000">Preferences</font>.systemRoot().node(<font color="#ff33ff">"com.jungleford.demo"</font>);<i><font color="#339900">// 这种方法是在“HKEY_LOCAL_MACHINE\SOFTWARE\Javasoft\Prefs\”下按“com\jungleford\demo”的路径建立一个注册表项</font></i></li>
						<li>myPrefs1.putInt(<font color="#ff33ff">"key1"</font>, 10); 
</li>
						<li>myPrefs1.putDouble(<font color="#ff33ff">"key2"</font>, -7.15); 
</li>
						<li>myPrefs1.put(<font color="#ff33ff">"key3"</font>, <font color="#ff33ff">"value3"</font>); 
</li>
						<li>
								<font color="#ff0000">FileOutputStream</font> out = <b><font color="#0000ff">new</font></b> <font color="#ff0000">FileOutputStream</font>(<font color="#ff33ff">"prefs.xml"</font>); 
</li>
						<li>myPrefs1.exportNode(out); </li>
				</ol>
		</div>
		<p>
				<br /> </p>
		<h3>
				<b>
						<u>网络I/O：Socket→RMI</u>
				</b>
		</h3>
		<p>
				<br />★<b>Socket</b><br />    Socket编程可能大家都很熟，所以就不多讨论了，只是说通过socket把数据保存到远端服务器或从网络socket读取数据也不失为一种值得考虑的方式。<br /><br />★<b>RMI</b><br />    RMI机制其实就是RPC（远程过程调用）的Java版本，它使用socket作为基本传输手段，同时也是序列化最重要的一个应用。现在网络传输从编程的角度来看基本上都是<b>以流的方式</b>操作，socket就是一个例子，将对象转换成字节流的一个重要目标就是为了方便网络传输。<br />    想象一下传统的<b>单机环境</b>下的程序设计，对于Java语言的函数（方法）调用（注意与C语言函数调用的区别）的参数传递，会有两种情况：如果是<b>基本数据类型</b>，这种情况下和C语言是一样的，采用<b>值传递</b>方式；如果是<b>对象</b>，则传递的是对象的<b>引用</b>，包括返回值也是引用，而不是一个完整的对象拷贝！试想一下在<b>不同的虚拟机</b>之间进行方法调用，即使是两个完全同名同类型的对象他们也很可能是不同的引用！此外对于方法调用过程，由于被调用过程的压栈，内存“现场”完全被被调用者占有，当被调用方法返回时，才将调用者的地址写回到程序计数器（PC），恢复调用者的状态，如果是两个虚拟机，根本不可能用简单压栈的方式来保存调用者的状态。因为种种原因，我们才需要建立RMI通信实体之间的“代理”对象，譬如“存根”就相当于远程服务器对象在客户机上的代理，stub就是这么来的，当然这是后话了。<br />    本地对象与远程对象（未必是物理位置上的不同机器，只要不是在同一个虚拟机内皆为“远程”）之间传递参数和返回值，可能有这么几种情形：<br /></p>
		<ul>
				<li>值传递：这又包括两种子情形：如果是基本数据类型，那么都是“可序列化”的，统统序列化成可传输的字节流；如果是对象，而且不是“远程对象”（所谓“远程对象”是实现了java.rmi.Remote接口的对象），本来对象传递的应该是引用，但由于上述原因，引用是不足以证明对象身份的，所以传递的仍然是<b>一个序列化的拷贝</b>（当然这个对象也必须满足上述“可序列化”的条件）。 <br /></li>
				<li>引用传递：可以引用传递的只能是“远程对象”。这里所谓的“引用”不要理解成了真的只是一个符号，它其实是一个留在（客户机）本地stub中的，和远端服务器上那个真实的对象张得一模一样的<b>镜像</b>而已！只是因为它有点“特权”（不需要经过序列化），在本地内存里已经有了一个实例，真正引用的其实是这个“孪生子”。</li>
		</ul>
		<p>
				<br />    由此可见，序列化在RMI当中占有多么重要的地位。<br /></p>
		<h3>
				<b>
						<u>数据库I/O：CMP、Hibernate</u>
				</b>
		</h3>
		<p>
				<br />★<b>什么是“Persistence”</b><br />    用过VMWare的朋友大概都知道当一个guest OS正在运行的时候点击“Suspend”将虚拟OS挂起，它会把整个虚拟内存的内容保存到磁盘上，譬如你为虚拟OS分配了128M的运行内存，那挂起以后你会在虚拟OS所在的目录下找到一个同样是128M的文件，这就是虚拟OS内存的完整镜像！这种内存的镜像手段其实就是“Persistence”（持久化）概念的由来。<br /><br />★<b>CMP和Hibernate</b><br />    因为我对J2EE的东西不是太熟悉，随便找了点材料看看，所以担心说的不到位，这次就不作具体总结了，人要学习……真是一件痛苦的事情<img height="20" alt="[cry]" src="http://www.javaresearch.org/faces/9.gif" width="20" border="0" /><br /></p>
		<h3>
				<b>
						<u>序列化再探讨</u>
				</b>
		</h3>
		<p>
				<br />    从以上技术的讨论中我们不难体会到，序列化是Java之所以能够出色地实现其鼓吹的两大卖点??分布式（distributed）和跨平台（OS independent）的一个重要基础。TIJ（即“<a href="http://www.mindview.net/Books/TIJ/"><u><font color="#0000ff">Thinking in Java</font></u></a>”）谈到I/O系统时，把序列化称为“lightweight persistence”??“轻量级的持久化”，这确实很有意思。<br /><br />★<b>为什么叫做“序列”化？</b><br />    开场白里我说更习惯于把“Serialization”称为“序列化”而不是“串行化”，这是有原因的。介绍这个原因之前先回顾一些计算机基本的知识，我们知道现代计算机的内存空间都是<b>线性编址</b>的（什么是“线性”知道吧，就是一个元素只有一个唯一的“前驱”和唯一的“后继”，当然头尾元素是个例外；对于地址来说，它的下一个地址当然不可能有两个，否则就乱套了），“地址”这个概念推广到数据结构，就相当于“指针”，这个在本科低年级大概就知道了。注意了，既然是线性的，那“地址”就可以看作是内存空间的“序号”，说明它的组织是有顺序的，“<b>序号</b>”或者说“序列号”正是“Serialization”机制的一种体现。为什么这么说呢？譬如我们有两个对象a和b,分别是类A和B的实例，它们都是可序列化的，而A和B都有一个类型为C的属性，根据前面我们说过的原则，C当然也必须是可序列化的。<br /></p>
		<div>
				<ol>
						<li>
								<b>
										<font color="#0000ff">import</font>
								</b> java.io.*; 
</li>
						<li>... 
</li>
						<li>
								<b>
										<font color="#0000ff">class</font>
								</b> A <b><font color="#0000ff">implements</font></b> <b><a href="http://spaces.msn.com/source/jdk142/java/io/Serializable.java.html"><font color="#0000ff"><u>Serializable</u></font></a></b></li>
						<li>{ 
</li>
						<li>  C c; 
</li>
						<li>  ... 
</li>
						<li>} 
</li>
						<li>
						</li>
						<li>
								<b>
										<font color="#0000ff">class</font>
								</b> B <b><font color="#0000ff">implements</font></b> <b><a href="http://spaces.msn.com/source/jdk142/java/io/Serializable.java.html"><font color="#0000ff"><u>Serializable</u></font></a></b></li>
						<li>{ 
</li>
						<li>  C c; 
</li>
						<li>  ... 
</li>
						<li>} 
</li>
						<li>
						</li>
						<li>
								<b>
										<font color="#0000ff">class</font>
								</b> C <b><font color="#0000ff">implements</font></b> <b><a href="http://spaces.msn.com/source/jdk142/java/io/Serializable.java.html"><font color="#0000ff"><u>Serializable</u></font></a></b></li>
						<li>{ 
</li>
						<li>  ... 
</li>
						<li>} 
</li>
						<li>
						</li>
						<li>A a; 
</li>
						<li>B b; 
</li>
						<li>C c1; 
</li>
						<li>... </li>
				</ol>
		</div>
		<p>
				<br />    注意，这里我们在实例化a和b的时候，有意让他们的c属性使用同一个C类型对象的引用，譬如c1，那么请试想一下，但我们序列化a和b的时候，它们的c属性在外部字节流（当然可以不仅仅是文件）里保存的是一份拷贝还是两份拷贝呢？序列化在这里使用的是一种<b>类似于“指针”的方案</b>：它为每个被序列化的对象标上一个“<b>序列号</b>”（serial number），但序列化一个对象的时候，如果其某个属性对象是已经被序列化的，那么这里只向输出流写入该属性的序列号；从字节流恢复被序列化的对象时，也根据序列号找到对应的流来恢复。这就是“序列化”名称的由来！这里我们看到“序列化”和“指针”是极相似的，只不过“指针”是内存空间的地址链，而序列化用的是<b>外部流中的“序列号链”</b>。<br />    使用“序列号”而不是内存地址来标识一个被序列化的对象，是因为从流中恢复对象到内存，其地址可能就未必是原来的地址了??我们需要的只是这些对象之间的引用关系，而不是死板的原始位置，这在RMI中就更是必要，在两台不同的机器之间传递对象（流），根本就不可能指望它们在两台机器上都具有相同的内存地址。 <br /><br />★<b>更灵活的“序列化”：transient属性和Externalizable</b><br />    Serializable确实很方便，方便到你几乎不需要做任何额外的工作就可以轻松将内存中的对象保存到外部。但有两个问题使得Serializable的威力收到束缚：<br />    一个是效率问题，《Core Java 2》中指出，Serializable使用系统默认的序列化机制会影响软件的运行速度，因为需要为每个属性的引用编号和查号，再加上I/O操作的时间（I/O和内存读写差的可是一个数量级的大小），其代价当然是可观的。<br />    另一个困扰是“裸”的Serializable<b>不可定制</b>，傻乎乎地什么都给你序列化了，不管你是不是想这么做。其实你可以有至少三种定制序列化的选择。其中一种前面已经提到了，就是在implements Serializable的类里面添加私有的writeObject()和readObject()方法（这种Serializable就不裸了，<img height="20" alt="[:E]" src="http://www.javaresearch.org/faces/13.gif" width="20" border="0" />），在这两个方法里，该序列化什么，不该序列化什么，那就由你说了算了，你当然可以在这两个方法体里面分别调用ObjectOutputStream.<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectOutputStream.html#defaultWriteObject()"><u><font color="#0000ff">defaultWriteObject</font></u></a>()和ObjectInputStream.<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/ObjectInputStream.html#defaultReadObject()"><u><font color="#0000ff">defaultReadObject</font></u></a>()仍然执行<b>默认的序列化动作</b>（那你在代码上不就做无用功了？呵呵），也可以用ObjectOutputStream.writeObject()和ObjectInputStream.readObject()方法对你中意的属性进行序列化。但虚拟机一看到你定义了这两个方法，它就不再用默认的机制了。<br />    如果仅仅为了跳过某些属性不让它序列化，上面的动作似乎显得麻烦，更简单的方法是对不想序列化的属性加上<b>transient</b>关键字，说明它是个“暂态变量”，默认序列化的时候就不会把这些属性也塞到外部流里了。当然，你如果定义writeObject()和readObject()方法的化，仍然可以把暂态变量进行序列化。题外话，像<b>transient</b>、<b>violate</b>、<b>finally</b>这样的关键字初学者可能会不太重视，而现在有的公司招聘就偏偏喜欢问这样的问题 :(<br />    再一个方案就是不实现Serializable而改成实现<a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/Externalizable.html"><u><font color="#0000ff">Externalizable</font></u></a>接口。我们研究一下这两个接口的源代码，发现它们很类似，甚至容易混淆。我们要记住的是：Externalizable默认并<b>不保存任何对象相关信息</b>！任何保存和恢复对象的动作都是你自己定义的。Externalizable包含两个public的方法：<br /></p>
		<div>
				<ol>
						<li>
								<b>
										<font color="#0000ff">public</font>
								</b> <b><font color="#0000ff">void</font></b> writeExternal(<font color="#ff0000">ObjectOutput</font> out) <b><font color="#0000ff">throws</font></b> <font color="#ff0000">IOException</font>; 
</li>
						<li>
								<b>
										<font color="#0000ff">public</font>
								</b> <b><font color="#0000ff">void</font></b> readExternal(<font color="#ff0000">ObjectInput</font> in) <b><font color="#0000ff">throws</font></b> <font color="#ff0000">IOException</font>, <b><a href="http://spaces.msn.com/source/jdk142/java/lang/ClassNotFoundException.java.html"><font color="#0000ff"><u>ClassNotFoundException</u></font></a></b>; </li>
				</ol>
		</div>
		<p>
				<br />    乍一看这和上面的writeObject()和readObject()几乎差不多，但Serializable和Externalizable走的是两个不同的流程：Serializable在对象不存在的情况下，就可以仅凭外部的字节序列把整个对象重建出来；但Externalizable在重建对象时，先是调用该类的默认构造函数（即不含参数的那个构造函数）使得内存中先有这么一个实例，然后再调用readExternal方法对实例中的属性进行恢复，因此，如果默认构造函数中和readExternal方法中都没有赋值的那些属性，特别他们是非基本类型的话，将会是空（null）。在这里需要注意的是，<b>transient只能用在对Serializable而不是Externalizable的实现里面</b>。 <br /><br />★<b>序列化与克隆</b><br />    从“可序列化”的递归定义来看，一个序列化的对象貌似对象内存映象的外部克隆，如果没有共享引用的属性的化，那么应该是一个<b>深度克隆</b>。关于克隆的话题有可以谈很多，这里就不细说了，有兴趣的话可以参考<a href="http://www-900.ibm.com/developerWorks/cn/"><u><font color="#0000ff">IBM developerWorks</font></u></a>上的一篇文章：<a href="http://www-900.ibm.com/developerWorks/cn/java/l-jpointer/index.shtml"><u><font color="#0000ff">JAVA中的指针,引用及对象的clone</font></u></a><br /></p>
		<h3>
				<b>
						<u>一点启示</u>
				</b>
		</h3>
		<p>
				<br />    作为一个实际的应用，我在写那个简易的邮件客户端JExp的时候曾经对比过好几种保存Message对象（主要是几个关键属性和邮件的内容）到本地的方法，譬如XML、Properties等，最后还是选择了用序列化的方式，因为这种方法最简单， 大约可算是“学以致用”罢。这里“存取程序状态”其实只是一个引子话题罢了，我想说的是??就如同前面我们讨论的关于logging的话题一样??在Java面前对同一个问题你可以有很多种solution：熟悉文件操作的，你可能会觉得Properties、XML或Bean比较方便，然后又发现了还有Preferences这么一个东东，大概又会感慨“天外有天”了，等到你接触了很多种新方法以后，结果又会“殊途同归”，重新反省Serialization机制本身。这不仅是Java，科学也是同样的道理。</p>
<img src ="http://www.blogjava.net/hua/aggbug/66590.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hua/" target="_blank">花</a> 2006-08-30 10:31 <a href="http://www.blogjava.net/hua/archive/2006/08/30/66590.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java中的时间操作和格式化</title><link>http://www.blogjava.net/hua/archive/2006/07/21/59459.html</link><dc:creator>花</dc:creator><author>花</author><pubDate>Fri, 21 Jul 2006 09:24:00 GMT</pubDate><guid>http://www.blogjava.net/hua/archive/2006/07/21/59459.html</guid><wfw:comment>http://www.blogjava.net/hua/comments/59459.html</wfw:comment><comments>http://www.blogjava.net/hua/archive/2006/07/21/59459.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hua/comments/commentRss/59459.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hua/services/trackbacks/59459.html</trackback:ping><description><![CDATA[
		<p>一、获取当前时间</p>
		<p>有两种方式可以获得，第一种，使用Date类。</p>
		<p>j2SE的包里有两个Date类，一个是java.sql.Date,一个是java.util.Date</p>
		<p>这里，要使用java.util.Date。获取当前时间的代码如下</p>
		<p>Date date = new Date();</p>
		<p>date.getTime();</p>
		<p>还有一种方式，使用System.currentTimeMillis();</p>
		<p>这两种方式获得的结果是一样的，都是得到一个当前的时间的long型的时间的毫秒值，这个值实际上是当前时间值与1970年一月一号零时零分零秒相差的毫秒数。</p>
		<p>当前的时间得到了，但实际的应用中最后往往不是要用这个long型的东西，用户希望得到的往往是一个时间的字符串，比如“2006年6月18号”，或“2006-06-18”，老外可能希望得到的是“06-18-2006”，诸如此类等等。这就是下一个要解决的问题</p>
		<p>二、获取某个时间的某种格式</p>
		<p>获取时间的格式，需要用到一个专门用于时间格式的类java.text.SimpleDateFormat。</p>
		<p>首先，定义一个SimpleDateFormat变量</p>
		<p>SimpleDateFormat sdf = new SimpleDateFormat("",Locale.SIMPLIFIED_CHINESE);</p>
		<p>这个构造函数的定义如下：</p>
		<p>SimpleDateFormat(String pattern, Locale locale) </p>
		<p>第一个参数pattern，我们后面再解释，这里我们使用一个"",第二个参数，是用来设置时区的，这里用到了java.util.Locale这个类，这个类了面定义了很多静态变量，直接拿过来用就OK，我们把时区设置为Locale.SIMPLIFIED_CHINESE，只看名字，这个静态变量的意义已经很清楚了。</p>
		<p>接下来我们使用这个SimpleDateFormat把当前时间格式化为一个如下格式的时间字符串“XXXX年XX月XX日_XX时XX分XX秒”，代码：</p>
		<p>sdf.applyPattern("yyyy年MM月dd日_HH时mm分ss秒");</p>
		<p>String timeStr = sdf.format(new Date()); </p>
		<p>获取时间格式的函数是format，这个函数的参数是java.util.Date对象，这个没有什么花头。</p>
		<p>要说明一下的是这个pattern，所谓的模式。这里，yyyy,MM,dd等，这就是模式。</p>
		<p>我们可以在SimpleDateFormat的构造函数中指定模式，比如</p>
		<p>SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd",Locale.SIMPLIFIED_CHINESE);</p>
		<p>也可以获取时间格式的时候使用applyPattern函数临时指定，上面的例子就是这样。</p>
		<p>什么字符代表什么，这是j2se约定好的，设置模式的时候，我们可以使用约定好的字符加上任何我们想要的字符串。</p>
		<p>j2se对字符所代表的模式的约定列表如下：</p>
		<p>
				<span lang="ZH-CN" style="FONT-FAMILY: SimSun"> 
<table cellspacing="1" cellpadding="1" width="500" align="center" summary="" border="0"><tbody><tr><td><font size="3">Letter</font></td><td><font size="3"><span style="FONT-WEIGHT: bold"><font size="3">Date or Time Component</font></span></font></td><td><font size="3"><span style="FONT-WEIGHT: bold"><font size="3">Presentation </font></span></font></td></tr><tr><td>G</td><td>Era designator </td><td>Text </td></tr><tr><td>y</td><td>Year </td><td>Year </td></tr><tr><td>M </td><td>Month in year </td><td>Month </td></tr><tr><td>w </td><td>Week in year </td><td>Number </td></tr><tr><td>W </td><td>Week in month </td><td>Number </td></tr><tr><td>D </td><td>Day in year</td><td>Number </td></tr><tr><td>d </td><td>Day in month </td><td>Number </td></tr><tr><td>F </td><td>Day of week in month </td><td>Number </td></tr><tr><td>E </td><td>Day in week </td><td>Text</td></tr><tr><td>a </td><td>Am/pm marker </td><td>Text </td></tr><tr><td>H </td><td>Hour in day (0-23)</td><td>Number </td></tr><tr><td>k </td><td>Hour in day (1-24)</td><td>Number </td></tr><tr><td>K </td><td>Hour in am/pm (0-11) </td><td>Number </td></tr><tr><td>h  </td><td>Hour in am/pm (1-12) </td><td>Number </td></tr><tr><td>m  </td><td>Minute in hour </td><td>Number </td></tr><tr><td>s  </td><td>Second in minute </td><td>Number </td></tr><tr><td>S  </td><td>Millisecond </td><td>Number  </td></tr><tr><td>z  </td><td>Time zone  </td><td>General time zone </td></tr><tr><td>Z  </td><td>Time zone </td><td>RFC 822 time zone </td></tr></tbody></table></span>
		</p>
<img src ="http://www.blogjava.net/hua/aggbug/59459.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hua/" target="_blank">花</a> 2006-07-21 17:24 <a href="http://www.blogjava.net/hua/archive/2006/07/21/59459.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于Java文件路径问题</title><link>http://www.blogjava.net/hua/archive/2006/06/27/55268.html</link><dc:creator>花</dc:creator><author>花</author><pubDate>Tue, 27 Jun 2006 02:12:00 GMT</pubDate><guid>http://www.blogjava.net/hua/archive/2006/06/27/55268.html</guid><wfw:comment>http://www.blogjava.net/hua/comments/55268.html</wfw:comment><comments>http://www.blogjava.net/hua/archive/2006/06/27/55268.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hua/comments/commentRss/55268.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hua/services/trackbacks/55268.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<p>
						<font face="Courier New">1.如何获得当前文件路径</font>
				</p>
				<p>
						<font face="Courier New">常用：</font>
				</p>
				<p>
						<font face="Courier New">字符串类型：System.getProperty("user.dir");</font>
				</p>
				<p>
						<font face="Courier New">综合：</font>
				</p>
				<p>
						<font face="Courier New">package com.zcjl.test.base;<br />import java.io.File;<br />public class Test {<br />    public static void main(String[] args) throws Exception {<br />        System.out.println(<br /></font>
						<font face="Courier New">
								<font color="#ff0000">            Thread.currentThread().getContextClassLoader().getResource(""));<br />        System.out.println(Test.class.getClassLoader().getResource(""));<br />        System.out.println(ClassLoader.getSystemResource(""));<br />        System.out.println(Test.class.getResource(""));<br />        System.out.println(Test.class.getResource("/"));<br />        System.out.println(new File("").getAbsolutePath());<br />        System.out.println(System.getProperty("user.dir"));</font>
								<br />    }<br />}</font>
				</p>
				<p>
						<font face="Courier New">2.Web服务中</font>
				</p>
				<p>
						<font face="Courier New">(1).Weblogic</font>
				</p>
				<p>
						<font face="Courier New">WebApplication的系统文件根目录是你的weblogic安装所在根目录。<br />例如：如果你的weblogic安装在c:\bea\weblogic700.....<br />那么，你的文件根路径就是c:\.<br />所以，有两种方式能够让你访问你的服务器端的文件：<br />a.使用绝对路径：<br />比如将你的参数文件放在c:\yourconfig\yourconf.properties，<br />直接使用 new FileInputStream("yourconfig/yourconf.properties");<br />b.使用相对路径：<br />相对路径的根目录就是你的webapplication的根路径，即WEB-INF的上一级目录，将你的参数文件放在yourwebapp\yourconfig\yourconf.properties，<br />这样使用：<br />new FileInputStream("./yourconfig/yourconf.properties");<br />这两种方式均可，自己选择。</font>
				</p>
				<p>
						<font face="Courier New">(2).Tomcat</font>
				</p>
				<p>
						<font face="Courier New">在类中输出System.getProperty("user.dir");显示的是%Tomcat_Home%/bin</font>
				</p>
				<p>
						<font face="Courier New">(3).Resin</font>
				</p>
				<p>
						<font face="Courier New">不是你的JSP放的相对路径,是JSP引擎执行这个JSP编译成SERVLET<br />的路径为根.比如用新建文件法测试File f = new File("a.htm");<br />这个a.htm在resin的安装目录下 </font>
				</p>
				<p>
						<font face="Courier New" color="#ff0000">(4).如何读相对路径哪？</font>
				</p>
				<p>
						<font face="Courier New" color="#ff0000">在Java文件中getResource或getResourceAsStream均可</font>
				</p>
				<p>
						<font face="Courier New" color="#ff0000">例：getClass().getResourceAsStream(filePath);//filePath可以是"/filename",这里的/代表web发布根路径下WEB-INF/classes</font>
				</p>
				<p>
						<font face="Courier New">(5).获得文件真实路径</font>
				</p>
				<p>
						<font face="Courier New">string  file_real_path=request.getRealPath("mypath/filename");  </font>
				</p>
				<p>
						<font face="Courier New">通常使用request.getRealPath("/");  </font>
				</p>
				<p>
						<font face="Courier New">3.文件操作的类</font>
				</p>
				<p>
						<font face="Courier New">import java.io.*;<br />import java.net.*;<br />import java.util.*;<br />//import javax.swing.filechooser.*;<br />//import org.jr.swing.filter.*;</font>
				</p>
				<p>
						<font face="Courier New">/**<br />* 此类中封装一些常用的文件操作。<br />* 所有方法都是静态方法，不需要生成此类的实例，<br />* 为避免生成此类的实例，构造方法被申明为private类型的。<br />* @since  0.1<br />*/</font>
				</p>
				<p>
						<font face="Courier New">public class FileUtil {<br />  /**<br />   * 私有构造方法，防止类的实例化，因为工具类不需要实例化。<br />   */<br />  private FileUtil() {</font>
				</p>
				<p>
						<font face="Courier New">  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 修改文件的最后访问时间。<br />   * 如果文件不存在则创建该文件。<br />   * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考</font>
				</p>
				<p>
						<font face="Courier New">虑中。&lt;/b&gt;<br />   * @param file 需要修改最后访问时间的文件。<br />   * @since  0.1<br />   */<br />  public static void touch(File file) {<br />    long currentTime = System.currentTimeMillis();<br />    if (!file.exists()) {<br />      System.err.println("file not found:" + file.getName());<br />      System.err.println("Create a new file:" + file.getName());<br />      try {<br />        if (file.createNewFile()) {<br />        //  System.out.println("Succeeded!");<br />        }<br />        else {<br />        //  System.err.println("Create file failed!");<br />        }<br />      }<br />      catch (IOException e) {<br />      //  System.err.println("Create file failed!");<br />        e.printStackTrace();<br />      }<br />    }<br />    boolean result = file.setLastModified(currentTime);<br />    if (!result) {<br />    //  System.err.println("touch failed: " + file.getName());<br />    }<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 修改文件的最后访问时间。<br />   * 如果文件不存在则创建该文件。<br />   * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考</font>
				</p>
				<p>
						<font face="Courier New">虑中。&lt;/b&gt;<br />   * @param fileName 需要修改最后访问时间的文件的文件名。<br />   * @since  0.1<br />   */<br />  public static void touch(String fileName) {<br />    File file = new File(fileName);<br />    touch(file);<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 修改文件的最后访问时间。<br />   * 如果文件不存在则创建该文件。<br />   * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考</font>
				</p>
				<p>
						<font face="Courier New">虑中。&lt;/b&gt;<br />   * @param files 需要修改最后访问时间的文件数组。<br />   * @since  0.1<br />   */<br />  public static void touch(File[] files) {<br />    for (int i = 0; i &lt; files.length; i++) {<br />      touch(files);<br />    }<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 修改文件的最后访问时间。<br />   * 如果文件不存在则创建该文件。<br />   * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考</font>
				</p>
				<p>
						<font face="Courier New">虑中。&lt;/b&gt;<br />   * @param fileNames 需要修改最后访问时间的文件名数组。<br />   * @since  0.1<br />   */<br />  public static void touch(String[] fileNames) {<br />    File[] files = new File[fileNames.length];<br />    for (int i = 0; i &lt; fileNames.length; i++) {<br />      files = new File(fileNames);<br />    }<br />    touch(files);<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 判断指定的文件是否存在。<br />   * @param fileName 要判断的文件的文件名<br />   * @return 存在时返回true，否则返回false。<br />   * @since  0.1<br />   */<br />  public static boolean isFileExist(String fileName) {<br />    return new File(fileName).isFile();<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 创建指定的目录。<br />   * 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。<br />   * &lt;b&gt;注意：可能会在返回false的时候创建部分父目录。&lt;/b&gt;<br />   * @param file 要创建的目录<br />   * @return 完全创建成功时返回true，否则返回false。<br />   * @since  0.1<br />   */<br />  public static boolean makeDirectory(File file) {<br />    File parent = file.getParentFile();<br />    if (parent != null) {<br />      return parent.mkdirs();<br />    }<br />    return false;<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 创建指定的目录。<br />   * 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。<br />   * &lt;b&gt;注意：可能会在返回false的时候创建部分父目录。&lt;/b&gt;<br />   * @param fileName 要创建的目录的目录名<br />   * @return 完全创建成功时返回true，否则返回false。<br />   * @since  0.1<br />   */<br />  public static boolean makeDirectory(String fileName) {<br />    File file = new File(fileName);<br />    return makeDirectory(file);<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 清空指定目录中的文件。<br />   * 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。<br />   * 另外这个方法不会迭代删除，即不会删除子目录及其内容。<br />   * @param directory 要清空的目录<br />   * @return 目录下的所有文件都被成功删除时返回true，否则返回false.<br />   * @since  0.1<br />   */<br />  public static boolean emptyDirectory(File directory) {<br />    boolean result = false;<br />    File[] entries = directory.listFiles();<br />    for (int i = 0; i &lt; entries.length; i++) {<br />      if (!entries.delete()) {<br />        result = false;<br />      }<br />    }<br />    return true;<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 清空指定目录中的文件。<br />   * 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。<br />   * 另外这个方法不会迭代删除，即不会删除子目录及其内容。<br />   * @param directoryName 要清空的目录的目录名<br />   * @return 目录下的所有文件都被成功删除时返回true，否则返回false。<br />   * @since  0.1<br />   */<br />  public static boolean emptyDirectory(String directoryName) {<br />    File dir = new File(directoryName);<br />    return emptyDirectory(dir);<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 删除指定目录及其中的所有内容。<br />   * @param dirName 要删除的目录的目录名<br />   * @return 删除成功时返回true，否则返回false。<br />   * @since  0.1<br />   */<br />  public static boolean deleteDirectory(String dirName) {<br />    return deleteDirectory(new File(dirName));<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 删除指定目录及其中的所有内容。<br />   * @param dir 要删除的目录<br />   * @return 删除成功时返回true，否则返回false。<br />   * @since  0.1<br />   */<br />  public static boolean deleteDirectory(File dir) {<br />    if ( (dir == null) || !dir.isDirectory()) {<br />      throw new IllegalArgumentException("Argument " + dir +<br />                                         " is not a directory. ");<br />    }</font>
				</p>
				<p>
						<font face="Courier New">    File[] entries = dir.listFiles();<br />    int sz = entries.length;</font>
				</p>
				<p>
						<font face="Courier New">    for (int i = 0; i &lt; sz; i++) {<br />      if (entries.isDirectory()) {<br />        if (!deleteDirectory(entries)) {<br />          return false;<br />        }<br />      }<br />      else {<br />        if (!entries.delete()) {<br />          return false;<br />        }<br />      }<br />    }</font>
				</p>
				<p>
						<font face="Courier New">    if (!dir.delete()) {<br />      return false;<br />    }<br />    return true;<br />  }</font>
				</p>
				<p>
						<br />
						<font face="Courier New">  /**<br />   * 返回文件的URL地址。<br />   * @param file 文件<br />   * @return 文件对应的的URL地址<br />   * @throws MalformedURLException<br />   * @since  0.4<br />   * @deprecated 在实现的时候没有注意到File类本身带一个toURL方法将文件路径转换为URL。<br />   *             请使用File.toURL方法。<br />   */<br />  public static URL getURL(File file) throws MalformedURLException {<br />    String fileURL = "file:/" + file.getAbsolutePath();<br />    URL url = new URL(fileURL);<br />    return url;<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 从文件路径得到文件名。<br />   * @param filePath 文件的路径，可以是相对路径也可以是绝对路径<br />   * @return 对应的文件名<br />   * @since  0.4<br />   */<br />  public static String getFileName(String filePath) {<br />    File file = new File(filePath);<br />    return file.getName();<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 从文件名得到文件绝对路径。<br />   * @param fileName 文件名<br />   * @return 对应的文件路径<br />   * @since  0.4<br />   */<br />  public static String getFilePath(String fileName) {<br />    File file = new File(fileName);<br />    return file.getAbsolutePath();<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 将DOS/Windows格式的路径转换为UNIX/Linux格式的路径。<br />   * 其实就是将路径中的"\"全部换为"/"，因为在某些情况下我们转换为这种方式比较方便，<br />   * 某中程度上说"/"比"\"更适合作为路径分隔符，而且DOS/Windows也将它当作路径分隔符。<br />   * @param filePath 转换前的路径<br />   * @return 转换后的路径<br />   * @since  0.4<br />   */<br />  public static String toUNIXpath(String filePath) {<br />    return filePath.replace('\\', '/');<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 从文件名得到UNIX风格的文件绝对路径。<br />   * @param fileName 文件名<br />   * @return 对应的UNIX风格的文件路径<br />   * @since  0.4<br />   * @see #toUNIXpath(String filePath) toUNIXpath<br />   */<br />  public static String getUNIXfilePath(String fileName) {<br />    File file = new File(fileName);<br />    return toUNIXpath(file.getAbsolutePath());<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 得到文件的类型。<br />   * 实际上就是得到文件名中最后一个“.”后面的部分。<br />   * @param fileName 文件名<br />   * @return 文件名中的类型部分<br />   * @since  0.5<br />   */<br />  public static String getTypePart(String fileName) {<br />    int point = fileName.lastIndexOf('.');<br />    int length = fileName.length();<br />    if (point == -1 || point == length - 1) {<br />      return "";<br />    }<br />    else {<br />      return fileName.substring(point + 1, length);<br />    }<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 得到文件的类型。<br />   * 实际上就是得到文件名中最后一个“.”后面的部分。<br />   * @param file 文件<br />   * @return 文件名中的类型部分<br />   * @since  0.5<br />   */<br />  public static String getFileType(File file) {<br />    return getTypePart(file.getName());<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 得到文件的名字部分。<br />   * 实际上就是路径中的最后一个路径分隔符后的部分。<br />   * @param fileName 文件名<br />   * @return 文件名中的名字部分<br />   * @since  0.5<br />   */<br />  public static String getNamePart(String fileName) {<br />    int point = getPathLsatIndex(fileName);<br />    int length = fileName.length();<br />    if (point == -1) {<br />      return fileName;<br />    }<br />    else if (point == length - 1) {<br />      int secondPoint = getPathLsatIndex(fileName, point - 1);<br />      if (secondPoint == -1) {<br />        if (length == 1) {<br />          return fileName;<br />        }<br />        else {<br />          return fileName.substring(0, point);<br />        }<br />      }<br />      else {<br />        return fileName.substring(secondPoint + 1, point);<br />      }<br />    }<br />    else {<br />      return fileName.substring(point + 1);<br />    }<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 得到文件名中的父路径部分。<br />   * 对两种路径分隔符都有效。<br />   * 不存在时返回""。<br />   * 如果文件名是以路径分隔符结尾的则不考虑该分隔符，例如"/path/"返回""。<br />   * @param fileName 文件名<br />   * @return 父路径，不存在或者已经是父目录时返回""<br />   * @since  0.5<br />   */<br />  public static String getPathPart(String fileName) {<br />    int point = getPathLsatIndex(fileName);<br />    int length = fileName.length();<br />    if (point == -1) {<br />      return "";<br />    }<br />    else if (point == length - 1) {<br />      int secondPoint = getPathLsatIndex(fileName, point - 1);<br />      if (secondPoint == -1) {<br />        return "";<br />      }<br />      else {<br />        return fileName.substring(0, secondPoint);<br />      }<br />    }<br />    else {<br />      return fileName.substring(0, point);<br />    }<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 得到路径分隔符在文件路径中首次出现的位置。<br />   * 对于DOS或者UNIX风格的分隔符都可以。<br />   * @param fileName 文件路径<br />   * @return 路径分隔符在路径中首次出现的位置，没有出现时返回-1。<br />   * @since  0.5<br />   */<br />  public static int getPathIndex(String fileName) {<br />    int point = fileName.indexOf('/');<br />    if (point == -1) {<br />      point = fileName.indexOf('\\');<br />    }<br />    return point;<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 得到路径分隔符在文件路径中指定位置后首次出现的位置。<br />   * 对于DOS或者UNIX风格的分隔符都可以。<br />   * @param fileName 文件路径<br />   * @param fromIndex 开始查找的位置<br />   * @return 路径分隔符在路径中指定位置后首次出现的位置，没有出现时返回-1。<br />   * @since  0.5<br />   */<br />  public static int getPathIndex(String fileName, int fromIndex) {<br />    int point = fileName.indexOf('/', fromIndex);<br />    if (point == -1) {<br />      point = fileName.indexOf('\\', fromIndex);<br />    }<br />    return point;<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 得到路径分隔符在文件路径中最后出现的位置。<br />   * 对于DOS或者UNIX风格的分隔符都可以。<br />   * @param fileName 文件路径<br />   * @return 路径分隔符在路径中最后出现的位置，没有出现时返回-1。<br />   * @since  0.5<br />   */<br />  public static int getPathLsatIndex(String fileName) {<br />    int point = fileName.lastIndexOf('/');<br />    if (point == -1) {<br />      point = fileName.lastIndexOf('\\');<br />    }<br />    return point;<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 得到路径分隔符在文件路径中指定位置前最后出现的位置。<br />   * 对于DOS或者UNIX风格的分隔符都可以。<br />   * @param fileName 文件路径<br />   * @param fromIndex 开始查找的位置<br />   * @return 路径分隔符在路径中指定位置前最后出现的位置，没有出现时返回-1。<br />   * @since  0.5<br />   */<br />  public static int getPathLsatIndex(String fileName, int fromIndex) {<br />    int point = fileName.lastIndexOf('/', fromIndex);<br />    if (point == -1) {<br />      point = fileName.lastIndexOf('\\', fromIndex);<br />    }<br />    return point;<br />  }</font>
				</p>
				<p>
						<font face="Courier New">  /**<br />   * 将文件名中的类型部分去掉。<br />   * @param filename 文件名<br />   * @return 去掉类型部分的结果<br />   * @since  0.5<br />   */<br />  public static String trimType(String filename) {<br />    int index = filename.lastIndexOf(".");<br />    if (index != -1) {<br />      return filename.substring(0, index);<br />    }<br />    else {<br />      return filename;<br />    }<br />  }<br />  /**<br />   * 得到相对路径。<br />   * 文件名不是目录名的子节点时返回文件名。<br />   * @param pathName 目录名<br />   * @param fileName 文件名<br />   * @return 得到文件名相对于目录名的相对路径，目录下不存在该文件时返回文件名<br />   * @since  0.5<br />   */<br />  public static String getSubpath(String pathName,String fileName) {<br />    int index = fileName.indexOf(pathName);<br />    if (index != -1) {<br />      return fileName.substring(index + pathName.length() + 1);<br />    }<br />    else {<br />      return fileName;<br />    }<br />  }</font>
				</p>
				<p>
						<font face="Courier New">}<br /> 4.遗留问题</font>
				</p>
				<p>
						<font face="Courier New">目前new FileInputStream()只会使用绝对路径，相对没用过，因为要相对于web服务器地址，比较麻烦</font>
				</p>
				<p>
						<font face="Courier New">还不如写个配置文件来的快哪</font>
				</p>
				<p>
						<font face="Courier New">5.按Java文件类型分类读取配置文件</font>
				</p>
				<div class="storycontent">
						<p>
								<font face="Courier New">配置文件是应用系统中不可缺少的，可以增加程序的灵活性。java.util.Properties是从jdk1.2就有的类，一直到现在都支持load ()方法，jdk1.4以后save(output,string) -&gt;store(output,string)。如果只是单纯的读，根本不存在烦恼的问题。web层可以通过 Thread.currentThread().getContextClassLoader().<br />getResourceAsStream("xx.properties") 获取；Application可以通过new FileInputStream("xx.properties");直接在classes一级获取。关键是有时我们需要通过web修改配置文件，我们不能将路径写死了。经过测试觉得有以下心得：</font>
						</p>
						<p>
								<font face="Courier New">1.servlet中读写。如果运用Struts 或者Servlet可以直接在初始化参数中配置，调用时根据servlet的getRealPath("/")获取真实路径，再根据String file = this.servlet.getInitParameter("abc");获取相对的WEB-INF的相对路径。<br />例：<br />InputStream input = Thread.currentThread().getContextClassLoader().<br />getResourceAsStream("abc.properties");<br />Properties prop = new Properties();<br />prop.load(input);<br />input.close();<br />OutputStream out = new FileOutputStream(path);<br />prop.setProperty("abc", “test");<br />prop.store(out, “–test–");<br />out.close();</font>
						</p>
						<p>
								<font face="Courier New">2.直接在jsp中操作，通过jsp内置对象获取可操作的绝对地址。<br />例：<br />// jsp页面<br />String path = pageContext.getServletContext().getRealPath("/");<br />String realPath = path+"/WEB-INF/classes/abc.properties";</font>
						</p>
						<p>
								<font face="Courier New">//java 程序<br />InputStream in = getClass().getClassLoader().getResourceAsStream("abc.properties"); // abc.properties放在webroot/WEB-INF/classes/目录下<br />prop.load(in);<br />in.close();</font>
						</p>
						<p>
								<font face="Courier New">OutputStream out = new FileOutputStream(path); // path为通过页面传入的路径<br />prop.setProperty("abc", “abcccccc");<br />prop.store(out, “–test–");<br />out.close();</font>
						</p>
						<p>
								<font face="Courier New">3.只通过Java程序操作资源文件<br />InputStream in = new FileInputStream("abc.properties"); // 放在classes同级</font>
						</p>
						<p>
								<font face="Courier New">OutputStream out = new FileOutputStream("abc.properties"); </font>
						</p>
				</div>
		</div>
<img src ="http://www.blogjava.net/hua/aggbug/55268.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hua/" target="_blank">花</a> 2006-06-27 10:12 <a href="http://www.blogjava.net/hua/archive/2006/06/27/55268.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>