﻿<?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-花香蝶自来</title><link>http://www.blogjava.net/ltc603/</link><description /><language>zh-cn</language><lastBuildDate>Tue, 12 May 2026 08:55:29 GMT</lastBuildDate><pubDate>Tue, 12 May 2026 08:55:29 GMT</pubDate><ttl>60</ttl><item><title>北京英语角</title><link>http://www.blogjava.net/ltc603/archive/2007/04/25/113412.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Wed, 25 Apr 2007 01:29:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2007/04/25/113412.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/113412.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2007/04/25/113412.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/113412.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/113412.html</trackback:ping><description><![CDATA[<div><font style="FONT-SIZE: 14px">北京英语角(BeiJing)www.52en.com<br>=================================<br>北大英语角：每周六晚7:00 俄文楼前空地<br>清华英语角：每周三晚7:00-10:00 西阶（west of Great Hall）<br>人大英语角：每周五晚 人大东门花园，老外多，质量不错<br>北外英语角：每周五晚6:30-8:30外研社后花园<br>朝阳文化馆：每周六下午3：00-5:00 门票6元地点在小庄，收费，但质量好<br></font></div>
<img src ="http://www.blogjava.net/ltc603/aggbug/113412.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2007-04-25 09:29 <a href="http://www.blogjava.net/ltc603/archive/2007/04/25/113412.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>男孩如何追女孩</title><link>http://www.blogjava.net/ltc603/archive/2007/04/23/113015.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Mon, 23 Apr 2007 11:12:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2007/04/23/113015.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/113015.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2007/04/23/113015.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/113015.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/113015.html</trackback:ping><description><![CDATA[1.未到火候，千万别说。有些男孩才跟人家约过几次，就提出要建立&#8220;更进一步&#8221;的关系，这十有八九要坏汤。爱情上的事妙就妙在一切尽在不言中。说清楚了，话挑明了，反而不美。本来人家肯单独出来跟你说话，这说明女孩对你不无好感。但是这种好感有时连她们自己都说不清，你却急于挑明，破坏了这种朦胧美，那就别怪人家敬而远之以致退避三舍了。看看周围那些还没开始就夭折的爱情，许多都是由男孩没有掌握好火候造成的。 <br><br>　　2.收起拳头是为了更好的出击。被女孩拒绝了怎么办呢？有人说应该穷追猛打、坚持不懈。其实不然。除非你是一个啥也不在乎的人，否则你很难承受女孩的白眼和同伴们嘲弄的目光。倒不如偃旗息鼓、暂时撤退。这不仅保护了你的尊严而且还留了条后路，从而为日后反攻创造机会。就让她在人群里闯荡。等她碰得头破血流时，你再去找她，她才能认识到原来你才是最爱她的人。即使实在割舍不下，要去找她，也应注意方式方法。千万别让她觉得你讨厌。而这一点往往是很难做到的。&nbsp; <br><br>　　3.不要太露骨。要学会不声不响地关心她，用你的诚实和善意对待她。只有这样你才能在一大帮围着她呱呱乱叫的男孩当中引起她的注意。记住，只有特别的你才会引起她特别的关注。&nbsp; <br><br>　　4.非请勿入。一个老是往女孩寝室跑的男孩是不会引起女孩太多的好感的。有些学生会干部借口工作常往女生寝室跑，去了后就老赖在那不走，结果给人家带来了诸多不便，效果只会适得其反。产生这种结果的根本原因还是因为太露骨。 <br><br>　　5.战略上藐视...，战术上重视...。有时你喜爱的女孩会和你有些接触，比如谈谈话、聊聊天、一起工作等等。你最好能以平常的心态看待这些事情，不要背上包袱、患得患失。例如不要太在意她无意中说的话，有些男孩容易自作多情，源出于此。但对每一次这样的机会则应引起足够的重视，例如初次谈话要注意掌握好分寸，最好不要涉及情爱范畴，不妨说说小时候的事。你如果只奔主体那就要糟！女孩非得象警惕狼一样地警惕你。&nbsp; <br><br>　　6.马屁最好少拍。你夸她长得漂亮，如果她真的漂亮，那么你不过是第七百个夸她漂亮的人，而她映象最深的恐怕是第一个这样夸她的人；如果她相貌普通，你这样夸她能产生什么样的结果谁也说不准。要是让她发现你言不由衷，那你就死定了。记住，哄女孩只有在女孩成为你的女朋友之后才能哄，还没追到手就开始哄是没有掌握好火候的表现。&nbsp; <br><br>　　7.少来点大男子主义。有个男孩好不容易请得他倾慕已久的女孩去吃饭，花了他半个月生活费。后来他去付账时发现女孩已经替他付了，他就要还她钱。女孩不愿意。他觉得女孩剥了他的面子，大为光火。女孩气得哭了。本来女孩替他付账这说明她对他有好感，他不思讨好反而发脾气，如此就难怪他要打光棍了。几乎没有一个女孩会对那些不尊重女性的男孩有好感，切记！切记！。&nbsp; <br><br>　　8.团结大多数。几乎每个女孩都有一两个最知心的女友，当其他道路都不通时，她们是你通往胜利的成功之路。你可以通过她们了解女孩到底对你有没有意思，还可以通过她们传达你的意思。这比你直接说要好得多。可千万别忽视这一支生力军，我不止一次地看到男孩通过这个方法大功告成的。&nbsp; <br><br>　　最后要提醒大家的是，爱情从来都没有固定的公式。别人成功的方法也许正是你的败着；别人失败的方法也许恰是你的妙着。我这里只提一点原则性的东西，切不可生搬硬套。如果因此而坏了你的好事切莫来找我。女孩要是不喜欢你，就是玉皇大帝也没有法子。还是古人说的好：不战而屈人之兵，上之上策也。 <br>
<img src ="http://www.blogjava.net/ltc603/aggbug/113015.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2007-04-23 19:12 <a href="http://www.blogjava.net/ltc603/archive/2007/04/23/113015.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是线程局部变量（thread-local variable）？轻松使用线程: 不共享有时是最好的</title><link>http://www.blogjava.net/ltc603/archive/2007/04/09/109492.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Mon, 09 Apr 2007 12:52:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2007/04/09/109492.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/109492.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2007/04/09/109492.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/109492.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/109492.html</trackback:ping><description><![CDATA[<blockquote><code><font face=新宋体>ThreadLocal</font></code> 类是悄悄地出现在 Java 平台版本 1.2 中的。虽然支持线程局部变量早就是许多线程工具（例如 Posix <code><font face=新宋体>pthreads</font></code> 工具）的一部分，但 Java Threads API 的最初设计却没有这项有用的功能。而且，最初的实现也相当低效。由于这些原因， <code><font face=新宋体>ThreadLocal</font></code> 极少受到关注，但对简化线程安全并发程序的开发来说，它却是很方便的。在 <em>轻松使用线程</em>的第 3 部分，Java 软件顾问 Brian Goetz 研究了 <code><font face=新宋体>ThreadLocal</font></code> 并提供了一些使用技巧。
<p>参加 Brian 的 <a href="http://www.ibm.com/developerworks/java_df.nsf/AllViewTemplate?OpenForm&amp;RestrictToCategory=23"><font color=#5c81a7><u>多线程 Java 编程讨论论坛</u></font></a>以获得您工程中的线程和并发问题的帮助。 </p>
</blockquote>
<p>编写线程安全类是困难的。它不但要求仔细分析在什么条件可以对变量进行读写，而且要求仔细分析其它类能如何使用某个类。 有时，要在不影响类的功能、易用性或性能的情况下使类成为线程安全的是很困难的。有些类保留从一个方法调用到下一个方法调用的状态信息，要在实践中使这样的类成为线程安全的是困难的。</p>
<p>管理非线程安全类的使用比试图使类成为线程安全的要更容易些。非线程安全类通常可以安全地在多线程程序中使用，只要您能确保一个线程所用的类的实例不被其它线程使用。例如，JDBC <code><font face=新宋体>Connection</font></code> 类是非线程安全的 — 两个线程不能在小粒度级上安全地共享一个 <code><font face=新宋体>Connection</font></code> — 但如果每个线程都有它自己的 <code><font face=新宋体>Connection</font></code> ，那么多个线程就可以同时安全地进行数据库操作。 </p>
<p>不使用 <code><font face=新宋体>ThreadLocal</font></code> 为每个线程维护一个单独的 JDBC 连接（或任何其它对象）当然是可能的；Thread API 给了我们把对象和线程联系起来所需的所有工具。而 ThreadLocal 则使我们能更容易地把线程和它的每线程（per-thread）数据成功地联系起来。 </p>
<p><a name=1><span class=atitle><font color=#000000 size=4>什么是线程局部变量（thread-local variable）？</font></span></a></p>
<p><em>线程局部变量</em>高效地为每个使用它的线程提供单独的线程局部变量值的副本。每个线程只能看到与自己相联系的值，而不知道别的线程可能正在使用或修改它们自己的副本。一些编译器（例如 Microsoft Visual C++ 编译器或 IBM XL FORTRAN 编译器）用存储类别修饰符（像 <code><font face=新宋体>static</font></code> 或 <code><font face=新宋体>volatile</font></code> ）把对线程局部变量的支持集成到了其语言中。Java 编译器对线程局部变量不提供特别的语言支持；相反地，它用 <code><font face=新宋体>ThreadLocal</font></code> 类实现这些支持， <code><font face=新宋体>核心 Thread</font></code> 类中有这个类的特别支持。 </p>
<p>因为线程局部变量是通过一个类来实现的，而不是作为 Java 语言本身的一部分，所以 Java 语言线程局部变量的使用语法比内建线程局部变量语言的使用语法要笨拙一些。要创建一个线程局部变量，请实例化类 <code><font face=新宋体>ThreadLocal</font></code> 的一个对象。 <code><font face=新宋体>ThreadLocal</font></code> 类的行为与 <code><font face=新宋体>java.lang.ref</font></code> 中的各种 <code><font face=新宋体>Reference</font></code> 类的行为很相似； <code><font face=新宋体>ThreadLocal</font></code> 类充当存储或检索一个值时的间接句柄。清单 1 显示了 <code><font face=新宋体>ThreadLocal</font></code> 接口。 </p>
<br><a name=listing1><strong><font color=#000000>清单 1. ThreadLocal 接口</font></strong></a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td>
            <pre><code class=section>
            <font face="Lucida Console">public class ThreadLocal {
            public Object get();
            public void set(Object newValue);
            public Object initialValue();
            }
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p><code><font face=新宋体>get()</font></code> 访问器检索变量的当前线程的值； <code><font face=新宋体>set()</font></code> 访问器修改当前线程的值。 <code><font face=新宋体>initialValue()</font></code> 方法是可选的，如果线程未使用过某个变量，那么您可以用这个方法来设置这个变量的初始值；它允许延迟初始化。用一个示例实现来说明 ThreadLocal 的工作方式是最好的方法。清单 2 显示了 <code><font face=新宋体>ThreadLocal</font></code> 的一个实现方式。它不是一个特别好的实现（虽然它与最初实现非常相似），所以很可能性能不佳，但它清楚地说明了 ThreadLocal 的工作方式。 </p>
<br><a name=listing2><strong><font color=#000000>清单 2. ThreadLocal 的糟糕实现</font></strong></a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td>
            <pre><code class=section>
            <font face="Lucida Console">public class ThreadLocal {
            private Map values = Collections.synchronizedMap(new HashMap());
            public Object get() {
            Thread curThread = Thread.currentThread();
            Object o = values.get(curThread);
            if (o == null &amp;&amp; !values.containsKey(curThread)) {
            o = initialValue();
            values.put(curThread, o);
            }
            return o;
            }
            public void set(Object newValue) {
            values.put(Thread.currentThread(), newValue);
            }
            public Object initialValue() {
            return null;
            }
            }
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>这个实现的性能不会很好，因为每个 <code><font face=新宋体>get()</font></code> 和 <code><font face=新宋体>set()</font></code> 操作都需要 <code><font face=新宋体>values</font></code> 映射表上的同步，而且如果多个线程同时访问同一个 <code><font face=新宋体>ThreadLocal</font></code> ，那么将发生争用。此外，这个实现也是不切实际的，因为用 <code><font face=新宋体>Thread</font></code> 对象做 <code><font face=新宋体>values</font></code> 映射表中的关键字将导致无法在线程退出后对 <code><font face=新宋体>Thread</font></code> 进行垃圾回收，而且也无法对死线程的 <code><font face=新宋体>ThreadLocal</font></code> 的特定于线程的值进行垃圾回收。 </p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 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 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 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/j-threads/index3.html#main"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=2><span class=atitle><font color=#000000 size=4>用 ThreadLocal 实现每线程 Singleton</font></span></a></p>
<p>线程局部变量常被用来描绘有状态&#8220;单子&#8221;（Singleton） 或线程安全的共享对象，或者是通过把不安全的整个变量封装进 <code><font face=新宋体>ThreadLocal</font></code> ，或者是通过把对象的特定于线程的状态封装进 <code><font face=新宋体>ThreadLocal</font></code> 。例如，在与数据库有紧密联系的应用程序中，程序的很多方法可能都需要访问数据库。在系统的每个方法中都包含一个 <code><font face=新宋体>Connection</font></code> 作为参数是不方便的 — 用&#8220;单子&#8221;来访问连接可能是一个虽然更粗糙，但却方便得多的技术。然而，多个线程不能安全地共享一个 JDBC <code><font face=新宋体>Connection</font></code> 。如清单 3 所示，通过使用&#8220;单子&#8221;中的 <code><font face=新宋体>ThreadLocal</font></code> ，我们就能让我们的程序中的任何类容易地获取每线程 <code><font face=新宋体>Connection</font></code> 的一个引用。这样，我们可以认为 <code><font face=新宋体>ThreadLocal</font></code> 允许我们创建 <em>每线程单子</em>。 </p>
<br><a name=listing3><strong><font color=#000000>清单 3. 把一个 JDBC 连接存储到一个每线程 Singleton 中</font></strong></a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td>
            <pre><code class=section>
            <font face="Lucida Console">public class ConnectionDispenser {
            private static class ThreadLocalConnection extends ThreadLocal {
            public Object initialValue() {
            return DriverManager.getConnection(ConfigurationSingleton.getDbUrl());
            }
            }
            private ThreadLocalConnection conn = new ThreadLocalConnection();
            public static Connection getConnection() {
            return (Connection) conn.get();
            }
            }
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>任何创建的花费比使用的花费相对昂贵些的有状态或非线程安全的对象，例如 JDBC <code><font face=新宋体>Connection</font></code> 或正则表达式匹配器，都是可以使用每线程单子（singleton）技术的好地方。当然，在类似这样的地方，您可以使用其它技术，例如用池，来安全地管理共享访问。然而，从可伸缩性角度看，即使是用池也存在一些潜在缺陷。因为池实现必须使用同步，以维护池数据结构的完整性，如果所有线程使用同一个池，那么在有很多线程频繁地对池进行访问的系统中，程序性能将因争用而降低。 </p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 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 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 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/j-threads/index3.html#main"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=3><span class=atitle><font color=#000000 size=4>用 ThreadLocal 简化调试日志纪录</font></span></a></p>
<p>其它适合使用 <code><font face=新宋体>ThreadLocal</font></code> 但用池却不能成为很好的替代技术的应用程序包括存储或累积每线程上下文信息以备稍后检索之用这样的应用程序。例如，假设您想创建一个用于管理多线程应用程序调试信息的工具。您可以用如清单 4 所示的 <code><font face=新宋体>DebugLogger</font></code> 类作为线程局部容器来累积调试信息。在一个工作单元的开头，您清空容器，而当一个错误出现时，您查询该容器以检索这个工作单元迄今为止生成的所有调试信息。 </p>
<br><a name=listing4><strong><font color=#000000>清单 4. 用 ThreadLocal 管理每线程调试日志</font></strong></a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td>
            <pre><code class=section>
            <font face="Lucida Console">public class DebugLogger {
            private static class ThreadLocalList extends ThreadLocal {
            public Object initialValue() {
            return new ArrayList();
            }
            public List getList() {
            return (List) super.get();
            }
            }
            private ThreadLocalList list = new ThreadLocalList();
            private static String[] stringArray = new String[0];
            public void clear() {
            list.getList().clear();
            }
            public void put(String text) {
            list.getList().add(text);
            }
            public String[] get() {
            return list.getList().toArray(stringArray);
            }
            }
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br>
<p>在您的代码中，您可以调用 <code><font face=新宋体>DebugLogger.put()</font></code> 来保存您的程序正在做什么的信息，而且，稍后如果有必要（例如发生了一个错误），您能够容易地检索与某个特定线程相关的调试信息。 与简单地把所有信息转储到一个日志文件，然后努力找出哪个日志记录来自哪个线程（还要担心线程争用日志纪录对象）相比，这种技术简便得多，也有效得多。 </p>
<p><code><font face=新宋体>ThreadLocal</font></code> 在基于 servlet 的应用程序或工作单元是一个整体请求的任何多线程应用程序服务器中也是很有用的，因为在处理请求的整个过程中将要用到单个线程。您可以通过前面讲述的每线程单子技术用 <code><font face=新宋体>ThreadLocal</font></code> 变量来存储各种每请求（per-request）上下文信息。 </p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 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 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 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/j-threads/index3.html#main"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=4><span class=atitle><font color=#000000 size=4>ThreadLocal 的线程安全性稍差的堂兄弟，InheritableThreadLocal</font></span></a></p>
<p>ThreadLocal 类有一个亲戚，InheritableThreadLocal，它以相似的方式工作，但适用于种类完全不同的应用程序。创建一个线程时如果保存了所有 <code><font face=新宋体>InheritableThreadLocal</font></code> 对象的值，那么这些值也将自动传递给子线程。如果一个子线程调用 <code><font face=新宋体>InheritableThreadLocal</font></code> 的 <code><font face=新宋体>get()</font></code> ，那么它将与它的父线程看到同一个对象。为保护线程安全性，您应该只对不可变对象（一旦创建，其状态就永远不会被改变的对象）使用 <code><font face=新宋体>InheritableThreadLocal</font></code> ，因为对象被多个线程共享。 <code><font face=新宋体>InheritableThreadLocal</font></code> 很合适用于把数据从父线程传到子线程，例如用户标识（user id）或事务标识（transaction id），但不能是有状态对象，例如 JDBC <code><font face=新宋体>Connection</font></code> 。 </p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 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 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 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/j-threads/index3.html#main"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=5><span class=atitle><font color=#000000 size=4>ThreadLocal 的性能</font></span></a></p>
<p>虽然线程局部变量早已赫赫有名并被包括 Posix <code><font face=新宋体>pthreads</font></code> 规范在内的很多线程框架支持，但最初的 Java 线程设计中却省略了它，只是在 Java 平台的版本 1.2 中才添加上去。在很多方面， <code><font face=新宋体>ThreadLocal</font></code> 仍在发展之中；在版本 1.3 中它被重写，版本 1.4 中又重写了一次，两次都专门是为了性能问题。 </p>
<p>在 JDK 1.2 中， <code><font face=新宋体>ThreadLocal</font></code> 的实现方式与清单 2 中的方式非常相似，除了用同步 <code><font face=新宋体>WeakHashMap</font></code> 代替 <code><font face=新宋体>HashMap</font></code> 来存储 values 之外。（以一些额外的性能开销为代价，使用 WeakHashMap 解决了无法对 Thread 对象进行垃圾回收的问题。）不用说， <code><font face=新宋体>ThreadLocal</font></code> 的性能是相当差的。 </p>
<p>Java 平台版本 1.3 提供的 <code><font face=新宋体>ThreadLocal</font></code> 版本已经尽量更好了；它不使用任何同步，从而不存在可伸缩性问题，而且它也不使用弱引用。相反地，人们通过给 <code><font face=新宋体>Thread</font></code> 添加一个实例变量（该变量用于保存当前线程的从线程局部变量到它的值的映射的 <code><font face=新宋体>HashMap</font></code> ）来修改 <code><font face=新宋体>Thread</font></code> 类以支持 <code><font face=新宋体>ThreadLocal</font></code> 。因为检索或设置一个线程局部变量的过程不涉及对可能被另一个线程读写的数据的读写操作，所以您可以不用任何同步就实现 <code><font face=新宋体>ThreadLocal.get()</font></code> 和 <code><font face=新宋体>set()</font></code> 。而且，因为每线程值的引用被存储在自已的 <code><font face=新宋体>Thread</font></code> 对象中，所以当对 <code><font face=新宋体>Thread</font></code> 进行垃圾回收时，也能对该 <code><font face=新宋体>Thread</font></code> 的每线程值进行垃圾回收。 </p>
<p>不幸的是，即使有了这些改进，Java 1.3 中的 <code><font face=新宋体>ThreadLocal</font></code> 的性能仍然出奇地慢。据我的粗略测量，在双处理器 Linux 系统上的 Sun 1.3 JDK 中进行 <code><font face=新宋体>ThreadLocal.get()</font></code> 操作，所耗费的时间大约是无争用同步的两倍。性能这么差的原因是 <code><font face=新宋体>Thread.currentThread()</font></code> 方法的花费非常大，占了 <code><font face=新宋体>ThreadLocal.get()</font></code> 运行时间的三分之二还多。虽然有这些缺点，JDK 1.3 <code><font face=新宋体>ThreadLocal.get()</font></code> 仍然比争用同步快得多，所以如果在任何存在严重争用的地方（可能是有非常多的线程，或者同步块被频繁地执行，或者同步块很大）， <code><font face=新宋体>ThreadLocal</font></code> 可能仍然要高效得多。 </p>
<p>在 Java 平台的最新版本，即版本 1.4b2 中， <code><font face=新宋体>ThreadLocal</font></code> 和 <code><font face=新宋体>Thread.currentThread()</font></code> 的性能都有了很大提高。有了这些提高， <code><font face=新宋体>ThreadLocal</font></code> 应该比其它技术，如用池，更快。由于它比其它技术更简单，也更不易出错，人们最终将发现它是避免线程间出现不希望的交互的有效途径。 </p>
<br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 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 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 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/j-threads/index3.html#main"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<p><a name=6><span class=atitle><font color=#000000 size=4>ThreadLocal 的好处</font></span></a></p>
<p><code><font face=新宋体>ThreadLocal</font></code> 能带来很多好处。它常常是把有状态类描绘成线程安全的，或者封装非线程安全类以使它们能够在多线程环境中安全地使用的最容易的方式。使用 <code><font face=新宋体>ThreadLocal</font></code> 使我们可以绕过为实现线程安全而对何时需要同步进行判断的复杂过程，而且因为它不需要任何同步，所以也改善了可伸缩性。除简单之外，用 <code><font face=新宋体>ThreadLocal</font></code> 存储每线程单子或每线程上下文信息在归档方面还有一个颇有价值好处 — 通过使用 <code><font face=新宋体>ThreadLocal</font></code> ，存储在 <code><font face=新宋体>ThreadLocal</font></code> 中的对象都是 <em>不</em>被线程共享的是清晰的，从而简化了判断一个类是否线程安全的工作。 </p>
<p>我希望您从这个系列中得到了乐趣，也学到了知识，我也鼓励您到我的 <a href="http://www.ibm.com/developerworks/java_df.nsf/AllViewTemplate?OpenForm&amp;RestrictToCategory=23"><font color=#5c81a7><u>讨论论坛</u></font></a>中来深入研究多线程问题。 </p>
<img src ="http://www.blogjava.net/ltc603/aggbug/109492.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2007-04-09 20:52 <a href="http://www.blogjava.net/ltc603/archive/2007/04/09/109492.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Web服务器开发环境下的线程安全问题</title><link>http://www.blogjava.net/ltc603/archive/2007/04/09/109490.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Mon, 09 Apr 2007 12:31:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2007/04/09/109490.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/109490.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2007/04/09/109490.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/109490.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/109490.html</trackback:ping><description><![CDATA[<p>Servlet是在多线程环境下的。即可能有多个请求发给一个servelt实例，每个请求是一个线程。 <br>struts下的action也类似，同样在多线程环境下。可以参考struts user guide: http://struts.apache.org/struts-action/userGuide/building_controller.html 中的Action Class Design Guidelines一节: Write code for a multi-threaded environment - Our controller servlet creates only one instance of your Action class, and uses this one instance to service all requests. Thus, you need to write thread-safe Action classes. Follow the same guidelines you would use to write thread-safe Servlets. <br>译:为多线程环境编写代码。我们的controller servlet指挥创建你的Action 类的一个实例，用此实例来服务所有的请求。因此，你必须编写线程安全的Action类。遵循与写线程安全的servlet同样的方针。 <br><br>1.什么是线程安全的代码 <br>在多线程环境下能正确执行的代码就是线程安全的。 <br>安全的意思是能正确执行，否则后果是程序执行错误，可能出现各种异常情况。</p>
<p>2.如何编写线程安全的代码 <br>很多书籍里都详细讲解了如何这方面的问题，他们主要讲解的是如何同步线程对共享资源的使用的问题。主要是对synchronized关键字的各种用法，以及锁的概念。 <br>Java1.5中也提供了如读写锁这类的工具类。这些都需要较高的技巧，而且相对难于调试。 <br><br>但是，线程同步是不得以的方法,是比较复杂的,而且会带来性能的损失。等效的代码中，不需要同步在编写容易度和性能上会更好些。 <br>我这里强调的是什么代码是始终为线程安全的、是不需要同步的。如下: <br>1)常量始终是线程安全的，因为只存在读操作。 <br>2)对构造器的访问(new 操作)是线程安全的，因为每次都新建一个实例，不会访问共享的资源。 <br>3)最重要的是:局部变量是线程安全的。因为每执行一个方法，都会在独立的空间创建局部变量，它不是共享的资源。局部变量包括方法的参数变量。 <br>struts user guide里有： <br>Only Use Local Variables - The most important principle that aids in thread-safe coding is to use only local variables, not instance variables , in your Action class. <br>译:只使用用局部变量。--编写线程安全的代码最重要的原则就是，在Action类中只使用局部变量，不使用实例变量。</p>
<p><br>总结： <br>在Java的Web服务器环境下开发，要注意线程安全的问题。最简单的实现方式就是在Servlet和Struts Action里不要使用类变量、实例变量，但可以使用类常量和实例常量。 <br>如果有这些变量，可以将它们转换为方法的参数传入，以消除它们。 <br>注意一个容易混淆的地方：被Servlet或Action调用的类中(如值对象、领域模型类)中是否可以安全的使用实例变量？如果你在每次方法调用时 <br>新建一个对象，再调用它们的方法，则不存在同步问题---因为它们不是多个线程共享的资源，只有共享的资源才需要同步---而Servlet和Action的实例对于多个线程是共享的。 <br>换句话说，Servlet和Action的实例会被多个线程同时调用，而过了这一层,如果在你自己的代码中没有另外启动线程，且每次调用后续业务对象时都是先新建一个实例再调用，则都是线程安全的。 </p>
<img src ="http://www.blogjava.net/ltc603/aggbug/109490.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2007-04-09 20:31 <a href="http://www.blogjava.net/ltc603/archive/2007/04/09/109490.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ActionServlet深入探讨</title><link>http://www.blogjava.net/ltc603/archive/2007/04/06/108859.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Fri, 06 Apr 2007 01:48:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2007/04/06/108859.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/108859.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2007/04/06/108859.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/108859.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/108859.html</trackback:ping><description><![CDATA[<div>java.lang.Object<br>&nbsp;&nbsp;|<br>&nbsp;&nbsp;+--javax.servlet.GenericServlet<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--javax.servlet.http.HttpServlet<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+--org.apache.struts.action.ActionServlet<br><br>Struts提供了一个缺省版本的ActionServlet类，你可以继承这个类，覆盖其中的一些方法来达到你的特殊处理的需要。ActionServlet继承与javax.servlet.http.HttpServlet，所以在本质上它和一个普通的servlet没有区别，你完全可以把它当做一个servlet来看待，只是在其中完成的功能不同罢了。ActionServlet主要完成如下功能：<br><br>将一个来自客户端的URI映射到一个相应的Action类<br>
<ul>
    <li>如果是这个Action类是第一次被调用，那么实例化一个并放入缓存<br>
    <li>如果在配置文件(struts-config.xml)中指定了相应的ActionForm，那么从Request中抓取数据填充FormBean<br>
    <li>调用这个Action类的perform()方法，传入ActionMapping的一个引用，对应的ActionForm、以及由容器传给ActionServlet的HttpServletRequest、HttpServletResponse对象。</li>
</ul>
<br><br>确省版本的ActionServlet会从配置文件web.xml中读取如下初始化参数：<br>
<ul>
    <li>application<br>应用使用的资源包(resources&nbsp;bundle)的基类<br>
    <li>factory<br>用于创建应用的MessageResources对象的MessageResourcesFactory的类名。确省是org.apache.struts.util.PropertyMessageResourcesFactory。<br>
    <li>config<br>Struts的配置文件，确省是/WEB-INF/struts-config.xml。注意这儿是与应用Context关联的相对路径。<br>
    <li>content<br>定义了确省的内容类型和编码格式，它会被自动地被设置到每个response中，如果JSP/Servlet中没有明确的设置。确省是text/html。<br>
    <li>debug<br>调试信息的级别。默认为0，比当前级别高的调试信息会被log到日志文件中。<br>
    <li>detail<br>与debug的作用类似，只是这个detail是initMapping()时专用的。调试信息会被打印到System.out，而不是日志文件。<br>
    <li>formBean<br>ActionFormBean的实现类，确省为org.apache.struts.action.ActionFormBean<br>
    <li>forward<br>应用中使用的ActionForward类，确省是org.apache.struts.action.ActionForward。<br>
    <li>locale<br>指定了确省使用的Locale对象。设为true，当得到一个session时，会自动在session中存储一个以Action.LOCALE_KEY标示的Locale对象，如果session中还没有与Action.LOCALE_KEY绑定的Locale对象。<br>
    <li>mapping<br>应用中使用的ActionMapping类，确省是org.apache.struts.action.ActionMapping。<br>
    <li>multipartClass<br>文件上传使用的MutipartRequestHandler的实现类。确省为org.apache.struts.upload.DiskMultipartRequestHandler<br>
    <li>nocache<br>如果设为true，那么ActionServlet会自动在每个到客户端的响应中添加nocache的HTML头，这样客户端就不会对应用中的页面进行缓存。确省为false<br>
    <li>null<br>如果设置为true，那么应用在得到一个未定义的message资源时，会返回null，而不是返回一个错误信息。确省是true。<br>
    <li>maxFileSize<br>文件上传的大小上限，确省为250M<br>
    <li>bufferSize<br>文件上传时的缓冲区的大小，确省为4M<br>
    <li>tempDir<br>设置用于上传时的临时目录。工作目录会作为一个Servlet环境（Context）的属性提供。<br>
    <li>validate<br>Are&nbsp;we&nbsp;using&nbsp;the&nbsp;new&nbsp;configuration&nbsp;file&nbsp;format?确省为true。<br>
    <li>validating</li>
</ul>
<br>在解析配置XML文件是是否进行有效性的验证。确省为true<br><br>ActionServlet中应用了命令设计模式。<br><br>一个Servlet在由容器生成时，首先会调用init()方法进行初始化，在接到一个HTTP请求时，调用相应的方法进行处理；比如GET请求调用doGet()方法，POST请求调用doPost()方法。所以首先看看ActionServlet的init()方法，你就会很清楚为什么ActionServlet可以完成这些功能了。<br>
<h3>init()</h3>
<br>在它的init()方法中，ActionServlet依次调用如下protected的方法完成初始化：<br>
<ul>
    <li>initActions()&nbsp;－&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;大家可能还曾有这个疑问：Struts为什么可以找到一个请求URI对应的action类呢？答案就在这儿，ActionServlet有一个actions属性，类型为org.apache.struts.util.FastHashMap，用于存储以类的全名为key的已实例化的Action类。在init()时首先调用的就是initActions()方法，在这个方法中只是简单的清除map中的所有的名值对，<br>
    <div class=codeStyle>
    <ol>
        <li>
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong><font color=#0000ff>synchronized</font></strong>&nbsp;(actions)&nbsp;{
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;actions.setFast(<strong><font color=#0000ff>false</font></strong>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;actions.clear();
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;actions.setFast(<strong><font color=#0000ff>true</font></strong>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </li>
    </ol>
    </div>
    <br>首先把actions设为slow模式，这时对FastHashMap的访问是线程同步的，然后清除actions中的所有的已存在的名/值对，最后再把actions的模式设为fast。由于FastHashMap是struts在java.util.HashMap的基础上的一个扩展类，是为了适应多线程、并且对HashMap的访问大部分是只读的特殊环境的需要。大家知道java.util.HashMap是非线程安全的，所以HashMap一般适用于单线程环境下。org.apache.struts.FastHashMap就是继承于java.util.HashMap，在其中添加多线程的支持产生的。在fast模式下的工作方式是这样的：读取是非线程同步的；写入时首先克隆当前map，然后在这个克隆上做写入操做，完成后用这个修改后的克隆版本替换原来的map。那么在什么时候会把Actions类添加到这个map中呢？我们已经提到了struts是动态的生成Action类的实例的，在每次ActionServlet接收到一个GET或POST的HTTP请求时，会在这个map中查找对应的Action类的实例，如果不存在，那么就实例化一个，并放入map中。可见这个actions属性起到了对Action类实例的缓存的作用。<br>
    <li>initInternal()&nbsp;－&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;初始化ActionServlet内部使用的资源包MessageResources，使用MessageResources.getMessageResources(internalName)得到&nbsp;&nbsp;&nbsp;&nbsp;internalName为"org.apache.struts.action.ActionResources"对应的ActionResources.properties文件。这个资源包主要用于ActionServlet处理过程中的用到的提示信息，这儿不展开讨论。<br>
    <li>initDebug()&nbsp;－&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从web.xml中读取本应用的debug级别参数getServletConfig().getInitParameter("debug")，然后赋给debug属性。<br>
    <li>initApplication()－&nbsp;&nbsp;&nbsp;&nbsp;初始化应用资源包，并放置入ServletContext中。<br>
    <div class=codeStyle>
    <ol>
        <li>
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong><a href="http://www.javaresearch.org/source/jdk142/java/lang/String.java.html" target=_blank><font class=classLink color=#0000ff><u>String</u></font></a></strong>&nbsp;factory&nbsp;=getServletConfig().getInitParameter(&#8220;factory&#8221;);&nbsp;&nbsp;&nbsp;&nbsp;
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong><a href="http://www.javaresearch.org/source/jdk142/java/lang/String.java.html" target=_blank><font class=classLink color=#0000ff><u>String</u></font></a></strong>&nbsp;oldFacory&nbsp;=&nbsp;MessageResourcesFactory.getFactoryClass();
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong><font color=#0000ff>if</font></strong>&nbsp;(factory&nbsp;!=<strong><font color=#0000ff>null</font></strong>)
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageResourcesFactory.setFactoryClass(factory);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong><a href="http://www.javaresearch.org/source/jdk142/java/lang/String.java.html" target=_blank><font class=classLink color=#0000ff><u>String</u></font></a></strong>&nbsp;value&nbsp;=&nbsp;getServletConfig().getInitParameter(<font color=#ff33ff>"application"</font>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageResourcesFactory&nbsp;factoryObject&nbsp;=
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageResourcesFactory.createFactory();
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;application&nbsp;=&nbsp;factoryObject.createResources(value);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageResourcesFactory.setFactory(oldFactory);&nbsp;
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getServletContext().setAttribute(<font color=#ff0000>Action</font>.MESSAGES_KEY,&nbsp;application); </li>
    </ol>
    </div>
    <br>说明：文中引用的代码片断可能会省略了一些例外检查等非主线的内容，敬请注意。<br><br>首先从配置文件中读取factory参数，如果这个参数不为空，那么就在MessageResourcesFactory中使用这个指定的Factory类；否则，使用默认的工厂类org.apche.struts.util.PropertyMessageResourceFactory。然后调用MessageResourcesFactory的静态createFactory()方法，生成一个具体的MessageResourceFactory对象（注意：MessageResourcesFactory是抽象类）。这样就可以调用这个具体的MessageResourceFactory的createResource()方法得到配置文件(web.xml)中定义的资源文件了。<br>上面的application对象类型为MessageResources。在web.xml中在配置ActionServlet时可以指定一个特定的工厂类。不能直接MessageResourcesFactory的createResources()方法，因为这个方法是abstract的。创建factoryObject的过程如下：<br>
    <div class=codeStyle>
    <ol>
        <li>&nbsp;&nbsp;&nbsp;&nbsp;
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageResourceFactory&nbsp;factoryObject=
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageResourcesFactory.createFactory();
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;application&nbsp;=&nbsp;factoryObject.createResources(value); </li>
    </ol>
    </div>
    <br>&lt;li&gt;initMapping()&nbsp;－&nbsp;&nbsp;&nbsp;&nbsp;为应用初始化mapping信息ActionServlet有一个protected的属性：mapping，封装了一个ActionMapping的对象集合，以便于管理、查找ActionMapping。mappings是org.apache.struts.action.ActionMappings类的实例。主要有两个方法：addMapping(ActionMapping&nbsp;mapping)和findMapping(String&nbsp;path)。ActionMapping也是使用上面提到的org.apache.struts.util.FastHashMap类来存储所有的ActionMapping对象。<br>
    <div class=codeStyle>
    <ol>
        <li>
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mappings.setServlet(<strong><font color=#0000ff>this</font></strong>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;&#8230;
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em><font color=#339900>//&nbsp;Initialize&nbsp;the&nbsp;name&nbsp;of&nbsp;our&nbsp;ActionFormBean&nbsp;implementation&nbsp;class</font></em>
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value&nbsp;=&nbsp;getServletConfig().getInitParameter(<font color=#ff33ff>"formBean"</font>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong><font color=#0000ff>if</font></strong>&nbsp;(value&nbsp;!=&nbsp;<strong><font color=#0000ff>null</font></strong>)
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;formBeanClass&nbsp;=&nbsp;value;
        <li>&nbsp;&nbsp;&nbsp;&nbsp;
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em><font color=#339900>//&nbsp;Initialize&nbsp;the&nbsp;name&nbsp;of&nbsp;our&nbsp;ActionForward&nbsp;implementation&nbsp;class</font></em>
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value&nbsp;=&nbsp;getServletConfig().getInitParameter(<font color=#ff33ff>"forward"</font>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong><font color=#0000ff>if</font></strong>&nbsp;(value&nbsp;!=&nbsp;<strong><font color=#0000ff>null</font></strong>)
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;forwardClass&nbsp;=&nbsp;value;
        <li>
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em><font color=#339900>//&nbsp;Initialize&nbsp;the&nbsp;name&nbsp;of&nbsp;our&nbsp;ActionMapping&nbsp;implementation&nbsp;class</font></em>
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value&nbsp;=&nbsp;getServletConfig().getInitParameter(<font color=#ff33ff>"mapping"</font>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong><font color=#0000ff>if</font></strong>&nbsp;(value&nbsp;!=&nbsp;<strong><font color=#0000ff>null</font></strong>)
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mappingClass&nbsp;=&nbsp;value; </li>
    </ol>
    </div>
    <br>在initMapping()中，首先链接mappings对象到本servlet实例。其实这句话的作用很简单，在ActionMappings中会有一个ActionServlet类型的属性，这个属性就界定了这个ActionMappings对象所属的ActionServlet。Struts的实现比较灵活，其中的ActionFormBean、ActionForward、ActionMapping类你完全可以使用自己实现的子类，来定制Struts的工作方式。上面的代码就从配置文件（web.xml）中读取formBean、forward、mapping参数，这些参数就是你定制的ActionFormBean、ActionForward、ActionMapping类名。<br>
    <div class=codeStyle>
    <ol>
        <li>
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em><font color=#339900>//&nbsp;Initialize&nbsp;the&nbsp;context-relative&nbsp;path&nbsp;to&nbsp;our&nbsp;configuration&nbsp;resources</font></em>
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value&nbsp;=&nbsp;getServletConfig().getInitParameter(<font color=#ff33ff>"config"</font>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong><font color=#0000ff>if</font></strong>&nbsp;(value&nbsp;!=&nbsp;<strong><font color=#0000ff>null</font></strong>)
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;config&nbsp;=&nbsp;value;
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em><font color=#339900>//&nbsp;Acquire&nbsp;an&nbsp;input&nbsp;stream&nbsp;to&nbsp;our&nbsp;configuration&nbsp;resource</font></em>
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#ff0000>InputStream</font>&nbsp;input&nbsp;=&nbsp;getServletContext().getResourceAsStream(config);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Digester&nbsp;digester&nbsp;=&nbsp;<strong><font color=#0000ff>null</font></strong>;
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digester&nbsp;=&nbsp;initDigester(detail);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong><font color=#0000ff>try</font></strong>&nbsp;{
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;formBeans.setFast(<strong><font color=#0000ff>false</font></strong>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;forwards.setFast(<strong><font color=#0000ff>false</font></strong>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mappings.setFast(<strong><font color=#0000ff>false</font></strong>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digester.parse(input);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mappings.setFast(<strong><font color=#0000ff>true</font></strong>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;forwards.setFast(<strong><font color=#0000ff>true</font></strong>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;formBeans.setFast(<strong><font color=#0000ff>true</font></strong>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<strong><font color=#0000ff>catch</font></strong>&nbsp;(<font color=#ff0000>SAXException</font>&nbsp;e)&nbsp;{
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong><font color=#0000ff>throw</font></strong>&nbsp;<strong><font color=#0000ff>new</font></strong>&nbsp;ServletException
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(internal.getMessage(<font color=#ff33ff>"configParse"</font>,&nbsp;config),&nbsp;e);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<strong><font color=#0000ff>finally</font></strong>&nbsp;{
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;input.close();
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </li>
    </ol>
    </div>
    <br>从web.xml读取Struts的配置文件的位置。使用org.apache.struts.digester.Digester解析config参数标示的配置文件，通常为&#8220;/WEB-INF/struts-config.xml&#8221;，解析出所有的data-source、form-bean、action-mapping、forward。从上面的程序片断看到，Digester仅仅调用了一个parse()方法，那么，Digester是怎样把解析struts-config.xml文件并把解析的结果form-bean等信息存储到属性变量formBeans等中的呢？你可以注意到在调用digester.parse(InputStream)之前，首先调用了initDigester()方法：<br>
    <div class=codeStyle>
    <ol>
        <li>
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Digester&nbsp;digester&nbsp;=&nbsp;<strong><font color=#0000ff>new</font></strong>&nbsp;Digester();
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digester.push(<strong><font color=#0000ff>this</font></strong>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digester.addObjectCreate(<font color=#ff33ff>"struts-config/action-mappings/action"</font>,
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mappingClass,&nbsp;<font color=#ff33ff>"className"</font>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digester.addSetProperties(<font color=#ff33ff>"struts-config/action-mappings/action"</font>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digester.addSetNext(<font color=#ff33ff>"struts-config/action-mappings/action"</font>,
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#ff33ff>"addMapping"</font>,
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#ff33ff>"org.apache.struts.action.ActionMapping"</font>);
        <li>
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digester.addSetProperty
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(<font color=#ff33ff>"struts-config/action-mappings/action/set-property"</font>,
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#ff33ff>"property"</font>,&nbsp;<font color=#ff33ff>"value"</font>); </li>
    </ol>
    </div>
    <br>在这个方法中首先生成一个Digester对象，然后设置解析的规则和回调，如果你对XML、SAX不是很熟，这儿不必纠缠太深。要注意的是addSetNext()方法，设置了每一个要解析元素的Set&nbsp;Next回调方法，而这个方法就是由digester解析器的父提供的。上面的片断中的&#8220;addMapping&#8221;就是ActionServlet本身定义的一个方法，将由Digester回调。Digester就是籍此把解析出的每一个FormBean、ActionForward、ActionMapping等存储到属性变量formBeans、forwards、mappings等中的。<br>
    <li>initUpload()&nbsp;－&nbsp;&nbsp;&nbsp;&nbsp;初始化有关Upload的一些参数，比如：bufferSize、tempDir。<br>
    <li>initDataSource()&nbsp;－取出在initMapping()中从配置文件中读取的每一个DataSource，设置LogWriter，如果为GenericDataSource的实例，则打开数据源。然后，把每个dataSource放入Context中。<br>dataSource.setLogWriter(scw)；<br>((GenericDataSource)dataSource).open();<br>getServletContext().setAttribute(key,dataSource);<br>
    <li>initOther()&nbsp;－&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;设置其它尚未初始化的的参数（content、locale、nocache），并发布formBeans、forwards、mappings到Context：<br>getServletContext().setAttribute(Action.FORM_BEANS_KEY,&nbsp;formBeans);<br>getServletContext().setAttribute(Action.FORWARDS_KEY,&nbsp;forwards);<br>getServletContext().setAttribute(Action.MAPPINGS_KEY,&nbsp;mappings);<br>
    <li>initServlet()&nbsp;－&nbsp;&nbsp;&nbsp;&nbsp;初始化Controller&nbsp;Servlet的Servlet&nbsp;Mapping。这儿也使用了Digester工具，扫描web.xml所有的&lt;web-app/servlet-mapping&gt;，寻找servlet-name与当前Servlet相同的mapping，置入Context。代码如下；<br>
    <div class=codeStyle>
    <ol>
        <li>
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Digester&nbsp;digester&nbsp;=&nbsp;<strong><font color=#0000ff>new</font></strong>&nbsp;Digester();
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digester.push(<strong><font color=#0000ff>this</font></strong>);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digester.setDebug(debug);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digester.setValidating(validating);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digester.addCallMethod(&#8220;web-appservlet-mapping&#8221;,&#8220;addServletMapping&#8221;,&nbsp;2);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digester.addCallParm(&#8220;web-appservlet-mappingservlet-name&#8221;,&nbsp;0);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digester.addCallParm(&#8220;web-appservlet-mappingurl-pattern&#8221;,&nbsp;1);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#ff0000>InputStream</font>&nbsp;is&nbsp;=&nbsp;getServletContext().getResourceAsStream(&#8220;/WEB-INFweb.xml&#8221;);
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;digester.parse(is);&nbsp;
        <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getServletContext().setAttribute(<font color=#ff0000>Action</font>.SERVLET_KEY,servletMapping); </li>
    </ol>
    </div>
    </li>
</ul>
</div>
<img src ="http://www.blogjava.net/ltc603/aggbug/108859.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2007-04-06 09:48 <a href="http://www.blogjava.net/ltc603/archive/2007/04/06/108859.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CSS全攻略－转</title><link>http://www.blogjava.net/ltc603/archive/2006/10/25/77265.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Wed, 25 Oct 2006 11:11:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/10/25/77265.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/77265.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/10/25/77265.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/77265.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/77265.html</trackback:ping><description><![CDATA[
		<div style="MARGIN-TOP: 5px; MARGIN-LEFT: 15px">
				<b>关键字:</b>   转贴     </div>
		<div style="MARGIN-TOP: 10px; MARGIN-LEFT: 15px">
				<table width="100%">
						<tbody>
								<tr>
										<td>
												<p>CSS全称Cascading Style Sheet。层叠式样式表。从三年前就开始使用CSS了，但一直以来都小看了它。CSS的出现其实是一次革命，它试图将网站的内容与表现分开。 <br />一、CSS的四种实现方式： <br />1.内嵌式：</p>
												<p>2.外链式：</p>
												<p>3.导入式</p>
												<p>3.属性式： <br />二.CSS的定义： <br />选择对象{属性1:值1;属性2:值2;属性3:值3;属性n:值n……} <br />如： <br />td{font-size:12px;color:#FFFF00} <br />.myname{font-size:12px;color:#FFFF00} <br />a:hover{font-size:12px;color:#FFFF00;text-decoration: underline;} <br />三.四种选择对象 <br />1.HTML selector (TagName) <br />2.class selector (.NAME) <br />3.ID selector (#IDname) <br />4.特殊对象 (a:hover　a:link　a:visited　a:active) <br />1.HTML selector <br />HTML selector就是HTML的置标符，如：DIV、TD、H1。HTML selector的作用范围是应用了该样式的所有页面中的所有该置标符。 <br />例： <br />td <br />{ color: #FF0000; <br />} <br />--&gt; <br />注意：在中没有应用什么，其中文字自动变红色。 <br />2.class selector <br />定义class selector需要往其名称其加一个点“.”。如“.classname”。class selector的作用范围是所有包含“class="classname"”的置标符。 <br />例： <br />.fontRed <br />{ color: #FF0000; <br />} --&gt; <br />注意：在第二个中没有“class="fontRed"”，所以文字没有变红色。 <br />3.ID selector <br />定义ID selector需要往其名称其加一个点“#”。如“#IDname”。ID selector的作用范围是所有包含“ID="classname"”的置标符。 <br />例：</p>
												<p>#fontRed <br />{ color: #FF0000; <br />} --&gt; <br />注意：在第二个中没有“ID="fontRed"”，所以文字没有变红色。 <br />4.特殊对象 <br />特殊对象包括四种，是针对链接对象设置的: <br />a:hover 鼠标移上时的超链接 <br />a:link 常规，非访问超链接 <br />a:visited 访问过的超链接 <br />a:active 鼠标点击时的超链接 <br />特殊对象的作用范围是所有置标符（这句话有待商榷，因为下面很快就有一种方法可以把“所有”两个字推翻）。 <br />例： <br />a:hover <br />{ color: #0000FF; <br />text-decoration: underline; <br />} --&gt; <br />注意下面，很有用！！！ <br />a.classname:hover <br />a#IDname:hover <br />这两种写法，是分别配合.classname与#IDname使用的。它的作用范围变成了所有包含“class="classname"”或“ID="IDname"”的置标符。这种写法，可以帮助你在同一页面中实现多种a:hover效果，可以看一下艺网的主页上导航栏文字与普通文章标题在鼠标时的区别。 <br />四.应用： <br />1.置标符　自动应用 <br />2.特制类　class="NAME" <br />3.ID ID="IDname" <br />4.特殊对象　自动应用 <br />五.CSS属性 <br />CSS的属性有很多，像上文中用到最多的color，表示文字的颜色。background-color表示背景色。这个是最主要的，但是因为没有什么难度，参考一下相关手册就可以了。</p>
												<p>注明两点 <br />第一点：css的方式现在一般都采用外部连接，这个用起来方便，编译起来也方便 <br />第二点：我用的是frontpage2003，很多css的参数都会自动提示，似乎好像以前我没有用到过~~dw有的</p>
												<p>CSS 标签属性/属性参考 <br />这里列出了目前支持的样式表(CSS)标签属性。标有星号(*)的标签属性可于 Microsoft® Internet Explorer 5 中使用。标有两个星号(**)的标签属性可于 Internet Explorer 5.5 中使用。带有两个加号(++)的标签属性可于 Internet Explorer 6 中使用。如果某个标签属性或属性已经提交给万维网协会(W3C)但尚未作为标准发布，则标有“已提交”。若某个标签属性或属性既未提交给 W3C 也不是标准，将标有“扩展”。</p>
												<p>CSS 标签属性/属性</p>
												<p>行为属性 behavior</p>
												<p>字体和文本属性 direction* </p>
												<p>direction <br />font <br />font-family <br />font-size <br />font-style <br />font-variant <br />font-weight <br />ime-mode <br />layout-grid <br />layout-grid-char <br />layout-grid-line <br />layout-grid-mode <br />layout-grid-type <br />letter-spacing <br />line-break <br />line-height <br />min-height ++ <br />ruby-align <br />ruby-overhang <br />ruby-position <br />text-align <br />text-autospace <br />text-decoration <br />text-indent <br />text-justify <br />text-kashida-space <br />text-overflow ++ <br />text-transform <br />text-underline-position <br />unicode-bidi <br />vertical-align <br />white-space** <br />word-break <br />word-spacing ++(于 Macintosh 版本 4.0 中可用) <br />word-wrap <br />writing-mode</p>
												<p>颜色和背景属性 background-attachment <br />background-color <br />background-image <br />background-position <br />background-position-x <br />background-position-y <br />background-repeat <br />color </p>
												<p>版面属性 border <br />border-bottom <br />border-bottom-color <br />border-bottom-style <br />border-bottom-width <br />border-collapse* <br />border-color <br />border-left <br />border-left-color <br />border-left-style <br />border-left-width <br />border-right <br />border-right-color <br />border-right-style <br />border-right-width <br />border-style <br />border-top <br />border-top-color <br />border-top-style <br />border-top-width <br />border-width <br />clear <br />float <br />layout-flow <br />margin <br />margin-bottom <br />margin-left <br />margin-right <br />margin-top <br />padding <br />padding-bottom <br />padding-left <br />padding-right <br />padding-top <br />scrollbar-3dlight-color <br />scrollbar-arrow-color <br />scrollbar-base-color <br />scrollbar-darkshadow-color <br />scrollbar-face-color <br />scrollbar-highlight-color <br />scrollbar-shadow-color <br />table-layout* <br />zoom</p>
												<p>分类属性 display </p>
												<p>list-style <br />list-style-image <br />list-style-position <br />list-style-type </p>
												<p>定位属性 bottom* </p>
												<p>clip <br />height <br />left <br />overflow <br />overflow-x <br />overflow-y <br />position <br />right* <br />top <br />visibility <br />width <br />z-index </p>
												<p>打印属性 page** </p>
												<p>pageBreakAfter <br />pageBreakBefore</p>
												<p>滤镜属性 filter</p>
												<p>伪类和其它属性 :active </p>
												<p>@charset <br />cursor <br />:first-letter** <br />:first-line** <br />@font-face <br />:hover <br />@import <br />!important <br />:link <br />@media* <br />@page** <br />:visited <br />有关表格边框的css语法整理</p>
												<p>我们知道Dreamweaver在表格制作方面做得非常出色，但是在某些时候还是必须结合css才能达到一些特定效果，下面我们先把有关表格边框的css语法整理出来，然后另外介绍怎样用css美化表格的边框。</p>
												<p>　　有关表格边框的css语法</p>
												<p>　　具体内容包括：上边框宽度、右边框宽度、下边框宽度、左边框宽度、边框宽度、边框颜色、边框样式、上边框、下边框、左边框、右边框、边框、宽度、高度、有关标签等。</p>
												<p>　　 1.上边框宽度</p>
												<p>　　语法: border-top-width: &lt;值&gt;</p>
												<p>　　允许值: thin | medium | thick | &lt;长度&gt;</p>
												<p>　　初始值: medium</p>
												<p>　　适用于: 所有对象</p>
												<p>　　向下兼容: 否</p>
												<p>　　上边框宽度属性用于指定一个元素上边框的宽度。值可以是三个关键字其中的一个，都不受字体大小或长度的影响，可以用于实现成比例的宽度。不允许使用负值。也可以用在上边框、边框的宽度或边框的属性略写。</p>
												<p>　　2.右边框宽度</p>
												<p>　　语法: border-right-width: &lt;值&gt;</p>
												<p>　　允许值: thin | medium | thick | &lt;长度&gt;</p>
												<p>　　初始值: medium</p>
												<p>　　适用于: 所有对象</p>
												<p>　　向下兼容: 否</p>
												<p>　　右边框宽度属性用于指定元素的右边框的宽度。值可以是三个关键字其中的一个，都不受字体大小或长度的影响，可以用于实现成比例的宽度。不允许使用负值。也可以用在右边框、边框的宽度或边框的属性略写。</p>
												<p>　　 3.下边框宽度</p>
												<p>　　语法: border-bottom-width: &lt;值&gt;</p>
												<p>　　允许值: thin | medium | thick | &lt;长度&gt;</p>
												<p>　　初始值: medium</p>
												<p>　　适用于: 所有对象</p>
												<p>　　向下兼容: 否</p>
												<p>　　下边框宽度属性用于指定元素的下边框的宽度。值可以是三个关键字其中的一个，都不受字体大小或长度的影响，可以用于实现成比例的宽度。不允许使用负值。也可以用在下边框、边框的宽度或边框的属性略写。</p>
												<p>　　 4.左边框宽度</p>
												<p>　　语法: border-left-width: &lt;值&gt;</p>
												<p>　　允许值: thin | medium | thick | &lt;长度&gt;</p>
												<p>　　初始值: medium</p>
												<p>　　适用于: 所有对象</p>
												<p>　　向下兼容: 否</p>
												<p>　　左边框宽度属性用于指定元素的左边框的宽度。值可以是三个关键字其中的一个，都不受字体大小或长度的影响，可以用于实现成比例的宽度。不允许使用负值。也可以用在左边框、边框的宽度或边框的属性略写。</p>
												<p>　　5.边框宽度</p>
												<p>　　语法: border-width: &lt;值&gt;</p>
												<p>　　允许值: [ thin | medium | thick | &lt;长度&gt; ]{1,4}</p>
												<p>　　初始值: 未定义</p>
												<p>　　适用于: 所有对象</p>
												<p>　　向下兼容: 否</p>
												<p>　　边框宽度属性用一到四个值来设置元素的边界，值是一个关键字或长度。不允许使用负值长度。如果四个值都给出了，它们分别应用于上、右、下和左边框的式样。如果给出一个值，它将被运用到各边上。如果两个或三个值给出了，省略了的值与对边相等。 这个属性是上边框宽度、右边框宽度、下边框宽度和左边框宽度属性的略写。也可以使用略写的边框属性。</p>
												<p>　　6.边框颜色</p>
												<p>　　语法: border-color: &lt;值&gt;</p>
												<p>　　允许值: &lt;颜色&gt;{1,4}</p>
												<p>　　初始值: 颜色属性的值</p>
												<p>　　适用于: 所有对象</p>
												<p>　　向下兼容: 否</p>
												<p>　　边框颜色属性设置一个元素的边框颜色。可以使用一到四个关键字。如果四个值都给出了，它们分别应用于上、右、下和左边框的式样。如果给出一个值，它将被运用到各边上。如果两个或三个值给出了，省略了的值与对边相等。也可以使用略写的边框属性。</p>
												<p>　　7.边框样式</p>
												<p>　　语法: border-style: &lt;值&gt;</p>
												<p>　　允许值: [ none | dotted | dashed | solid | double | groove | ridge | inset | outset ]{1,4}</p>
												<p>　　初始值: none</p>
												<p>　　适用于: 所有对象</p>
												<p>　　向下兼容: 否</p>
												<p>　　边框样式属性用于设置一个元素边框的样式。这个属性必须用于指定可见的边框。可以使用一到四个关键字。如果四个值都给出了，它们分别应用于上、右、下和左边框的式样。如果给出一个值，它将被运用到各边上。如果两个或三个值给出了，省略了的值与对边相等。也可以使用略写的边框属性。</p>
												<p>　　none：无样式； <br />　　dotted：点线； <br />　　dashed：虚线； <br />　　solid：实线； <br />　　double：双线； <br />　　groove：槽线； <br />　　ridge：脊线； <br />　　inset：内凹； <br />　　outset：外凸。 <br />8.上边框</p>
												<p>　　语法: border-top: &lt;值&gt;</p>
												<p>　　允许值: &lt;上边框宽度&gt; || &lt;边框式样&gt; || &lt;颜色&gt;</p>
												<p>　　初始值: 未定义</p>
												<p>　　适用于: 所有对象</p>
												<p>　　向下兼容: 否</p>
												<p>　　上边框属性是一个用于设置一个元素上边框的宽度、式样和颜色的略写。注意只能给出一个边框式样。也可以使用略写的边框属性。</p>
												<p>　　9.右边框</p>
												<p>　　语法: border-right: &lt;值&gt;</p>
												<p>　　允许值: &lt;右边框宽度&gt; || &lt;边框式样&gt; || &lt;颜色&gt;</p>
												<p>　　初始值: 未定义</p>
												<p>　　适用于: 所有对象</p>
												<p>　　向下兼容: 否</p>
												<p>　　右边框属性是一个用于设置一个元素右边框的宽度、式样、和颜色的略写。注意只能给出一个边框式样。也可以使用略写的边框属性。　　 </p>
												<p>　　10.下边框</p>
												<p>　　语法: border-bottom: &lt;值&gt;</p>
												<p>　　允许值: &lt;下边框宽度&gt; || &lt;边框式样&gt; || &lt;颜色&gt;</p>
												<p>　　初始值: 未定义</p>
												<p>　　适用于: 所有对象</p>
												<p>　　向下兼容: 否</p>
												<p>　　下边框属性是一个用于设置一个元素的下边框的宽度、式样和颜色的略写。注意只能给出一个边框式样。也可以使用略写的边框属性。</p>
												<p>　　11.左边框</p>
												<p>　　语法: border-left: &lt;值&gt;</p>
												<p>　　允许值: &lt;左边框宽度&gt; || &lt;边框式样&gt; || &lt;颜色&gt;</p>
												<p>　　初始值: 未定义</p>
												<p>　　适用于: 所有对象</p>
												<p>　　向下兼容: 否</p>
												<p>　　左边框属性是一个用于设置一个元素左边框的宽度、式样和颜色的略写。注意只能给出一个边框式样。也可以使用略写的边框属性。</p>
												<p>　　 12.边框</p>
												<p>　　语法: border: &lt;值&gt;</p>
												<p>　　允许值: &lt;边框宽度&gt; || &lt;边框式样&gt; || &lt;颜色&gt;</p>
												<p>　　初始值: 未定义</p>
												<p>　　适用于: 所有对象</p>
												<p>　　向下兼容: 否</p>
												<p>　　边框属性是一个用于设置一个元素边框的宽度、式样和颜色的略写。</p>
												<p>　　边框声明的例子包括：</p>
												<p>　　H2 { border: groove 3em } <br />　　A:link { border: solid blue } <br />　　A:visited { border: thin dotted #800080 } <br />　　A:active { border: thick double red }</p>
												<p>　　边框属性只能设置四种边框；只能给出一组边框的宽度和式样。为了给出一个元素的四种边框的不同的值，网页制作者必须用一个或更多的属性，如：上边框、右边框、下边框、左边框、边框颜色、边框宽度、边框式样、上边框宽度、右边框宽度、下边框宽度或左边框宽度。 </p>
												<p>　　13.宽度</p>
												<p>　　语法: width: &lt;值&gt;</p>
												<p>　　允许值: &lt;长度&gt; | &lt;百分比&gt; | auto</p>
												<p>　　初始值: auto</p>
												<p>　　适用于: 块级和替换元素</p>
												<p>　　向下兼容: 否</p>
												<p>　　宽度属性的初始值为“auto”，即为该元素的原有宽度(有就是元素自己的宽度)。百分比参考上级元素的宽度。不允许使用负的长度值。</p>
												<p>　　14.高度</p>
												<p>　　语法: height: &lt;值&gt;</p>
												<p>　　允许值: &lt;长度&gt; | auto</p>
												<p>　　初始值: auto</p>
												<p>　　适用于: 块级和替换元素</p>
												<p>　　向下兼容: 否</p>
												<p>　　高度属性的初始值为“auto”，即为该元素的原有高度(有就是元素自己的高度，)。百分比参考上级元素的宽度。不允许使用负的长度值。</p>
												<p>　　15.有关标签</p>
												<p>　　table：表格标签，对整个表格样式的定义要放在table中； <br />　　td：单元格标签，对单元格样式的定义要放在td中。 </p>
												<p>css滤镜</p>
												<p>随着网页设计技术的发展，人们已经不满足于原有的一些HTML标记，而是希望能够为页面添加一些多媒体属性，例如滤镜的和渐变的效果。CSS技术的飞快发展使这些需求成为了现实。从现在开始我要为大家介绍一个新的CSS扩展部分：CSS滤镜属性（Filter Properties)。使用这种技术可以把可视化的滤镜和转换效果添加到一个标准的HTML元素上，例如图片、文本容器、以及其他一些对象。对于滤镜和渐变效果，前者是基础，因为后者就是滤镜效果的不断变化和演示更替。当滤镜和渐变效果结合到一个基本的SCRIPT小程序中后，网页设计者就可以拥有一个建立动态交互文档的强大工具。也就是CSS FILTER+ SCRIPT， 这就说明想要建立动态的文档还要一些SCRIPT （脚本语言）的基础。 <br />可视化滤镜属性只能用在HTML控件元素上。所谓的HTML空间元素就是它们在页面上定义了一个矩形空间，浏览器的窗口可以显示这些空间。下面列出了HTML合法的控件和它们的说明。</p>
												<p>备注：可惜只有IE4.0以上支持，如果是别的浏览器，那就.......</p>
												<p>元素 说明 <br />BODY 网页文档的主体元素，所有的可见范围都在&lt;BODY&gt;元素内 <br />BUTTON 表单域的按钮，可以有“发送(submit)”、“重置(reset)”等形式 <br />DIV 定义了网页上的一个区域，这个区域的高度、宽度或者绝对位置都是以知的 <br />IMG 图片元素，通过指定“src"属性来指定图片的来源 <br />INPUT 输入表单域 <br />MARQUEE 移动字幕效果 <br />SPAN 定义了网页上的一个区域，这个区域的高度、宽度或者绝对位置都是以知的 <br />TABLE 表格 <br />TD 表格数据单元格 <br />TEXTAREA 文本区域 <br />TFOOT 多行输入文本框 <br />TH 表格标题单元格 <br />THEAD 表格标题 <br />TR 表格行 </p>
												<p>　</p>
												<p>IE4.0以上支持的滤镜属性表</p>
												<p>滤镜效果 描述 <br />Alpha 设置透明度 <br />Blru 建立模糊效果 <br />Chroma 把指定的颜色设置为透明 <br />DropShadow 建立一种偏移的影象轮廓，即投射阴影 <br />FlipH 水平反转 <br />FlipV 垂直反转 <br />Glow 为对象的外边界增加光效 <br />Grayscale 降低图片的彩色度 <br />Invert 将色彩、饱和度以及亮度值完全反转建立底片效果 <br />Light 在一个对象上进行灯光投影 <br />Mask 为一个对象建立透明膜 <br />Shadow 建立一个对象的固体轮廓，即阴影效果 <br />Wave 在X轴和Y轴方向利用正弦波纹打乱图片 <br />Xray 只显示对象的轮廓 </p>
												<p>1、Alpha 滤镜 <br /></p>
												<p>语法：{FILTER：ALPHA(opacity=opacity,finishopacity=finishopacity,style=style,startx=startx, <br />starty=starty,finishx=finishx,finishy=finishy)}</p>
												<p>"Alpha"属性是把一个目标元素与背景混合。设计者可以指定数值来控制混合的程度。这种“与背景混合”通俗地说就是一个元素的透明度。通过指定坐标，可以指定点、线、面的透明度。他们的参数含义分别如下： <br />“opacity"代表透明度水准。默认的范围是从0 到 100，他们其实是百分比的形式。也就是说，0代表完全透明，100代表完全不透明。”finishopacity"是一个可选参数，如果想要设置渐变的透明效果，就可以使用他们来指定结束时的透明度。范围也是0 到 100。“style" 参数指定了透明区域的形状特征。其中0代表统一形状、1代表线形、2代表放射状、3代表长方形。”STARTX“和”STARTY“代表渐变透明效果的开始X和Y坐标。”FINISHX“和”FINISHY“代表渐变透明效果结束X和Y 的坐标。</p>
												<p>效果如下：</p>
												<p>2、Blur 滤镜</p>
												<p>语法：对于HTML：{ilter:blur(add=add,direction=direction,strength=strength)} <br />对于Script语言： [oblurfilter=] object.filters.blur <br />用手指在一幅尚未干透的油画上迅速划过时，画面就会变得模糊。”Blur"就是产生同样的模糊效果。 <br />“ADD”参数是一个布尔判断“TRUE（默认）”或者“FALSE”。它指定图片是否被改变成印象派的模糊效果。模糊效果是按顺时针的方向进行的，“DIRECTION”参数用来设置模糊的方向。其中0度代表垂直向上，然后每45度为一个单位。它的默认值是向左的270度。“STRENGTH“值只能使用整数来指定，她代表有多少像素的宽度将受到模糊影响。默认是5个。对于网页上的字体，如果设置他的模糊”ADD=1“，那么这些字体的效果会非常好看的。如下： <br />filter:blur(add=ture,direction=135,strength=10）</p>
												<p>3、FlipH, FlipV 滤镜</p>
												<p>语法：{filter:filph} ,{filter:filpv} 分别是水平反转和垂直反转，具体如下：</p>
												<p>4、Chroma 滤镜</p>
												<p>语法：{filter:chroma(color=color)} <br />使用”Chroma"属性可以设置一个对象中指定的颜色为透明色，参数COLOR即要透明的颜色。下面是兰色文字，然后用Chroma 滤镜过滤掉兰色，就成了下面的样子。 <br />filter:chroma(color=blue)</p>
												<p>滴水檐坊</p>
												<p>5、DropShadow 滤镜</p>
												<p>语法：{filter:dropshadow(color=color,offx=ofx,offy=offy,positive=positive)}</p>
												<p>“DropShaow"顾名思义就是添加对象的阴影效果。其工作原理是建立一个偏移量，加上较深。"Color"代表投射阴影的颜色，"offx"和"offy"分别是X方向和Y方向阴影的饿偏移量。"Positive"参数是一个布尔值，如果为“TRUE（非0）”，那么就为任何的非透明像素建立可见的投影。如果为“FASLE（0）”，那么就为透明的像素部分建立透明效果</p>
												<p>6、Glow 滤镜</p>
												<p>语法：{filter:glow(color=color,strength)} <br />当对一个对象使用"glow"属性后，这个对象的边缘就会产生类似发光的效果。“COLOR”是指定发光的颜色，“STRENGTH”则是强度的表现，可以从1到255之间的任何整数来指定这个力度。 <br />filter:glow(color=red,strength=10) 后的效果 </p>
												<p>7、Gray ,Invert,Xray 滤镜</p>
												<p>语法：{filter:gray} ,{filter:invert},{filter:xray}</p>
												<p>Gray滤镜是把一张图片变成灰度图；Invert滤镜是把对象的可视化属性全部翻转，包括色彩、饱和度、和亮度值；Xray滤镜是让对象反映出它的轮廓并把这些轮廓加亮，也就是所谓的“X”光片。</p>
												<p>效果如下：</p>
												<p>、Light 滤镜</p>
												<p>语法：Filter{light}</p>
												<p>这个属性模拟光源的投射效果。一旦为对象定义了“LIGHT"滤镜属性，那么就可以调用它的“方法(Method)"来设置或者改变属性。“LIGHT"可用的方法有：</p>
												<p>AddAmbient 加入包围的光源 <br />AddCone 加入锥形光源 <br />AddPoint 加入点光源 <br />Changcolor 改变光的颜色 <br />Changstrength 改变光源的强度 <br />Clear 清除所有的光源 <br />MoveLight 移动光源 <br />可以定义光源的虚拟位置，以及通过调整X轴和Y轴的数值来控制光源焦点的位置，还可以调整光源的形式（点光源或者锥形光源）指定光源是否模糊边界、光源的颜色、亮度等属性。如果动态的设置光源，可能回产生一些意想不到的效果。后面几页会有具体范例。</p>
												<p>9、Mask 滤镜</p>
												<p>语法：{filter:mask(color=color)}</p>
												<p>使用"MASK"属性可以为对象建立一个覆盖于表面的膜，其效果就象戴者有色眼镜看物体一样。</p>
												<p>10、Shadow 滤镜</p>
												<p>语法：{filter:shadow(color=color,direction=direction)}</p>
												<p>利用“Shadow”属性可以在指定的方向建立物体的投影，COLOR是投影色，DIRECTION是设置投影的方向。其中0度代表垂直向上，然后每45度为一个单位。它的默认值是向左的270度。</p>
												<p>filter:shadow(color=red,direction=225) <br />filter:shadow(color=blue,direction=225) <br />filter:shadow(color=gray,direction=225) <br />效果分别如下：</p>
												<p>11、Wave 滤镜 语法：{filter:wave(add=add,freq=freq,lightstrength=strength,phase=phase,strength=strength)} <br />"wave" 属性把对象按垂直的波形样式打乱。默认是“TRUE（非0）”， <br />“ADD”表示是否要把对象按照波形样式打乱，</p>
												<p>“FREQ”是波纹的频率，也就是指定在对象上一共需要产生多少个完整的波纹，</p>
												<p>“LIGHTSTRENGTH”参数可以对于波纹增强光影的效果，范围0----100，</p>
												<p>“PHASE”参数用来设置正弦波的偏移量。</p>
												<p>“STRENGTH”代表振幅大小。</p>
										</td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.blogjava.net/ltc603/aggbug/77265.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2006-10-25 19:11 <a href="http://www.blogjava.net/ltc603/archive/2006/10/25/77265.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java虚拟机的深入研究--转</title><link>http://www.blogjava.net/ltc603/archive/2006/10/15/75267.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Sun, 15 Oct 2006 07:39:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/10/15/75267.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/75267.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/10/15/75267.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/75267.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/75267.html</trackback:ping><description><![CDATA[
		<div align="left">
				<p class="title" align="center">Java虚拟机的深入研究</p>
				<p align="center">作者：刘学超 </p>
				<p>
						<b>1  Java技术与Java虚拟机</b>
				</p>
				<p class="normal">说起Java，人们首先想到的是Java编程语言，然而事实上，Java是一种技术，它由四方面组成: Java编程语言、Java类文件格式、Java虚拟机和Java应用程序接口(Java API)。它们的关系如下图所示：</p>
				<p align="center">
						<img height="397" alt="" src="http://gceclub.sun.com.cn/staticcontent/html/2004-04-09/jvm01.gif" width="529" border="0" />
				</p>
				<p align="center">图1  Java四个方面的关系</p>
				<p class="normal">运行期环境代表着Java平台，开发人员编写Java代码(.java文件)，然后将之编译成字节码(.class文件)。最后字节码被装入内存，一旦字节码进入虚拟机，它就会被解释器解释执行，或者是被即时代码发生器有选择的转换成机器码执行。从上图也可以看出Java平台由Java虚拟机和Java应用程序接口搭建，Java语言则是进入这个平台的通道，用Java语言编写并编译的程序可以运行在这个平台上。这个平台的结构如下图所示：</p>
				<p align="center">
						<img height="251" alt="" src="http://gceclub.sun.com.cn/staticcontent/html/2004-04-09/jvm02.gif" width="609" border="0" />
				</p>
				<p class="normal">在Java平台的结构中, 可以看出，Java虚拟机(JVM) 处在核心的位置，是程序与底层操作系统和硬件无关的关键。它的下方是移植接口，移植接口由两部分组成：适配器和Java操作系统, 其中依赖于平台的部分称为适配器；JVM 通过移植接口在具体的平台和操作系统上实现；在JVM 的上方是Java的基本类库和扩展类库以及它们的API， 利用Java API编写的应用程序(application) 和小程序(Java applet) 可以在任何Java平台上运行而无需考虑底层平台, 就是因为有Java虚拟机(JVM)实现了程序与操作系统的分离，从而实现了Java 的平台无关性。 
</p>
				<p class="normal">那么到底什么是Java虚拟机(JVM)呢？通常我们谈论JVM时，我们的意思可能是： 
</p>
				<ol>
						<li>对JVM规范的的比较抽象的说明； 
</li>
						<li>对JVM的具体实现； 
</li>
						<li>在程序运行期间所生成的一个JVM实例。 </li>
				</ol>
				<p class="normal">对JVM规范的的抽象说明是一些概念的集合，它们已经在书《The Java Virtual Machine Specification》（《Java虚拟机规范》）中被详细地描述了；对JVM的具体实现要么是软件，要么是软件和硬件的组合，它已经被许多生产厂商所实现，并存在于多种平台之上；运行Java程序的任务由JVM的运行期实例单个承担。在本文中我们所讨论的Java虚拟机(JVM)主要针对第三种情况而言。它可以被看成一个想象中的机器，在实际的计算机上通过软件模拟来实现，有自己想象中的硬件，如处理器、堆栈、寄存器等，还有自己相应的指令系统。</p>
				<p class="normal">JVM在它的生存周期中有一个明确的任务，那就是运行Java程序，因此当Java程序启动的时候，就产生JVM的一个实例；当程序运行结束的时候，该实例也跟着消失了。下面我们从JVM的体系结构和它的运行过程这两个方面来对它进行比较深入的研究。 
</p>
				<p>
						<b>2  Java虚拟机的体系结构</b>
				</p>
				<p class="normal">刚才已经提到，JVM可以由不同的厂商来实现。由于厂商的不同必然导致JVM在实现上的一些不同，然而JVM还是可以实现跨平台的特性，这就要归功于设计JVM时的体系结构了。</p>
				<p class="normal">我们知道，一个JVM实例的行为不光是它自己的事，还涉及到它的子系统、存储区域、数据类型和指令这些部分，它们描述了JVM的一个抽象的内部体系结构，其目的不光规定实现JVM时它内部的体系结构，更重要的是提供了一种方式，用于严格定义实现时的外部行为。每个JVM都有两种机制，一个是装载具有合适名称的类(类或是接口)，叫做类装载子系统；另外的一个负责执行包含在已装载的类或接口中的指令，叫做运行引擎。每个JVM又包括方法区、堆、Java栈、程序计数器和本地方法栈这五个部分，这几个部分和类装载机制与运行引擎机制一起组成的体系结构图为：</p>
				<p align="center">
						<img height="299" alt="" src="http://gceclub.sun.com.cn/staticcontent/html/2004-04-09/jvm03.gif" width="589" border="0" />
				</p>
				<p align="center">图3  JVM的体系结构</p>
				<p class="normal">JVM的每个实例都有一个它自己的方法域和一个堆，运行于JVM内的所有的线程都共享这些区域；当虚拟机装载类文件的时候，它解析其中的二进制数据所包含的类信息，并把它们放到方法域中；当程序运行的时候，JVM把程序初始化的所有对象置于堆上；而每个线程创建的时候，都会拥有自己的程序计数器和Java栈，其中程序计数器中的值指向下一条即将被执行的指令，线程的Java栈则存储为该线程调用Java方法的状态；本地方法调用的状态被存储在本地方法栈，该方法栈依赖于具体的实现。</p>
				<p class="normal">下面分别对这几个部分进行说明。</p>
				<p class="normal">执行引擎处于JVM的核心位置，在Java虚拟机规范中，它的行为是由指令集所决定的。尽管对于每条指令，规范很详细地说明了当JVM执行字节码遇到指令时，它的实现应该做什么，但对于怎么做却言之甚少。Java虚拟机支持大约248个字节码。每个字节码执行一种基本的CPU运算,例如,把一个整数加到寄存器,子程序转移等。Java指令集相当于Java程序的汇编语言。</p>
				<p class="normal">Java指令集中的指令包含一个单字节的操作符,用于指定要执行的操作,还有0个或多个操作数,提供操作所需的参数或数据。许多指令没有操作数,仅由一个单字节的操作符构成。</p>
				<pre>虚拟机的内层循环的执行过程如下: 
do{ 
取一个操作符字节; 
根据操作符的值执行一个动作; 
}while(程序未结束)
</pre>
				<p class="normal">由于指令系统的简单性,使得虚拟机执行的过程十分简单,从而有利于提高执行的效率。指令中操作数的数量和大小是由操作符决定的。如果操作数比一个字节大,那么它存储的顺序是高位字节优先。例如,一个16位的参数存放时占用两个字节,其值为: 
</p>
				<p class="normal">第一个字节*256+第二个字节字节码。 
</p>
				<p class="normal">指令流一般只是字节对齐的。指令tableswitch和lookup是例外,在这两条指令内部要求强制的4字节边界对齐。 
</p>
				<p class="normal">对于本地方法接口，实现JVM并不要求一定要有它的支持，甚至可以完全没有。Sun公司实现Java本地接口(JNI)是出于可移植性的考虑，当然我们也可以设计出其它的本地接口来代替Sun公司的JNI。但是这些设计与实现是比较复杂的事情，需要确保垃圾回收器不会将那些正在被本地方法调用的对象释放掉。 
</p>
				<p class="normal">Java的堆是一个运行时数据区,类的实例(对象)从中分配空间，它的管理是由垃圾回收来负责的:不给程序员显式释放对象的能力。Java不规定具体使用的垃圾回收算法,可以根据系统的需求使用各种各样的算法。 
</p>
				<p class="normal">Java方法区与传统语言中的编译后代码或是Unix进程中的正文段类似。它保存方法代码(编译后的java代码)和符号表。在当前的Java实现中,方法代码不包括在垃圾回收堆中,但计划在将来的版本中实现。每个类文件包含了一个Java类或一个Java界面的编译后的代码。可以说类文件是Java语言的执行代码文件。为了保证类文件的平台无关性,Java虚拟机规范中对类文件的格式也作了详细的说明。其具体细节请参考Sun公司的Java虚拟机规范。 
</p>
				<p class="normal">Java虚拟机的寄存器用于保存机器的运行状态,与微处理器中的某些专用寄存器类似。Java虚拟机的寄存器有四种: </p>
				<ol>
						<li>pc: Java程序计数器； 
</li>
						<li>optop: 指向操作数栈顶端的指针； 
</li>
						<li>frame: 指向当前执行方法的执行环境的指针；。 
</li>
						<li>vars: 指向当前执行方法的局部变量区第一个变量的指针。 </li>
				</ol>
				<p class="normal">在上述体系结构图中，我们所说的是第一种，即程序计数器，每个线程一旦被创建就拥有了自己的程序计数器。当线程执行Java方法的时候，它包含该线程正在被执行的指令的地址。但是若线程执行的是一个本地的方法，那么程序计数器的值就不会被定义。 
</p>
				<p class="normal">Java虚拟机的栈有三个区域:局部变量区、运行环境区、操作数区。</p>
				<p>
						<b>局部变量区</b>
				</p>
				<p class="normal">每个Java方法使用一个固定大小的局部变量集。它们按照与vars寄存器的字偏移量来寻址。局部变量都是32位的。长整数和双精度浮点数占据了两个局部变量的空间,却按照第一个局部变量的索引来寻址。(例如,一个具有索引n的局部变量,如果是一个双精度浮点数,那么它实际占据了索引n和n+1所代表的存储空间)虚拟机规范并不要求在局部变量中的64位的值是64位对齐的。虚拟机提供了把局部变量中的值装载到操作数栈的指令,也提供了把操作数栈中的值写入局部变量的指令。</p>
				<p>
						<b>运行环境区</b>
				</p>
				<p class="normal">在运行环境中包含的信息用于动态链接,正常的方法返回以及异常捕捉。 
</p>
				<p class="normal">
						<b>动态链接</b>
				</p>
				<p class="normal">运行环境包括对指向当前类和当前方法的解释器符号表的指针,用于支持方法代码的动态链接。方法的class文件代码在引用要调用的方法和要访问的变量时使用符号。动态链接把符号形式的方法调用翻译成实际方法调用,装载必要的类以解释还没有定义的符号,并把变量访问翻译成与这些变量运行时的存储结构相应的偏移地址。动态链接方法和变量使得方法中使用的其它类的变化不会影响到本程序的代码。 
</p>
				<p class="normal">
						<b>正常的方法返回</b>
				</p>
				<p class="normal">如果当前方法正常地结束了,在执行了一条具有正确类型的返回指令时,调用的方法会得到一个返回值。执行环境在正常返回的情况下用于恢复调用者的寄存器,并把调用者的程序计数器增加一个恰当的数值,以跳过已执行过的方法调用指令,然后在调用者的执行环境中继续执行下去。 
</p>
				<p class="normal">
						<b>异常捕捉</b>
				</p>
				<p class="normal">异常情况在Java中被称作Error(错误)或Exception(异常),是Throwable类的子类,在程序中的原因是:①动态链接错,如无法找到所需的class文件。②运行时错,如对一个空指针的引用。程序使用了throw语句。 
</p>
				<p class="normal">当异常发生时,Java虚拟机采取如下措施: 
</p>
				<ul>
						<li>检查与当前方法相联系的catch子句表。每个catch子句包含其有效指令范围,能够处理的异常类型,以及处理异常的代码块地址。 
</li>
						<li>与异常相匹配的catch子句应该符合下面的条件:造成异常的指令在其指令范围之内,发生的异常类型是其能处理的异常类型的子类型。如果找到了匹配的catch子句,那么系统转移到指定的异常处理块处执行;如果没有找到异常处理块,重复寻找匹配的catch子句的过程,直到当前方法的所有嵌套的catch子句都被检查过。 
</li>
						<li>由于虚拟机从第一个匹配的catch子句处继续执行,所以catch子句表中的顺序是很重要的。因为Java代码是结构化的,因此总可以把某个方法的所有的异常处理器都按序排列到一个表中,对任意可能的程序计数器的值,都可以用线性的顺序找到合适的异常处理块,以处理在该程序计数器值下发生的异常情况。 
</li>
						<li>如果找不到匹配的catch子句,那么当前方法得到一个"未截获异常"的结果并返回到当前方法的调用者,好像异常刚刚在其调用者中发生一样。如果在调用者中仍然没有找到相应的异常处理块,那么这种错误将被传播下去。如果错误被传播到最顶层,那么系统将调用一个缺省的异常处理块。 </li>
				</ul>
				<p>
						<b>操作数栈区</b>
				</p>
				<p class="normal">机器指令只从操作数栈中取操作数,对它们进行操作,并把结果返回到栈中。选择栈结构的原因是:在只有少量寄存器或非通用寄存器的机器(如Intel486)上,也能够高效地模拟虚拟机的行为。操作数栈是32位的。它用于给方法传递参数,并从方法接收结果,也用于支持操作的参数,并保存操作的结果。例如,iadd指令将两个整数相加。相加的两个整数应该是操作数栈顶的两个字。这两个字是由先前的指令压进堆栈的。这两个整数将从堆栈弹出、相加,并把结果压回到操作数栈中。 
</p>
				<p class="normal">每个原始数据类型都有专门的指令对它们进行必须的操作。每个操作数在栈中需要一个存储位置,除了long和double型,它们需要两个位置。操作数只能被适用于其类型的操作符所操作。例如,压入两个int类型的数,如果把它们当作是一个long类型的数则是非法的。在Sun的虚拟机实现中,这个限制由字节码验证器强制实行。但是,有少数操作(操作符dupe和swap),用于对运行时数据区进行操作时是不考虑类型的。 
</p>
				<p class="normal">本地方法栈，当一个线程调用本地方法时，它就不再受到虚拟机关于结构和安全限制方面的约束，它既可以访问虚拟机的运行期数据区，也可以使用本地处理器以及任何类型的栈。例如，本地栈是一个C语言的栈，那么当C程序调用C函数时，函数的参数以某种顺序被压入栈，结果则返回给调用函数。在实现Java虚拟机时，本地方法接口使用的是C语言的模型栈，那么它的本地方法栈的调度与使用则完全与C语言的栈相同。</p>
				<p>
						<b>3  Java虚拟机的运行过程</b>
				</p>
				<p class="normal">上面对虚拟机的各个部分进行了比较详细的说明，下面通过一个具体的例子来分析它的运行过程。</p>
				<p class="normal">虚拟机通过调用某个指定类的方法main启动，传递给main一个字符串数组参数，使指定的类被装载，同时链接该类所使用的其它的类型，并且初始化它们。例如对于程序：</p>
				<pre>class HelloApp 
{
	public static void main(String[] args) 
	{
		System.out.println("Hello World!"); 
		for (int i = 0; i &lt; args.length; i++ )
		{
			System.out.println(args[i]);
		}
	}
}
</pre>
				<p class="normal">编译后在命令行模式下键入： java HelloApp run virtual machine 
</p>
				<p class="normal">将通过调用HelloApp的方法main来启动java虚拟机，传递给main一个包含三个字符串"run"、"virtual"、"machine"的数组。现在我们略述虚拟机在执行HelloApp时可能采取的步骤。 
</p>
				<p class="normal">开始试图执行类HelloApp的main方法，发现该类并没有被装载，也就是说虚拟机当前不包含该类的二进制代表，于是虚拟机使用ClassLoader试图寻找这样的二进制代表。如果这个进程失败，则抛出一个异常。类被装载后同时在main方法被调用之前，必须对类HelloApp与其它类型进行链接然后初始化。链接包含三个阶段：检验，准备和解析。检验检查被装载的主类的符号和语义，准备则创建类或接口的静态域以及把这些域初始化为标准的默认值，解析负责检查主类对其它类或接口的符号引用，在这一步它是可选的。类的初始化是对类中声明的静态初始化函数和静态域的初始化构造方法的执行。一个类在初始化之前它的父类必须被初始化。整个过程如下：</p>
				<p align="center">
						<img height="610" alt="" src="http://gceclub.sun.com.cn/staticcontent/html/2004-04-09/jvm04.gif" width="702" border="0" />
				</p>
				<p align="center">图4：虚拟机的运行过程</p>
				<p>
						<b>4  结束语</b>
				</p>
				<p class="normal">本文通过对JVM的体系结构的深入研究以及一个Java程序执行时虚拟机的运行过程的详细分析，意在剖析清楚Java虚拟机的机理。</p>
		</div>
<img src ="http://www.blogjava.net/ltc603/aggbug/75267.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2006-10-15 15:39 <a href="http://www.blogjava.net/ltc603/archive/2006/10/15/75267.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正则表达式</title><link>http://www.blogjava.net/ltc603/archive/2006/10/13/74915.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Fri, 13 Oct 2006 01:36:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/10/13/74915.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/74915.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/10/13/74915.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/74915.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/74915.html</trackback:ping><description><![CDATA[
		<p>struts的validator资料见：<a href="http://struts.apache.org/userGuide/dev_validator.html">struts validator</a></p>
		<p>附一个正则表达式的资料：</p>
		<h3 class="title" id="startcontent">正则表达式</h3>
		<p>　　正则表达式(regular expression)描述了一种字符串匹配的模式，可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。<br />　　列目录时，　dir *.txt或ls *.txt中的*.txt就</p>
		<ins>不</ins>是一个正则表达式,因为这里*与正则式的*的含义是不同的。<a name="more"></a><p>　　为便于理解和记忆，先从一些概念入手，所有特殊字符或字符组合有一个总表在后面，最后一些例子供理解相应的概念。</p><p></p><h3>正则表达式</h3><p><br />　　是由普通字符（例如字符 a 到 z）以及特殊字符（称为元字符）组成的文字模式。正则表达式作为一个模板，将某个字符模式与所搜索的字符串进行匹配。<br />　　可以通过在一对分隔符之间放入表达式模式的各种组件来构造一个正则表达式，即/expression/ </p><p></p><p></p><h3>普通字符</h3><p><br />　　由所有那些未显式指定为元字符的打印和非打印字符组成。这包括所有的大写和小写字母字符，所有数字，所有标点符号以及一些符号。 <br /></p><h3>非打印字符</h3><p><br /></p><table border="1"><tbody><tr><td>字符 </td><td>含义</td></tr><tr><td>\cx </td><td>匹配由x指明的控制字符。例如， \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则，将 c 视为一个原义的 'c' 字符。</td></tr><tr><td>\f </td><td>匹配一个换页符。等价于 \x0c 和 \cL。</td></tr><tr><td>\n </td><td>匹配一个换行符。等价于 \x0a 和 \cJ。</td></tr><tr><td>\r </td><td>匹配一个回车符。等价于 \x0d 和 \cM。</td></tr><tr><td>\s </td><td>匹配任何空白字符，包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。</td></tr><tr><td>\S </td><td>匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。</td></tr><tr><td>\t </td><td>匹配一个制表符。等价于 \x09 和 \cI。</td></tr><tr><td>\v </td><td>匹配一个垂直制表符。等价于 \x0b 和 \cK。</td></tr></tbody></table><br /><h3>特殊字符</h3><p><br />　　所谓特殊字符，就是一些有特殊含义的字符，如上面说的"*.txt"中的*，简单的说就是表示任何字符串的意思。如果要查找文件名中有＊的文件，则需要对＊进行转义，即在其前加一个\。ls \*.txt。正则表达式有以下特殊字符。<br /></p><table border="1"><tbody><tr><td>特别字符</td><td>说明</td></tr><tr><td>$</td><td>匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性，则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身，请使用 \$。</td></tr><tr><td>( )</td><td>标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符，请使用 \( 和 \)。</td></tr><tr><td>*</td><td>匹配前面的子表达式零次或多次。要匹配 * 字符，请使用 \*。</td></tr><tr><td>+</td><td>匹配前面的子表达式一次或多次。要匹配 + 字符，请使用 \+。</td></tr><tr><td>.</td><td>匹配除换行符 \n之外的任何单字符。要匹配 .，请使用 \。</td></tr><tr><td>[ </td><td>标记一个中括号表达式的开始。要匹配 [，请使用 \[。</td></tr><tr><td>?</td><td>匹配前面的子表达式零次或一次，或指明一个非贪婪限定符。要匹配 ? 字符，请使用 \?。</td></tr><tr><td>\</td><td>将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如， 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\"，而 '\(' 则匹配 "("。</td></tr><tr><td>^</td><td>匹配输入字符串的开始位置，除非在方括号表达式中使用，此时它表示不接受该字符集合。要匹配 ^ 字符本身，请使用 \^。</td></tr><tr><td>{</td><td>标记限定符表达式的开始。要匹配 {，请使用 \{。</td></tr><tr><td>|</td><td>指明两项之间的一个选择。要匹配 |，请使用 \|。</td></tr></tbody></table><p></p><p><br /><strong>　　构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与操作符将小的表达式结合在一起来创建更大的表达式。正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。</strong></p><p><br /></p><h3>限定符</h3><p><br />　　限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有*或+或?或{n}或{n,}或{n,m}共6种。<br />*、+和?限定符都是贪婪的，因为它们会尽可能多的匹配文字，只有在它们的后面加上一个?就可以实现非贪婪或最小匹配。<br />　　正则表达式的限定符有：<br /></p><table border="1"><tbody><tr><td>字符 </td><td>描述</td></tr><tr><td>* </td><td>匹配前面的子表达式零次或多次。例如，zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。</td></tr><tr><td>+ </td><td>匹配前面的子表达式一次或多次。例如，'zo+' 能匹配 "zo" 以及 "zoo"，但不能匹配 "z"。+ 等价于 {1,}。</td></tr><tr><td>? </td><td>匹配前面的子表达式零次或一次。例如，"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。</td></tr><tr><td>{n} </td><td>n 是一个非负整数。匹配确定的 n 次。例如，'o{2}' 不能匹配 "Bob" 中的 'o'，但是能匹配 "food" 中的两个 o。</td></tr><tr><td>{n,} </td><td>n 是一个非负整数。至少匹配n 次。例如，'o{2,}' 不能匹配 "Bob" 中的 'o'，但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。</td></tr><tr><td>{n,m} </td><td>m 和 n 均为非负整数，其中n &lt;= m。最少匹配 n 次且最多匹配 m 次。例如，"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。</td></tr></tbody></table><p></p><p></p><h3>定位符</h3><p><br />　　用来描述字符串或单词的边界，^和$分别指字符串的开始与结束，\b描述单词的前或后边界，\B表示非单词边界。</p><ins>不能对定位符使用限定符。</ins><p></p><p></p><h3>选择</h3><p><br />　　用圆括号将所有选择项括起来，相邻的选择项之间用|分隔。但用圆括号会有一个副作用，是相关的匹配会被缓存，此时可用?:放在第一个选项前来消除这种副作用。<br />　　其中?:是非捕获元之一，还有两个非捕获元是?=和?!，这两个还有更多的含义，前者为正向预查，在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串，后者为负向预查，在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串。 </p><p></p><p></p><h3>后向引用</h3><p><br />　　对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中，所捕获的每个子匹配都按照在正则表达式模式中从左至右所遇到的内容存储。存储子匹配的缓冲区编号从 1 开始，连续编号直至最大 99 个子表达式。每个缓冲区都可以使用 '\n' 访问，其中 n 为一个标识特定缓冲区的一位或两位十进制数。<br />　　可以使用非捕获元字符 '?:', '?=', or '?!' 来忽略对相关匹配的保存。 </p><p></p><p></p><h3>各种操作符的运算优先级</h3><p><br />　　相同优先级的从左到右进行运算，不同优先级的运算先高后低。各种操作符的优先级从高到低如下：<br /></p><table border="1"><tbody><tr><td>操作符 </td><td>描述</td></tr><tr><td>\ </td><td>转义符</td></tr><tr><td>(), (?:), (?=), [] </td><td>圆括号和方括号</td></tr><tr><td>*, +, ?, {n}, {n,}, {n,m} </td><td>限定符</td></tr><tr><td>^, $, \anymetacharacter </td><td>位置和顺序</td></tr><tr><td>| </td><td>“或”操作</td></tr></tbody></table><p></p><p></p><h3>全部符号解释</h3><p><br /></p><table border="1"><tbody><tr><td>字符 </td><td>描述</td></tr><tr><td>\ </td><td>将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如，'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\\' 匹配 "\" 而 "\(" 则匹配 "("。</td></tr><tr><td>^ </td><td>匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性，^ 也匹配 '\n' 或 '\r' 之后的位置。</td></tr><tr><td>$ </td><td>匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性，$ 也匹配 '\n' 或 '\r' 之前的位置。</td></tr><tr><td>* </td><td>匹配前面的子表达式零次或多次。例如，zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。</td></tr><tr><td>+ </td><td>匹配前面的子表达式一次或多次。例如，'zo+' 能匹配 "zo" 以及 "zoo"，但不能匹配 "z"。+ 等价于 {1,}。</td></tr><tr><td>? </td><td>匹配前面的子表达式零次或一次。例如，"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。</td></tr><tr><td>{n} </td><td>n 是一个非负整数。匹配确定的 n 次。例如，'o{2}' 不能匹配 "Bob" 中的 'o'，但是能匹配 "food" 中的两个 o。</td></tr><tr><td>{n,} </td><td>n 是一个非负整数。至少匹配n 次。例如，'o{2,}' 不能匹配 "Bob" 中的 'o'，但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。</td></tr><tr><td>{n,m} </td><td>m 和 n 均为非负整数，其中n &lt;= m。最少匹配 n 次且最多匹配 m 次。例如，"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。</td></tr><tr><td>? </td><td>当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时，匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串，而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如，对于字符串 "oooo"，'o+?' 将匹配单个 "o"，而 'o+' 将匹配所有 'o'。</td></tr><tr><td>. </td><td>匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符，请使用象 '[.\n]' 的模式。</td></tr><tr><td>(pattern) </td><td>匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到，在VBScript 中使用 SubMatches 集合，在JScript 中则使用 $0…$9 属性。要匹配圆括号字符，请使用 '\(' 或 '\)'。</td></tr><tr><td>(?:pattern) </td><td>匹配 pattern 但不获取匹配结果，也就是说这是一个非获取匹配，不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如， 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。</td></tr><tr><td>(?=pattern) </td><td>正向预查，在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配，也就是说，该匹配不需要获取供以后使用。例如，'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ，但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符，也就是说，在一个匹配发生后，在最后一次匹配之后立即开始下一次匹配的搜索，而不是从包含预查的字符之后开始。</td></tr><tr><td>(?!pattern) </td><td>负向预查，在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配，也就是说，该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows"，但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符，也就是说，在一个匹配发生后，在最后一次匹配之后立即开始下一次匹配的搜索，而不是从包含预查的字符之后开始</td></tr><tr><td>x|y </td><td>匹配 x 或 y。例如，'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。</td></tr><tr><td>[xyz] </td><td>字符集合。匹配所包含的任意一个字符。例如， '[abc]' 可以匹配 "plain" 中的 'a'。</td></tr><tr><td>[^xyz] </td><td>负值字符集合。匹配未包含的任意字符。例如， '[^abc]' 可以匹配 "plain" 中的'p'。</td></tr><tr><td>[a-z] </td><td>字符范围。匹配指定范围内的任意字符。例如，'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。</td></tr><tr><td>[^a-z] </td><td>负值字符范围。匹配任何不在指定范围内的任意字符。例如，'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。</td></tr><tr><td>\b </td><td>匹配一个单词边界，也就是指单词和空格间的位置。例如， 'er\b' 可以匹配"never" 中的 'er'，但不能匹配 "verb" 中的 'er'。</td></tr><tr><td>\B </td><td>匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er'，但不能匹配 "never" 中的 'er'。</td></tr><tr><td>\cx </td><td>匹配由 x 指明的控制字符。例如， \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则，将 c 视为一个原义的 'c' 字符。</td></tr><tr><td>\d </td><td>匹配一个数字字符。等价于 [0-9]。</td></tr><tr><td>\D </td><td>匹配一个非数字字符。等价于 [^0-9]。</td></tr><tr><td>\f </td><td>匹配一个换页符。等价于 \x0c 和 \cL。</td></tr><tr><td>\n </td><td>匹配一个换行符。等价于 \x0a 和 \cJ。</td></tr><tr><td>\r </td><td>匹配一个回车符。等价于 \x0d 和 \cM。</td></tr><tr><td>\s </td><td>匹配任何空白字符，包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。</td></tr><tr><td>\S </td><td>匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。</td></tr><tr><td>\t </td><td>匹配一个制表符。等价于 \x09 和 \cI。</td></tr><tr><td>\v </td><td>匹配一个垂直制表符。等价于 \x0b 和 \cK。</td></tr><tr><td>\w </td><td>匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。</td></tr><tr><td>\W </td><td>匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。</td></tr><tr><td>\xn </td><td>匹配 n，其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如，'\x41' 匹配 "A"。'\x041' 则等价于 '\x04' &amp; "1"。正则表达式中可以使用 ASCII 编码。.</td></tr><tr><td>\num </td><td>匹配 num，其中 num 是一个正整数。对所获取的匹配的引用。例如，'(.)\1' 匹配两个连续的相同字符。</td></tr><tr><td>\n </td><td>标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式，则 n 为向后引用。否则，如果 n 为八进制数字 (0-7)，则 n 为一个八进制转义值。</td></tr><tr><td>\nm </td><td>标识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式，则 nm 为向后引用。如果 \nm 之前至少有 n 个获取，则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足，若 n 和 m 均为八进制数字 (0-7)，则 \nm 将匹配八进制转义值 nm。</td></tr><tr><td>\nml </td><td>如果 n 为八进制数字 (0-3)，且 m 和 l 均为八进制数字 (0-7)，则匹配八进制转义值 nml。</td></tr><tr><td>\un </td><td>匹配 n，其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如， \u00A9 匹配版权符号 (?)。</td></tr></tbody></table><p></p><p></p><h3>部分例子</h3><p><br /></p><table border="1"><tbody><tr><td>正则表达式</td><td>说明</td></tr><tr><td>/\b([a-z]+) \1\b/gi</td><td>一个单词连续出现的位置</td></tr><tr><td>/(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)/ </td><td>将一个URL解析为协议、域、端口及相对路径</td></tr><tr><td>/^(?:Chapter|Section) [1-9][0-9]{0,1}$/</td><td>定位章节的位置</td></tr><tr><td>/[-a-z]/</td><td>A至z共26个字母再加一个-号。</td></tr><tr><td>/ter\b/</td><td>可匹配chapter，而不能terminal</td></tr><tr><td>/\Bapt/</td><td>可匹配chapter，而不能aptitude</td></tr><tr><td>/Windows(?=95 |98 |NT )/</td><td>可匹配Windows95或Windows98或WindowsNT,当找到一个匹配后，从Windows后面开始进行下一次的检索匹配。</td></tr></tbody></table><img src ="http://www.blogjava.net/ltc603/aggbug/74915.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2006-10-13 09:36 <a href="http://www.blogjava.net/ltc603/archive/2006/10/13/74915.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>出了点车祸</title><link>http://www.blogjava.net/ltc603/archive/2006/10/09/73942.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Mon, 09 Oct 2006 00:57:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/10/09/73942.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/73942.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/10/09/73942.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/73942.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/73942.html</trackback:ping><description><![CDATA[前两天晚上在经过一个十字路口的时候，不知中了什么邪，跑着横穿马路，正好被一个出租车撞上，<br /><br />撞的偶pp上，那叫一个险。幸亏是撞的pp上，而且车速不算快。身上是些皮外伤。<br /><br />所以奉劝大家宁等3小时，别抢1妙。<br /><br />要是真有急事儿，非要抢那1妙，那就别犹豫，跑快点。<img height="19" src="http://www.blogjava.net/Emoticons/emsmilep.gif" width="19" border="0" /><img src ="http://www.blogjava.net/ltc603/aggbug/73942.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2006-10-09 08:57 <a href="http://www.blogjava.net/ltc603/archive/2006/10/09/73942.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>101了</title><link>http://www.blogjava.net/ltc603/archive/2006/09/30/73076.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Sat, 30 Sep 2006 09:06:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/09/30/73076.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/73076.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/09/30/73076.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/73076.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/73076.html</trackback:ping><description><![CDATA[真的很快，工作了感觉时间过得太快了。真是time fly啊<img height="19" src="http://www.blogjava.net/Emoticons/emsmilep.gif" width="19" border="0" /><br /><br />明天就101了，感觉刚过完51。<br /><br />回家修养几天。<img height="19" src="http://www.blogjava.net/Emoticons/emteeth.gif" width="19" border="0" /><img src ="http://www.blogjava.net/ltc603/aggbug/73076.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2006-09-30 17:06 <a href="http://www.blogjava.net/ltc603/archive/2006/09/30/73076.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>