﻿<?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/ltc603/category/6947.html</link><description>&lt;font size="3"&gt;学无止境&lt;/font&gt;
&lt;br&gt;

&lt;script type="text/javascript" src="http://wujunlove.googlepages.com/bigstaticeyes.js"&gt;&lt;/script&gt;</description><language>zh-cn</language><lastBuildDate>Tue, 10 Apr 2007 00:13:48 GMT</lastBuildDate><pubDate>Tue, 10 Apr 2007 00:13:48 GMT</pubDate><ttl>60</ttl><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>0</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>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>OOD原则：SRP、OCP以及LSP</title><link>http://www.blogjava.net/ltc603/archive/2006/09/06/68123.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Wed, 06 Sep 2006 12:35:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/09/06/68123.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/68123.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/09/06/68123.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/68123.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/68123.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<div id="blogContent">
						<font style="LINE-HEIGHT: 1.5em" size="4">
								<strong>单一职责原则（SRP : Single Response Principle)</strong>
						</font>
						<br />
						<br />就一个类而言，应该仅有一个引起它变化的原因。<br /><font color="#ee1d24">在这里，职责的定义是： “变化的原因”。</font><br /><br />对于何时遵循SRP有以下的考虑：<br />1.如果应用程序的变化会影响到类中某一种职责，那么就应该将它与另一种职责分开，这样做可以避免客户应用程序和类中的这两职责耦合在一起。<br />2.如果应用程序的变化总是会导致两个职责同时变化，那么就不必要分离它们。实际上，分离它们会引起不必要的复杂性。<br /><br />从上可以得知：变化的轴线仅当变化实际发生时才具有真正的意义。如果没有征兆，那么去应用SRP，或者任何其它原则都是不明智。<br /><br /><strong>实际应用：持久化（Persistence）</strong><br />实际开发中，考虑到业务规则是会频繁改变的，而持久化的方式却不会如此频繁的变化，并且变化的原因也是完全不同的。如果把业务规则和持久化方式绑定到一起，就会为以后的开发、维护造成麻烦。运用分层（layer）架构模式或者TDD开发方式可以很早分离这两个职责，特殊情况下，还可以使用FACADE或者PROXY模式对设计进行重构，分离这两个职责。<br /><br /><div id="blogContent"><strong><font style="LINE-HEIGHT: 1.5em" size="4">开闭原则（OCP : The Open-Close Principle)</font></strong><br /><br />描述：软件实体（类、模型、函数等等）应该是可以扩展的，但是不可修改。<br /><br />遵循开闭原则设计出的模块具有两个主要的特征。它们是：<br />1. “对于扩展是开放的”（Open for extension）。<br />   这意味着模块的行为是可以扩展的。当应用的需要改变时，我们可以对模块进行扩展，使其具有满足那些改变的新行为。换句话说，我们可以改变模块的功能。<br />2. “对于更改是封闭的”（Closed for modification）。<br />   对模块行为进行扩展时，不必改动模块的源代码或者二进制代码。模块的二进制可执行版本，无论是可链接的库、DLL或者Java的.jar文件，都无需改动。<br /><br /><font color="#ee1d24">对于OCP，关键的是 抽象</font><br />模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体，所以它对于更改可以是关闭的。同时，通过从这个抽象体派生，也可以扩展此模块的行为。<br /><br />在许多方面，OCP都是面向对象设计的核心所在。但实际应用中，滥用OCP原则也是错误的。正确的做法是应该仅仅对程序中呈现出频繁变化的那些部分做出抽象。拒绝不成熟的抽象和抽象本身一样重要。</div><br /><br /><div id="blogContent"><strong><font style="LINE-HEIGHT: 1.5em" size="4">Liskov替换原则（LSP）</font></strong><br /><br />描述：子类型（subtype）必须能够替换掉它们的基类型（base type）。<br /><br />此原则是Barbara Liskov首次在1988年写下的。所以就叫做Liskov替换原则。她如此写到：<br />“这里需要如下替换性质：若对每个类型S的对象o1，都存在一个类型T的对象o2，使得在所有针对T编写的程序P中，用o1替换o2后，程序P行为功能不变，则S是T的子类型。<br /><br />LSP然我们得出一个非常重要的结论：一个模型，如果孤立的看，并不具有真正意义上的有效性。模型的有效性只能通过它的客户程序来表现。<br /><br />在考虑一个特定设计是否恰当时，不能完全孤立的来看这个解决方案。必须要根据该设计的使用者所做出的合理假设来审视它。<br /><br />有谁知道设计的使用者会做出什么样的合理假设呢？大多数这样的假设都很难预测。事实上，如果试图去预测所有这些假设，我们所得到的系统很可能会充满不必要的复杂性的臭味。因此，像所有其它原则一样了，通常最好的办法就是只预测那些最明显的对于LSP的违反情况，而推迟所有其它的预测，直到出现相关的脆弱性的臭味时，才去处理它们。<br /><br /><font color="#ee1d24">IS-A是关于行为的。</font><br />LSP清晰的指出，OOD中IS－A关系是就行为方式而言的，行为方式是可以进行合理假设的，是客户程序所依赖的。<br /><br /><font color="#ee1d24">基于契约设计</font><br />基于契约设计（DBC：Design By Contract）。使用DBC，类的编写者能够显式的规定针对该类的契约。客户代码的编写者可以通过该契约获悉可以依赖的行为方式。契约是通过为每个方法声明的前置条件（preconditions)和后置条件（postconditions)来指定的。要使一个方法得以执行，前置条件必须要为真。执行完毕后，该方法要保证后置条件为真。<br /><br /><font color="#ee1d24">在单元测试中指定契约</font><br />也可以通过编写单元测试的方式来指定契约。客户代码编写者会去查看这些单元测试，这样他们就可以知道对于要使用的类，应该做什么合理的假设。<br /><br /><font color="#ee1d24">启发式规则和习惯用法</font><br /><br />1.派生类中的退化函数<br />  在基类中实现了f()方法，在派生类中的函数f()就是退化的，派生类中的退化函数并不总表示为违反LSP，但是当存在这种情况时，还是值得注意一下的。<br />2.从派生类中抛出异常<br />  在派生类的方法中添加了其基类不会抛出的异常。如果基类的使用者不期望这些异常，那么把它们添加到派生类的方法中就会导致不可替换性。此时要遵循LSP，要么就必须改变使用者的期望，要么派生类就不应该抛出这些异常。<br /><br />总结：OCP是OOD中很多原则的核心。如果这个原则应用的有效，应用程序就会具有更多的可维护性、可重用性以及健壮性。LSP是使OCP成为可能的主要原则之一。正是子类型的可替换性才使得使用基类类型的模块在无需修改的情况下就可以扩展。这种可替换性必须是开发人员可以隐式依赖的。因此，如果没有显式的强制基类类型的契约，那么代码就必须良好的并且明显的表达出这一点。<br />      术语IS-A的含义过于广泛以至于不能作为子类型的定义。子类型的正确定义是“可替换性”，这里的可替换性可以通过显式或者隐式的契约来定义。</div></div>
				<br />
				<br />
				<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=749737</p>
				<div class="relation-article">
						<h3 class?posttitle?="">相关文章：</h3>
						<ul class="postText">
								<li>
										<a href="http://blog.csdn.net/lanbaibai/archive/2004/09/06/95660.aspx" target="_blank">面向对象的设计原则(整理于敏捷开发)</a> 2004-09-06 <a href="http://blog.csdn.net/lanbaibai/" target="_blank">lanbaibai</a></li>
								<li>
										<a href="http://blog.csdn.net/devercn/archive/2004/07/20/46584.aspx" target="_blank">面向对象设计原则</a> 2004-07-20 <a href="http://blog.csdn.net/devercn/" target="_blank">devercn</a></li>
								<li>
										<a href="http://blog.csdn.net/jotter/archive/2005/06/29/407678.aspx" target="_blank">OOD设计原则</a> 2005-06-29 <a href="http://blog.csdn.net/jotter/" target="_blank">jotter</a></li>
								<li>
										<a href="http://blog.csdn.net/asterdnet/archive/2004/12/13/215333.aspx" target="_blank">开放－封闭原则（OCP：The Open-Closed Principle）</a> 2004-12-13 <a href="http://blog.csdn.net/asterdnet/" target="_blank">asterdnet</a></li>
								<li>
										<a href="http://blog.csdn.net/robinvane/archive/2004/09/22/113257.aspx" target="_blank">Liskov替换原则</a> 2004-09-22 <a href="http://blog.csdn.net/robinvane/" target="_blank">robinvane</a></li>
						</ul>
				</div>
		</div>
<img src ="http://www.blogjava.net/ltc603/aggbug/68123.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-06 20:35 <a href="http://www.blogjava.net/ltc603/archive/2006/09/06/68123.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一些java术语 </title><link>http://www.blogjava.net/ltc603/archive/2006/09/05/67741.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Tue, 05 Sep 2006 02:11:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/09/05/67741.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/67741.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/09/05/67741.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/67741.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/67741.html</trackback:ping><description><![CDATA[
		<table style="TABLE-LAYOUT: fixed" cellspacing="0" cellpadding="0" width="100%">
				<tbody>
						<tr>
								<td valign="top" width="32">
								</td>
								<td style="LEFT: 0px; WIDTH: 100%; WORD-WRAP: break-word; 130: ">
										<table cellspacing="0" cellpadding="0" width="100%">
												<tbody>
														<tr>
																<td>
																		<font color="#000000">Abstract class 抽象类:抽象类是不允许实例化的类，因此一般它需要被进行扩展继承。 <br /><br />　　Abstract method 抽象方法:抽象方法即不包含任何功能代码的方法。 <br /><br />　　Access modifier 访问控制修饰符:访问控制修饰符用来修饰Java中类、以及类的方法和变量的访问控制属性。 <br /><br />　　Anonymous class 匿名类:当你需要创建和使用一个类，而又不需要给出它的名字.<br /><br />　　Anonymous inner classes 匿名内部类:匿名内部类是没有类名的局部内部类。 <br /><br />　　API 应用程序接口:提供特定功能的一组相关的类和方法的集合。 <br /><br />　　Array 数组:存储一个或者多个相同数据类型的数据结构，使用下标来访问。<br /><br />　　Automatic variables 自动变量:也称为方法局部变量method local variables，声明在方法体中的变量 <br /><br />　　AWT抽象窗口工具集:一个独立的API平台提供用户界面功能。 <br /><br />　　Base class 基类:即被扩展继承的类。 <br /><br />　　Blocked state 阻塞状态:当一个线程等待资源时即处于阻塞状态。阻塞状态不使用处理器资源 <br /><br />　　Call stack 调用堆栈:调用堆栈是一个方法列表，按调用顺序保存所有在运行期被调用的方法。 <br /><br />　　Casting 类型转换 :即一个类型到另一个类型的转换<br /><br />　　char 字符:容纳单字符的一种基本数据类型。 <br /><br />　　Child class 子类:见继承类Derived class <br /><br />　　Class 类:面向对象中的最基本、最重要的定义类型。 <br /><br />　　Class members 类成员:定义在类一级的变量，包括实例变量和静态变量。 <br /><br />　　Class methods 类方法:类方法通常是指的静态方法<br /><br />　　Class variable 类变量:见静态变量Static variable <br /><br />　　Collection 容器类:容器类可以看作是一种可以储存其他对象的对象<br /><br />　　Collection interface 容器类接口:容器类接口定义了一个对所有容器类的公共接口。 <br /><br />　　Collections framework 容器类构架:接口、实现和算法三个元素构成了容器类的架构。 <br /><br />　　Constructor 构造函数:在对象创建或者实例化时候被调用的方法。<br /><br />　　Containers容器:容器是一种特殊的组件，它可以容纳其他组件。 <br /><br />　　Declaration 声明:声明即是在源文件中描述类、接口、方法、包或者变量的语法。 <br /><br />　　Derived class 继承类:继承类是扩展继承某个类的类。 <br /><br />　　Encapsulation 封装性:封装性体现了面向对象程序设计的一个特性<br /><br />　　Event classes 事件类:所有的事件类都定义在java.awt.event包中。 <br /><br />　　Event sources 事件源:产生事件的组件或对象称为事件源。<br /><br />　　Exception 异常:异常是一种对象类型,异常还指应用中发生的一种非标准流程情况，即异常状态<br /><br />　　Extensibility扩展性:扩展性指的是面向对象程序中，不需要重写代码和重新设计，能容易的<br />             增强源设计的功能。 <br /><br />　　Finalizer 收尾:每个类都有一个特殊的方法finalizer，它不能被直接调用，而被JVM在适当的<br />           时候调用，通常用来处理一些清理资源的工作，因此称为收尾机制。 <br /><br />　　Garbage collection 垃圾回收机制:当需要分配的内存空间不再使用的时候，JVM将调用垃圾<br />           回收机制来回收内存空间。 <br /><br />　　Guarded region 监控区域:一段用来监控错误产生的代码。 <br /><br />　　Heap堆:Java中管理内存的结构称作堆。 <br /><br />　　Identifiers 标识符:即指定类、方法、变量的名字。注意Java是大小写敏感的语言。 <br /><br />　　Import statement 引入语法:引入语法允许你可以不使用某个类的全名就可以参考这个类。 <br /><br />　　Inheritance 继承:继承是面向对象程序设计的重要特点，它是一种处理方法，通过这一方法，<br />             一个对象可以获得另一个对象的特征。 <br /><br />　　Inner classes 内部类:内部类与一般的类相似，只是它被声明在类的内部，或者甚至某个<br />             类方法体中。 <br /><br />　　Instance 实例:类实例化以后成为一个对象。 <br /><br />　　Instance variable 实例变量<br /><br />　　Interface 接口:接口定义的是一组方法或者一个公共接口，它必须通过类来实现。 <br /><br />　　Java source file Java源文件:Java源程序包含的是Java程序语言计算机指令。 <br /><br />　　Java Virtual Machine (JVM) Java虚拟机:解释和执行Java字节码的程序，其中Java字节码<br />             由Java编译器生成。 <br /><br />　　javac Java编译器:Javac是Java编译程序的名称。 <br /><br />　　JVM Java虚拟机:见Java虚拟机 <br /><br />　　Keywords 关键字:即Java中的保留字，不能用作其他的标识符。 <br /><br />　　Layout managers 布局管理器:布局管理器是一些用来负责处理容器中的组件布局排列的类。 <br /><br />　　Local inner classes 局部内部类:在方法体中，或者甚至更小的语句块中定义的内部类。 <br /><br />　　Local variable 局部变量:在方法体中声明的变量 <br /><br />　　Member inner classes 成员内部类:定义在封装类中的没有指定static修饰符的内部类。 <br /><br />　　Members 成员:类中的元素，包括方法和变量。 <br /><br />　　Method 方法:完成特定功能的一段源代码，可以传递参数和返回结果，定义在类中。 <br /><br />　　Method local variables 方法局部变量:见自动变量Automatic variables <br /><br />　　Modifier 修饰符:用来修饰类、方法或者变量行为的关键字。 <br /><br />　　Native methods 本地方法:本地方法是指使用依赖平台的语言编写的方法，它用来完成Java<br />                 无法处理的某些依赖于平台的功能。 <br /><br />　　Object 对象:一旦类实例化之后就成为对象。 <br /><br />　　Overloaded methods 名称重载方法:方法的名称重载是指同一个类中具有多个方法，使用相同<br />                    的名称而只是其参数列表不同。 <br /><br />　　Overridden methods 覆盖重载方法:方法的覆盖重载是指父类和子类使用的方法采用同样的<br />                    名称、参数列表和返回类型。 <br /><br />　　Package 包:包即是将一些类聚集在一起的一个实体。 <br /><br />　　Parent class 父类:被其他类继承的类。也见基类。 <br /><br />　　Private members 私有成员:私有成员只能在当前类被访问，其他任何类都不可以访问之。 <br /><br />　　Public members 公共成员:公共成员可以被任何类访问，而不管该类属于那个包。 <br /><br />　　Runtime exceptions 运行时间异常:运行时间异常是一种不能被你自己的程序处理的异常。<br />                   通常用来指示程序BUG。 <br /><br />　　Source file 源文件:源文件是包含你的Java代码的一个纯文本文件。 <br /><br />　　Stack trace 堆栈轨迹:如果你需要打印出某个时间的调用堆栈状态，你将产生一个堆栈轨迹。 <br /><br />　　Static inner classes 静态内部类:静态内部类是内部类最简单的形式，它于一般的类很相似，<br />                   除了被定义在了某个类的内部。 <br /><br />　　Static methods 静态方法:静态方法声明一个方法属于整个类，即它可以不需要实例化一个类<br />                就可以通过类直接访问之。 <br /><br />　　Static variable 静态变量:也可以称作类变量。它类似于静态方法，也是可以不需要实例化类<br />               就可以通过类直接访问。 <br /><br />　　Superclass 超类:被一个或多个类继承的类。 <br /><br />　　Synchronized methods 同步方法:同步方法是指明某个方法在某个时刻只能由一个线程访问。 <br /><br />　　Thread 线程:线程是一个程序内部的顺序控制流。 <br /><br />　　Time-slicing 时间片:调度安排线程执行的一种方案。 <br /><br />　　Variable access 变量访问控制:变量访问控制是指某个类读或者改变一个其他类中的变量的能力。 <br /><br />　　Visibility 可见性: 可见性体现了方法和实例变量对其他类和包的访问控制。 <br /><br /><br />J2EE相关名词解释:<br /><br />　　容器：充当中间件的角色 <br /><br />　　WEB容器：给处于其中的应用程序组件（JSP，SERVLET）提供一个环境，使JSP,SERVLET直接更容器中的环境变量接口交互，不必关注其它系统问题。主要有WEB服务器来实现。例如：TOMCAT,WEBLOGIC,WEBSPHERE等。该容器提供的接口严格遵守J2EE规范中的WEB APPLICATION 标准。我们把遵守以上标准的WEB服务器就叫做J2EE中的WEB容器。 <br /><br />　　EJB容器：Enterprise java bean 容器。更具有行业领域特色。他提供给运行在其中的组件EJB各种管理功能。只要满足J2EE规范的EJB放入该容器，马上就会被容器进行高效率的管理。并且可以通过现成的接口来获得系统级别的服务。例如邮件服务、事务管理。 <br /><br />　　WEB容器和EJB容器在原理上是大体相同的，更多的区别是被隔离的外界环境。WEB容器更多的是跟基于HTTP的请求打交道。而EJB容器不是。它是更多的跟数据库、其它服务打交道。但他们都是把与外界的交互实现从而减轻应用程序的负担。例如SERVLET不用关心HTTP的细节，直接引用环境变量session,request,response就行、EJB不用关心数据库连接速度、各种事务控制，直接由容器来完成。 <br /><br />　　RMI/IIOP:远程方法调用/internet对象请求中介协议，他们主要用于通过远程调用服务。例如，远程有一台计算机上运行一个程序，它提供股票分析服务，我们可以在本地计算机上实现对其直接调用。当然这是要通过一定的规范才能在异构的系统之间进行通信。RMI是JAVA特有的。 <br /><br />　　JNDI:JAVA命名目录服务。主要提供的功能是：提供一个目录系统，让其它各地的应用程序在其上面留下自己的索引，从而满足快速查找和定位分布式应用程序的功能。 <br /><br />　　JMS:JAVA消息服务。主要实现各个应用程序之间的通讯。包括点对点和广播。 <br /><br />　　JAVAMAIL:JAVA邮件服务。提供邮件的存储、传输功能。他是JAVA编程中实现邮件功能的核心。相当MS中的EXCHANGE开发包。 <br /><br />　　JTA：JAVA事务服务。提供各种分布式事务服务。应用程序只需调用其提供的接口即可。<br />　　<br />　　JAF:JAVA安全认证框架。提供一些安全控制方面的框架。让开发者通过各种部署和自定义实现自己的个性安全控制策略。 <br /><br />　　EAI:企业应用集成。是一种概念，从而牵涉到好多技术。J2EE技术是一种很好的集成实现。<br /></font>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/ltc603/aggbug/67741.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-05 10:11 <a href="http://www.blogjava.net/ltc603/archive/2006/09/05/67741.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用过的类包</title><link>http://www.blogjava.net/ltc603/archive/2006/08/25/65799.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Fri, 25 Aug 2006 06:41:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/08/25/65799.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/65799.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/08/25/65799.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/65799.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/65799.html</trackback:ping><description><![CDATA[JDK<br />java.lang.*; <br />java.io.*; <br />java.util.*; <br />java.sql.*; <br /><img src ="http://www.blogjava.net/ltc603/aggbug/65799.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-08-25 14:41 <a href="http://www.blogjava.net/ltc603/archive/2006/08/25/65799.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正则表达式不断积累中</title><link>http://www.blogjava.net/ltc603/archive/2006/08/21/64722.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Mon, 21 Aug 2006 02:02:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/08/21/64722.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/64722.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/08/21/64722.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/64722.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/64722.html</trackback:ping><description><![CDATA[1、将com<strong>.</strong>regex.test转换成com/regex/test<br />用replaceAll转换<br />replaceAll中的第一个参数是一个正则表达示，不是普通的字符串组合<br /><br />replaceAll("<strong>.</strong>", "/") &lt;===&gt;Pattern.compile("<strong>.</strong>")<strong>.</strong>matcher(str)<strong>.</strong>replaceAll("/")<br /><br />"."需加转义符，要写成"\\.",如replaceAll("\\<strong>.</strong>", "/") 即可。<br /><img src ="http://www.blogjava.net/ltc603/aggbug/64722.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-08-21 10:02 <a href="http://www.blogjava.net/ltc603/archive/2006/08/21/64722.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>javaIO学习笔记</title><link>http://www.blogjava.net/ltc603/archive/2006/08/16/63944.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Wed, 16 Aug 2006 08:59:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/08/16/63944.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/63944.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/08/16/63944.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/63944.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/63944.html</trackback:ping><description><![CDATA[
		<p>第一篇、<a href="http://www.blueidea.com/bbs/newsdetail.asp?id=996916">http://www.blueidea.com/bbs/newsdetail.asp?id=996916</a>（里面有很多例子）<br /><br />第二篇、彻底明白Java的IO系统(文摘）---JAVA之精髓IO流<br />一． Input和Output<br />1. stream代表的是任何有能力产出数据的数据源，或是任何有能力接收数据的接收源。在Java的IO中，所有的stream（包括Input和Out stream）都包括两种类型：<br />1.1 以字节为导向的stream<br />以字节为导向的stream，表示以字节为单位从stream中读取或往stream中写入信息。以字节为导向的stream包括下面几种类型：<br />1) input　stream：<br />1) ByteArrayInputStream：把内存中的一个缓冲区作为InputStream使用<br />2) StringBufferInputStream：把一个String对象作为InputStream<br />3) FileInputStream：把一个文件作为InputStream，实现对文件的读取操作<br />4) PipedInputStream：实现了pipe的概念，主要在线程中使用<br />5) SequenceInputStream：把多个InputStream合并为一个InputStream<br />2) Out　stream<br />1) ByteArrayOutputStream：把信息存入内存中的一个缓冲区中<br />2) FileOutputStream：把信息存入文件中<br />3) PipedOutputStream：实现了pipe的概念，主要在线程中使用<br />4) SequenceOutputStream：把多个OutStream合并为一个OutStream<br />1.2 以Unicode字符为导向的stream<br />以Unicode字符为导向的stream，表示以Unicode字符为单位从stream中读取或往stream中写入信息。以Unicode字符为导向的stream包括下面几种类型：<br />1) Input　Stream<br />1) CharArrayReader：与ByteArrayInputStream对应<br />2) StringReader：与StringBufferInputStream对应<br />3) FileReader：与FileInputStream对应<br />4) PipedReader：与PipedInputStream对应<br />2) Out　Stream<br />1) CharArrayWrite：与ByteArrayOutputStream对应<br />2) StringWrite：无与之对应的以字节为导向的stream<br />3) FileWrite：与FileOutputStream对应<br />4) PipedWrite：与PipedOutputStream对应<br />以
字符为导向的stream基本上对有与之相对应的以字节为导向的stream。两个对应类实现的功能相同，字是在操作时的导向不同。如
CharArrayReader：和ByteArrayInputStream的作用都是把内存中的一个缓冲区作为InputStream使用，所不同的
是前者每次从内存中读取一个字节的信息，而后者每次从内存中读取一个字符。<br />1.3 两种不现导向的stream之间的转换<br />InputStreamReader和OutputStreamReader：把一个以字节为导向的stream转换成一个以字符为导向的stream。<br />2. stream添加属性<br />2.1 “为stream添加属性”的作用<br />运用上面介绍的Java中操作IO的API，我们就可完成我们想完成的任何操作了。但通过FilterInputStream和FilterOutStream的子类，我们可以为stream添加属性。下面以一个例子来说明这种功能的作用。<br />如果我们要往一个文件中写入数据，我们可以这样操作：<br />FileOutStream fs = new FileOutStream(“test.txt”);<br />然
后就可以通过产生的fs对象调用write()函数来往test.txt文件中写入数据了。但是，如果我们想实现“先把要写入文件的数据先缓存到内存中，
再把缓存中的数据写入文件中”的功能时，上面的API就没有一个能满足我们的需求了。但是通过FilterInputStream和
FilterOutStream的子类，为FileOutStream添加我们所需要的功能。<br />2.2 FilterInputStream的各种类型<br />2.2.1 用于封装以字节为导向的InputStream<br />1) DataInputStream：从stream中读取基本类型（int、char等）数据。<br />2) BufferedInputStream：使用缓冲区<br />3) LineNumberInputStream：会记录input stream内的行数，然后可以调用getLineNumber()和setLineNumber(int)<br />4) PushbackInputStream：很少用到，一般用于编译器开发<br />2.2.2 用于封装以字符为导向的InputStream<br />1) 没有与DataInputStream对应的类。除非在要使用readLine()时改用BufferedReader，否则使用DataInputStream<br />2) BufferedReader：与BufferedInputStream对应<br />3) LineNumberReader：与LineNumberInputStream对应<br />4) PushBackReader：与PushbackInputStream对应<br />2.3 FilterOutStream的各种类型<br />2.2.3 用于封装以字节为导向的OutputStream<br />1) DataIOutStream：往stream中输出基本类型（int、char等）数据。<br />2) BufferedOutStream：使用缓冲区<br />3) PrintStream：产生格式化输出<br />2.2.4 用于封装以字符为导向的OutputStream<br />1) BufferedWrite：与对应<br />2) PrintWrite：与对应<br />3. RandomAccessFile<br />1) 可通过RandomAccessFile对象完成对文件的读写操作<br />2) 在产生一个对象时，可指明要打开的文件的性质：r，只读；w，只写；rw可读写<br />3) 可以直接跳到文件中指定的位置<br />4. I/O应用的一个例子<br />import java.io.*;<br />public class TestIO{<br />public static void main(String[] args)<br />throws IOException{<br />//1.以行为单位从一个文件读取数据<br />BufferedReader in = <br />new BufferedReader(<br />new FileReader("F:\\nepalon\\TestIO.java"));<br />String s, s2 = new String();<br />while((s = in.readLine()) != null)<br />s2 += s + "\n";<br />in.close();</p>
		<p>//1b. 接收键盘的输入<br />BufferedReader stdin = <br />new BufferedReader(<br />new InputStreamReader(System.in));<br />System.out.println("Enter a line:");<br />System.out.println(stdin.readLine());</p>
		<p>//2. 从一个String对象中读取数据<br />StringReader in2 = new StringReader(s2);<br />int c;<br />while((c = in2.read()) != -1)<br />System.out.println((char)c);<br />in2.close();</p>
		<p>//3. 从内存取出格式化输入<br />try{<br />DataInputStream in3 = <br />new DataInputStream(<br />new ByteArrayInputStream(s2.getBytes()));<br />while(true)<br />System.out.println((char)in3.readByte()); <br />}<br />catch(EOFException e){<br />System.out.println("End of stream");<br />}</p>
		<p>//4. 输出到文件<br />try{<br />BufferedReader in4 =<br />new BufferedReader(<br />new StringReader(s2));<br />PrintWriter out1 =<br />new PrintWriter(<br />new BufferedWriter(<br />new FileWriter("F:\\nepalon\\ TestIO.out")));<br />int lineCount = 1;<br />while((s = in4.readLine()) != null)<br />out1.println(lineCount++ + "：" + s);<br />out1.close();<br />in4.close();<br />}<br />catch(EOFException ex){<br />System.out.println("End of stream");<br />}</p>
		<p>//5. 数据的存储和恢复<br />try{<br />DataOutputStream out2 = <br />new DataOutputStream(<br />new BufferedOutputStream(<br />new FileOutputStream("F:\\nepalon\\ Data.txt")));<br />out2.writeDouble(3.1415926);<br />out2.writeChars("\nThas was pi:writeChars\n");<br />out2.writeBytes("Thas was pi:writeByte\n");<br />out2.close();<br />DataInputStream in5 =<br />new DataInputStream(<br />new BufferedInputStream(<br />new FileInputStream("F:\\nepalon\\ Data.txt")));<br />BufferedReader in5br =<br />new BufferedReader(<br />new InputStreamReader(in5));<br />System.out.println(in5.readDouble());<br />System.out.println(in5br.readLine());<br />System.out.println(in5br.readLine());<br />}<br />catch(EOFException e){<br />System.out.println("End of stream");<br />}</p>
		<p>//6. 通过RandomAccessFile操作文件<br />RandomAccessFile rf =<br />new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw");<br />for(int i=0; i&lt;10; i++)<br />rf.writeDouble(i*1.414);<br />rf.close();</p>
		<p>rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r");<br />for(int i=0; i&lt;10; i++)<br />System.out.println("Value " + i + "：" + rf.readDouble());<br />rf.close();</p>
		<p>rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw");<br />rf.seek(5*8);<br />rf.writeDouble(47.0001);<br />rf.close();</p>
		<p>rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r");<br />for(int i=0; i&lt;10; i++)<br />System.out.println("Value " + i + "：" + rf.readDouble());<br />rf.close();<br />}<br />}<br />关于代码的解释（以区为单位）：<br />1区中，当读取文件时，先把文件内容读到缓存中，当调用in.readLine()时，再从缓存中以字符的方式读取数据（以下简称“缓存字节读取方式”）。<br />1b区中，由于想以缓存字节读取方式从标准IO（键盘）中读取数据，所以要先把标准IO（System.in）转换成字符导向的stream，再进行BufferedReader封装。<br />2区中，要以字符的形式从一个String对象中读取数据，所以要产生一个StringReader类型的stream。<br />4区中，对String对象s2读取数据时，先把对象中的数据存入缓存中，再从缓冲中进行读取；对TestIO.out文件进行操作时，先把格式化后的信息输出到缓存中，再把缓存中的信息输出到文件中。<br />5
区中，对Data.txt文件进行输出时，是先把基本类型的数据输出屋缓存中，再把缓存中的数据输出到文件中；对文件进行读取操作时，先把文件中的数据读
取到缓存中，再从缓存中以基本类型的形式进行读取。注意in5.readDouble()这一行。因为写入第一个writeDouble()，所以为了正
确显示。也要以基本类型的形式进行读取。<br />6区是通过RandomAccessFile类对文件进行操作。</p>
		<p>第三篇、花1K内存实现高效I/O的RandomAccessFile类（<a href="http://www-128.ibm.com/developerworks/cn/java/l-javaio/index.html">http://www-128.ibm.com/developerworks/cn/java/l-javaio/index.html</a>），解决RandomAccessFile类效率低下的问题，特别是“<a name="5"><span class="atitle"><font face="Arial" size="4">与JDK1.4新类MappedByteBuffer+RandomAccessFile的对比</font></span></a>”部分讲了怎样用jdk自己的功能实现。</p>
<img src ="http://www.blogjava.net/ltc603/aggbug/63944.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-08-16 16:59 <a href="http://www.blogjava.net/ltc603/archive/2006/08/16/63944.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>查询字符串是否含有汉字（是否含有gb2312格式的字符）</title><link>http://www.blogjava.net/ltc603/archive/2006/08/16/63935.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Wed, 16 Aug 2006 08:38:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/08/16/63935.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/63935.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/08/16/63935.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/63935.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/63935.html</trackback:ping><description><![CDATA[//对于gb2312来讲, 首字节码位从0×81 至0×FE，尾字节码位分别是0×40 至0×FE<br />//本例是验证此串是否含有gb2312格式的字符，即是否含有汉字<br />public class Test{<br />  public boolean isGB2312( String str )<br />  {<br />    char[] chars = str.toCharArray();<br />    boolean isGB2312 = false;<br />    for ( int i = 0; i &lt; chars.length; i++ )<br />    {<br />      byte[] bytes = ( "" + chars[i] ).getBytes();<br />      if ( bytes.length == 2 )<br />      {<br />        int[] ints = new int[2];<br />        ints[0] = bytes[0] &amp; 0xff;<br />        ints[1] = bytes[1] &amp; 0xff;<br />        if ( ints[0] &gt;= 0x81 &amp;&amp; ints[0] &lt;= 0xFE &amp;&amp; ints[1] &gt;= 0x40 &amp;&amp; ints[1] &lt;= 0xFE )<br />        {<br />          isGB2312 = true;<br />          break;<br />        }<br />      }<br />    }<br />    return isGB2312;<br />  }<br />  <br />  public static void main(String[] args)<br />  {<br />    String s = "ss您好ss";//结果为true<br />    String s = "ssssss";//结果为false<br />    Test test = new Test();<br />    System.out.println(test.isGB2312(s));<br />  }<br />}<img src ="http://www.blogjava.net/ltc603/aggbug/63935.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-08-16 16:38 <a href="http://www.blogjava.net/ltc603/archive/2006/08/16/63935.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA&amp;J2EE FAQ</title><link>http://www.blogjava.net/ltc603/archive/2006/08/16/63899.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Wed, 16 Aug 2006 06:43:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/08/16/63899.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/63899.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/08/16/63899.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/63899.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/63899.html</trackback:ping><description><![CDATA[
		<p>Java FAQ <br /> <br />目录：<br />Q1.1 什么是Java、Java2、JDK？JDK后面的1.3、1.4版本号又是怎么回事？<br />Q1.2 什么是JRE/J2RE？<br />Q1.3 学习Java用什么工具比较好？<br />Q1.4  学习Java有哪些好的参考书？<br />Q1.5  Java和C++哪个更好？<br />Q1.6  什么是J2SE/J2EE/J2ME？<br />Q2.1  我写了第一个Java程序，应该如何编译/运行？<br />Q2.2  我照你说的做了，但是出现什么“'javac' 不是内部或外部命令，也不是可运行的<br />程序或批处理文件。”。<br />Q2.3  环境变量怎么设置？<br />Q2.4  我在javac xxx.java的时候显示什么“unreported exception java.io.IOExcepti<br />on;”。<br />Q2.5  javac xxx.java顺利通过了，但是java xxx的时候显示什么“NoClassDefFoundErr<br />or”。<br />Q2.6  我在java xxx的时候显示“Exception in thread "main" java.lang.NoSuchMetho<br />dError: main”。<br />Q2.7  在java xxx的时候显示“Exception in thread "main" java.lang.NullPointerEx<br />ception”。<br />Q2.8 package是什么意思？怎么用？<br />Q2.9 我没有声明任何package会怎么样？<br />Q2.10 在一个类中怎么使用其他类？<br />Q2.11 我用了package的时候显示"NoClassDefFoundError"，但是我把所有package去掉的<br />时候能正常运行。<br />Q2.12 我想把java编译成exe文件，该怎么做？<br />Q2.13 我在编译的时候遇到什么"deprecated API"，是什么意思？<br />Q3.1 我怎么给java程序加启动参数，就像dir /p/w那样？<br />Q3.2 我怎么从键盘输入一个int/double/字符串？<br />Q3.3 我怎么输出一个int/double/字符串？<br />Q3.4 我发现有些书上直接用System.in输入，比你要简单得多。<br />Q3.5 我怎么从文件输入一个int/double/字符串？<br />Q3.6 我想读写文件的指定位置，该怎么办？<br />Q3.7 怎么判断要读的文件已经到了尽头？<br />Q4.1  java里面怎么定义宏？<br />Q4.2  java里面没法用const。<br />Q4.3  java里面也不能用goto。<br />Q4.4  java里面能不能重载操作符？<br />Q4.5  我new了一个对象，但是没法delete掉它。<br />Q4.6  我想知道为什么main方法必须被声明为public static？为什么在main方法中不能调<br />用非static成员？<br />Q4.7  throw和throws有什么不同？<br />Q4.8  什么是异常？<br />Q4.9  final和finally有什么不同？<br />Q5.1  extends和implements有什么不同？<br />Q5.2  java怎么实现多继承？<br />Q5.3 abstract是什么？<br />Q5.4 public,protected,private有什么不同？<br />Q5.5 Override和Overload有什么不同？<br />Q5.6 我继承了一个方法，但现在我想调用在父类中定义的方法。<br />Q5.7 我想在子类的构造方法中调用父类的构造方法，该怎么办？<br />Q5.8 我在同一个类中定义了好几个构造方法并且想在一个构造方法中调用另一个。<br />Q5.9 我没有定义构造方法会怎么样？<br />Q5.10 我调用无参数的构造方法失败了。<br />Q5.11 我该怎么定义类似于C++中的析构方法（destructor）？<br />Q5.12 我想将一个父类对象转换成一个子类对象该怎么做？<br />Q5.13 其实我不确定a是不是B的实例，能不能分情况处理？<br />Q5.14 我在方法里修改了一个对象的值，但是退出方法后我发现这个对象的值没变！<br />Q6.1 java能动态分配数组吗？<br />Q6.2 我怎么知道数组的长度？<br />Q6.3 我还想让数组的长度能自动改变，能够增加/删除元素。<br />Q     什么是链表？为什么要有ArrayList和LinkedList两种List？<br />Q6.5 我想用队列/栈。<br />Q6.6 我希望不要有重复的元素。<br />Q6.7 我想遍历集合/Map。<br />Q6.8 我还要能够排序。<br />Q6.9 但是我想给数组排序。<br />Q6.10 我想按不同方式排序。<br />Q6.11 Map有什么用？<br />Q6.12 set方法没问题，但是get方法返回的是Object。<br />Q6.13 ArrayList和Vector有什么不同？HashMap和Hashtable有什么不同？<br />Q6.14 我要获得一个随机数。<br />Q6.15 我比较两个String总是false，但是它们明明都是"abc" ！<br />Q6.16 我想修改一个String但是在String类中没找到编辑方法。<br />Q6.17 我想处理日期/时间。</p>
		<p>一、准备篇</p>
		<p>Q1.1 什么是Java、Java2、JDK？JDK后面的1.3、1.4版本号又是怎么回事？<br />答：Java是一种通用的，并发的，强类型的，面向对象的编程语言（摘自Java规范第二版<br />）。<br />JDK是Sun公司分发的免费Java开发工具包，正式名称为J2SDK(Java2 Software Develop K<br />it)。<br />包括基本的java工具包和标准类库。<br />到目前(2003年7月)为止，Java有3个主要版本，即1.0，1.1，2.0；<br />JDK有1.0，1.1，1.2，1.3，1.4五个版本。<br />从JDK1.2起，Sun公司觉得Java改变足够大而将java语言版本号提升为2.0。<br />不同的JDK主要在于提供的类库不同。作为学习你可以下载最新的JDK1.4.2。<br />真正开发时则应考虑向前兼容，比如1.3。下载请去<a href="http://java.sun.com/">http://java.sun.com</a>。<br />JDK1.5预计将在2004年推出，届时其中将包含若干崭新的特性。</p>
		<p>Q1.2 什么是JRE/J2RE？<br />答：J2RE是Java2 Runtime Environment，即Java运行环境，有时简称JRE。<br />如果你只需要运行Java程序或Applet，下载并安装它即可。<br />如果你要自行开发Java软件，请下载JDK。在JDK中附带有JRE。<br />注意由于Microsoft对Java的支持不完全，请不要使用IE自带的虚拟机来运行Applet，务必<br />安装一个JRE或JDK。</p>
		<p>Q1.3 学习Java用什么工具比较好？<br />答：作者建议首先使用JDK+文本编辑器，这有助你理解下列几个基础概念：path，classp<br />ath，package <br />并熟悉基本命令：javac和java。并且下载和你的JDK版本一致的API帮助。<br />如果你不确定类或函数的用法，请先查阅API而不是发贴求助。<br />当你熟悉Java之后，你可以考虑开始使用一个IDE。<br />作者推荐eclipse，下载网址<a href="http://www.eclipse.org/">http://www.eclipse.org</a>。因为eclispe是免费的，插件化的<br />。<br />eclispe的主要缺点是缺乏一个可视化的桌面程序开发工具，<br />幸运的是IBM在2003年11月已经将部分代码捐给eclipse组织，可以预计这个缺点很快就会<br />得到弥补。<br />无论如何，请不要使用Microsoft的VJ++！众所周知Microsoft从来就没有认真支持过Java<br />。<br />最后但并非最不重要，要有一本好的参考书，并且英文要过关。</p>
		<p>Q1.4  学习Java有哪些好的参考书？<br />答：作者首先推荐Thinking in Java，中文名《Java编程思想》，有中文版。<br />目前的最新版本是第三版。<br />在<a href="http://64.78.49.204/">http://64.78.49.204</a>可以免费下载英文版。<br />该书第一章介绍了很多面向对象的编程思想，作为新手应当认真阅读。<br />除此以外，O'relly出版社和Wrox出版社的书也不错。作者本人不喜欢大陆作者的书。<br />也许你觉得英文太难，但是网上大多数资料都是英文的。另外，你需要经常查阅API，而那<br />也是英文的。</p>
		<p>Q1.5  Java和C++哪个更好？<br />答：这个问题是一个很不恰当的问题。你应该问：Java和C++哪个更适用于我的项目？<br />Java的优点和缺点一样明显。<br />跨平台是Java的主要优点，但代价是运行速度的下降。<br />VC和Windows平台有良好的集成和足够快的速度，但是也只能局限在Windows平台上。<br />和C++相比，Java学起来更快，开发人员不会碰到很多容易出错的特性。<br />但是VB程序员甚至只需要拼装模块就可以了。</p>
		<p>Q1.6  什么是J2SE/J2EE/J2ME？<br />答：J2SE就是一般的Java。<br />J2ME是针对嵌入式设备的，比如支持Java的手机，它有自己的JRE和SDK。<br />J2EE是一组用于企业级程序开发的规范和类库，它使用J2SE的JRE。</p>
		<p>二、命令篇</p>
		<p>Q2.1  我写了第一个Java程序，应该如何编译/运行？<br />答：首先请将程序保存为xxx.java文件，注意你可能需要修改文件后缀名。<br />然后在dos窗口下使用javac xxx.java命令，你会发现该目录下多了一个xxx.class文件，</p>
		<p>再使用java xxx命令，你的java程序就开始运行了。</p>
		<p>Q2.2  我照你说的做了，但是出现什么“'javac' 不是内部或外部命令，也不是可运行的<br />程序或批处理文件。”。<br />答：你遇到了path问题。操作系统在一定的范围(path)内搜索javac.exe，但是没能找到。</p>
		<p>请编辑你的操作系统环境变量，新增一个JAVA_HOME变量，设为你JDK的安装目录，<br />再编辑Path变量，加上一项 %JAVA_HOME%\bin。<br />然后保存并新开一个dos窗口，你就可以使用javac和java命令了。</p>
		<p>Q2.3  环境变量怎么设置？<br />答：请向身边会设的人咨询。</p>
		<p>Q2.4  我在javac xxx.java的时候显示什么“unreported exception java.io.IOExcepti<br />on;”。<br />答：参见Q4.8以了解java中的异常机制。</p>
		<p>Q2.5  javac xxx.java顺利通过了，但是java xxx的时候显示什么“NoClassDefFoundErr<br />or”。<br />答：1. 你遇到了classpath问题。java命令在一定的范围(classpath)内搜索你直接或间接<br />使用的class文件，但是未能找到。<br />首先请确认你没有错敲成java xxx.class，<br />其次，检查你的CLASSPATH环境变量，其实你并不需要设置该变量，<br />但如果你设置了该变量又没有包含.（代表当前目录）的项，<br />你就会遇到这个问题。请在你的CLASSPATH环境变量中加入一项. 或干脆删掉这个变量。</p>
		<p>2. 如果你使用了并非JDK自带的标准包，比如javax.servlet.*包，也会遇到这个问题，请<br />将相应的jar文件加入classpath。<br />3. 如果你在java源文件中定义了package，请参见Q2.11。</p>
		<p>
				<br />Q2.6  我在java xxx的时候显示“Exception in thread "main" java.lang.NoSuchMetho<br />dError: main”。<br />答：首先，在你的程序中每个java文件有且只能有一个public类，<br />这个类的类名必须和文件名的大小写完全一样。<br />其次，在你要运行的类中有且只能有一个public static void main(String[] args)方法<br />，<br />这个方法就是你的主程序。</p>
		<p>
				<br />Q2.7  在java xxx的时候显示“Exception in thread "main" java.lang.NullPointerEx<br />ception”。<br />答：在程序中你试图在值为null的对象变量上调用方法，请检查你的程序确保你的对象被恰当的初始化。<br />参见Q4.8以了解java中的异常机制。</p>
		<p>
				<br />Q2.8 package是什么意思？怎么用？<br />答：为了唯一标识每个类并分组，java使用了package的概念。<br />每个类都有一个全名，例如String的全名是java.lang.String，其中java.lang是包名，S<br />tring是短名。按照java命名惯例，包名是全部小写的，而类名的第一个字母是大写的。<br />这样，如果你自行定义了同样名字的类String，你可以把它放在mypackage中，<br />通过使用全名mypackage.String和java.lang.String来区分这两个类。<br />同时，将逻辑上相关的类放在同一个包中，可以使程序结构更为清楚。<br />为了定义包，你要做的就是在java文件开头加一行“package mypackage;”。<br />注意包没有嵌套或包含关系，mypackage包和mypackage.mysubpackage包对JRE来说是并列的两个包（虽然开发者可<br />能暗示包含关系）。</p>
		<p>Q2.9 我没有声明任何package会怎么样？<br />答：你的类被认为放在默认包中。这时全名和短名是一致的。</p>
		<p>Q2.10 在一个类中怎么使用其他类？<br />答：如果你使用java.lang包或者默认包中的类，不用做任何事。<br />如果你的类位于mypackage包中，并且要调用同一包中的其他类，也不用做任何事。<br />如果你使用其他包中的类，在package声明之后，类声明之前使用import otherpackage1.Class<br />1; 或 import otherpackage2.*;  <br />这里.*表示引入这个包中的所有类。然后在程序中你可以使用其他类的短名。<br />如果短名间有重名冲突，必须使用全名来区分。<br />注意在使用其他包中的类时，你只能使用public的类和接口，参见Q5.4。</p>
		<p>Q2.11 我用了package的时候显示"NoClassDefFoundError"，但是我把所有package去掉的<br />时候能正常运行。<br />答：将你的java文件按包名组织存放。<br />比如你的工作目录是/work，你的类是package1.Class1，那么将它存放为/work/package1<br />/Class1.java。<br />如果没有声明包，那么直接放在/work下。<br />在/work下执行javac package1/class1.java，再执行java package1.class1，你会发现一<br />切正常。<br />另外，如果你的类的个数已经多到了你需要使用包来组织的话，你可以考虑开始使用IDE。</p>
		<p>Q2.12 我想把java编译成exe文件，该怎么做？<br />答：JDK只能将java源文件编译为class文件。<br />class文件是一种跨平台的字节码，必须依赖平台相关的JRE来运行。Java以此来实现跨平<br />台性。<br />有些开发工具可以将java文件编译为exe文件。作者反对这种做法，因为这样就取消了跨平<br />台性。<br />如果你确信你的软件只在Windows平台上运行，你可以考虑使用C++/C#来编程。</p>
		<p>Q2.13 我在编译的时候遇到什么"deprecated API"，是什么意思？<br />答：所谓deprecated是指已经过时，但是为了向前兼容起见仍然保留的方法。<br />这些方法可能会在以后取消支持。你应当改用较新的方法。<br />在API里面会说明你应当用什么方法来代替之。</p>
		<p>三、I/O篇</p>
		<p>Q3.1 我怎么给java程序加启动参数，就像dir /p/w那样？<br />答：还记得public static void main(String[] args)吗？这里的args就是你的启动参数<br />。<br />在运行时你输入java package1.class1 arg1 arg2，args中就会有两个String，第一个是<br />arg1，第二个是arg2。</p>
		<p>Q3.2 我怎么从键盘输入一个int/double/字符串？<br />答：java的I/O操作比C++要复杂一点。如果要从键盘输入，样例代码如下：<br />BufferedReader cin = new BufferedReader( new InputStreamReader( System.in ) );</p>
		<p>String s = cin.readLine();<br />这样你就获得了一个字符串，如果你需要数字的话再使用：<br />int n = Integer.parseInt( s ); 或者 double d = Double.parseDouble( s );<br />来将字符串"534"转换成int或double。</p>
		<p>Q3.3 我怎么输出一个int/double/字符串？<br />答：使用System.out.println(n)或者System.out.println("Hello")等等。</p>
		<p>Q3.4 我发现有些书上直接用System.in输入，比你要简单得多。<br />答：java使用unicode，是双字节。而System.in是单字节的stream。<br />如果你要输入双字节文字比如中文，请使用作者的做法。</p>
		<p>Q3.5 我怎么从文件输入/输出一个int/double/字符串？<br />答：类似于从键盘输入，只不过换成<br />BufferedReader fin = new BufferedReader( new FileReader(" myFileName " ) );<br />PrintWriter fout = new PrintWriter( new FileWriter(" myFileName " ) );<br />另外如果你还没下载API，请开始下载并阅读java.io包中的内容。</p>
		<p>Q3.6 我想读写文件的指定位置，该怎么办？<br />答：java.io.RandomAccessFile可以满足你的需要。</p>
		<p>Q3.7 怎么判断要读的文件已经到了尽头？<br />答：在Reader的read方法中明确说明返回-1表示流的结尾。</p>
		<p>四、 关键字篇</p>
		<p>Q4.1  java里面怎么定义宏？<br />答：java不支持宏，因为宏代换不能保证类型安全。<br />如果你需要定义常量，可以将它定义为某个类的static final成员。参见Q4.2和Q4.6。</p>
		<p>
				<br />Q4.2  java里面没法用const。<br />答：你可以用final关键字。例如 final int m = 9。被声明为final的变量不能被再次赋<br />值。唯一的例外是所谓blank final，如下例所示：<br />public class MyClass1 {<br />    private final int a = 3;<br />    private final int b; // blank final</p>
		<p>    public MyClass1() {<br />        a = 5; // 不合法，final变量不能被再次赋值。<br />        b = 4; // 合法，这是b第一次被赋值。<br />        b = 6; // 不合法，b不能被再次赋值。<br />    }<br />}<br />final也可以用于声明方法或类，被声明为final的方法或类不能被继承。<br />注意const是java的保留字以备扩充。</p>
		<p>Q4.3  java里面也不能用goto。<br />答：甚至在面向过程的语言中你也可以完全不用goto。请检查你的程序流程是否合理。</p>
		<p>如果你需要从多层循环中迅速跳出，java增强了（和C++相比）break和continue的功能，<br />支持label。<br />例如：<br />outer : <br />while( ... )<br />{<br />inner :<br />for( ... )<br />{<br />           ...   break inner; ...<br />           ... continue outer; ...<br />}<br />}<br />和const一样，goto也是java的保留字以备扩充。</p>
		<p>Q4.4  java里面能不能重载操作符？<br />答：不能。String的+号是唯一一个内置的重载操作符。你可以通过定义接口和方法来实现<br />类似功能。</p>
		<p>Q4.5  我new了一个对象，但是没法delete掉它。<br />答：java有自动内存回收机制，即所谓Garbarge Collection。你不需要删除对象。你再也<br />不用担心指针错误，内存溢出了。</p>
		<p>Q4.6  我想知道为什么main方法必须被声明为public static？为什么在main方法中不能调<br />用非static成员？<br />答：声明为public是为了这个方法可以被外部调用，详情见Q5.4。<br />static是为了将某个成员变量/方法关联到类（class）而非实例（instance）。<br />你不需要创建一个对象就可以直接使用这个类的static成员，因而在static成员中不能调<br />用非static成员，因为后者是关联到对象实例（instance）的。<br />在A类中调用B类的static成员可以使用B.staticMember的写法。<br />注意一个类的static成员变量是唯一的，被所有该类对象所共享的，在多线程程序设计中尤其要谨慎小心。<br />类的static成员是在类第一次被JRE装载的时候初始化的。<br />你可以使用如下方法来使用非static成员：<br />public class A<br />{<br />    private void someMethod() //非static成员<br />    {}<br />    public static void main(String args)<br />    {<br />         A a = new A();  //创建一个对象实例<br />         a.someMethod();  //现在你可以使用非static方法了<br />    }<br />}</p>
		<p>
				<br />Q4.7  throw和throws有什么不同？<br />答：throws用于方法声明中，声明一个方法会抛出哪些异常。而throw是在方法体中实际执行抛出异常的<br />动作。<br />如果你在方法中throw一个异常，却没有在方法声明中声明之，编译器会报错。<br />注意Error和RuntimeException的子类是例外，无需特别声明。</p>
		<p>Q4.8  什么是异常？<br />答：异常最早在Ada语言中引入，用于在程序中动态处理错误并恢复。<br />你可以在方法中拦截底层异常并处理之，也可以抛给更高层的模块去处理。<br />你也可以抛出自己的异常指示发生了某些不正常情况。常见的拦截处理代码如下：<br />try<br />{<br />......//以下是可能发生异常的代码<br />        ...... //异常被你或低层API抛出，执行流程中断并转向拦截代码。<br />        ...... <br />}<br />catch(Exception1 e) //如果Exception1是Exception2的子类并要做特别处理，应排在前<br />面<br />{<br />  //发生Exception1时被该段拦截<br />}<br />catch(Exception2 e)<br />{<br />  //发生Exception2时被该段拦截<br />}<br />finally //这是可选的<br />{<br />   //无论异常是否发生，均执行此段代码<br />   //即使在catch段中又向外抛出了新的exception，finally段也会得到执行。<br />}</p>
		<p>Q4.9  final和finally有什么不同？<br />答：final请见Q4.2。finally用于异常机制，参见Q4.8。</p>
		<p>五、 面向对象篇</p>
		<p>Q5.1  extends和implements有什么不同？<br />答：对于class而言，extends用于（单）继承一个类（class），而implements用于实现一个接口（interf<br />ace）。<br />interface的引入是为了部分地提供多继承的功能。<br />在interface中只需声明方法头，而将方法体留给实现的class来做。<br />这些实现的class的实例完全可以当作interface的实例来对待。<br />在interface之间也可以声明为extends（多继承）的关系。<br />注意一个interface可以extends多个其他interface。</p>
		<p>Q5.2  java怎么实现多继承？<br />答：java不支持显式的多继承。<br />因为在显式多继承的语言例如c++中，会出现子类被迫声明祖先虚基类构造函数的问题，</p>
		<p>而这是违反面向对象的封装性原则的。<br />java提供了interface和implements关键字来部分地实现多继承。参见Q5.1。</p>
		<p>Q5.3 abstract是什么？<br />答：被声明为abstract的方法无需给出方法体，留给子类来实现。<br />而如果一个类中有abstract方法，那么这个类也必须声明为abstract。<br />被声明为abstract的类无法实例化，尽管它可以定义构造方法供子类使用。</p>
		<p>Q5.4 public,protected,private有什么不同？<br />答：这些关键字用于声明类和成员的可见性。<br />public成员可以被任何类访问，<br />protected成员限于自己和子类访问，<br />private成员限于自己访问。<br />Java还提供了第四种的默认可见性，一般称为package private，当没有任何public,protected,private修饰符时，成员<br />是同一包内可见。<br />类可以用public或默认来修饰。<br /></p>
		<p>Q5.5 Override和Overload有什么不同？<br />答：Override是指父类和子类之间方法的继承关系，这些方法有着相同的名称和参数类型<br />。<br />Overload是指同一个类中不同方法（可以在子类也可以在父类中定义）间的关系，<br />这些方法有着相同的名称和不同的参数类型。</p>
		<p>
				<br />Q5.6 我继承了一个方法，但现在我想调用在父类中定义的方法。<br />答：用super.xxx()可以在子类中调用父类方法。</p>
		<p>Q5.7 我想在子类的构造方法中调用父类的构造方法，该怎么办？<br />答：在子类构造方法的第一行调用super(...)即可。</p>
		<p>Q5.8 我在同一个类中定义了好几个构造方法并且想在一个构造方法中调用另一个。<br />答：在构造方法第一行调用this(...)。</p>
		<p>Q5.9 我没有定义构造方法会怎么样？<br />答：自动获得一个无参数的构造方法。</p>
		<p>Q5.10 我调用无参数的构造方法失败了。<br />答：如果你至少定义了一个构造方法，就不再有自动提供的无参数的构造方法了。<br />你需要另外显式定义一个无参数的构造方法。<br />另外一种可能是你的构造方法或者类不是public的，参见Q5.4了解java中的可见性。</p>
		<p>Q5.11 我该怎么定义类似于C++中的析构方法（destructor）？<br />答：提供一个void finalize()方法。在Garbarge Collector回收该对象时会调用该方法。</p>
		<p>注意实际上你很难判断一个对象会在什么时候被回收。作者从未感到需要用到该方法。</p>
		<p>
				<br />Q5.12 我想将一个父类对象转换成一个子类对象该怎么做？<br />答：强制类型转换。如<br />public void meth(A a)<br />{<br />B b = (B)a;<br />}<br />如果a实际上并不是B的实例，会抛出ClassCastException。所以请确保a确实是B的实例。</p>
		<p>
				<br />Q5.13 其实我不确定a是不是B的实例，能不能分情况处理？<br />答：可以使用instanceof操作符。例如<br />if( a instanceof B )<br />{<br />B b = (B)a;<br />}<br />else<br />{<br />...<br />}</p>
		<p>Q5.14 我在方法里修改了一个对象的值，但是退出方法后我发现这个对象的值没变！<br />答：很可能你把传入参数重赋了一个新对象，例如下列代码就会造成这种错误：<br />public void fun1(A a) //a是局部参数，指向了一个外在对象。<br />{<br />a = new A(); //a指向了一个新对象，和外在对象脱钩了。如果你要让a作为传出变量，<br />不要写这一句。<br />        a.setAttr(attr);//修改了新对象的值，外在对象没有被修改。<br />}<br />基本类型也会出现这种情况。例如：<br />public void fun2(int a)<br />{<br />a = 10;//只作用于本方法，外面的变量不会变化。<br />}</p>
		<p>六、java.util篇</p>
		<p>Q6.1 java能动态分配数组吗？<br />答：可以。例如int n = 3; Language[] myLanguages = new Language[n];</p>
		<p>Q6.2 我怎么知道数组的长度？<br />答：用length属性。如上例中的  myLanguages.length 就为 3。</p>
		<p>Q6.3 我还想让数组的长度能自动改变，能够增加/删除元素。<br />答：用顺序表--java.util.List接口。<br />你可以选择用ArrayList或是LinkedList，前者是数组实现，后者是链表实现。<br />例如：  List list = new ArrayList(); 或是 List list = new LinkedList();  。</p>
		<p>Q    什么是链表？为什么要有ArrayList和LinkedList两种List？<br />答：请补习数据结构。</p>
		<p>Q6.5 我想用队列/栈。<br />答：用java.util.LinkedList。</p>
		<p>Q6.6 我希望不要有重复的元素。<br />答：用集合--java.util.Set接口。例如：Set set = new HashSet()。</p>
		<p>Q6.7 我想遍历集合/Map。<br />答：用java.util.Iterator。参见API。</p>
		<p>Q6.8 我还要能够排序。<br />答：用java.util.TreeSet。例如：Set set = new TreeSet()。放进去的元素会自动排序<br />。<br />你需要为元素实现Comparable接口，还可能需要提供equals()方法，compareTo()方法，h<br />ashCode()方法。</p>
		<p>Q6.9 但是我想给数组排序。<br />答：java.util.Arrays类包含了sort等实用方法。</p>
		<p>Q6.10 我想按不同方式排序。<br />答：为每种方式定义一个实现了接口Comparator的排序类并和Arrays或TreeSet综合运用。</p>
		<p>
				<br />Q6.11 Map有什么用？<br />答：存储key-value的关键字-值对，你可以通过关键字来快速存取相应的值。</p>
		<p>Q6.12 set方法没问题，但是get方法返回的是Object。<br />答：强制类型转换成你需要的类型。参见Q5.12。</p>
		<p>Q6.13 ArrayList和Vector有什么不同？HashMap和Hashtable有什么不同？<br />答：ArrayList和HashMap是多线程不安全的，在多个线程中访问同一个ArrayList对象可能<br />会引起冲突并导致错误。<br />而Vector和Hashtable是多线程安全的，即使在多个线程中同时访问同一个Vector对象也不<br />会引起差错。<br />看起来我们更应该使用Vector和Hashtable，但是实际上Vector和Hashtable的性能太差，<br />所以如果你不在多线程中使用的话，还是应该用ArrayList和HashMap。</p>
		<p>Q6.14 我要获得一个随机数。<br />答：使用java.util.Random类。</p>
		<p>Q6.15 我比较两个String总是false，但是它们明明都是"abc" ！<br />答：比较String一定要使用equals或equalsIgnoreCase方法，不要使用 == ！<br />==比较的是两个引用（变量）是否指向了同一个对象，而不是比较其内容。</p>
		<p>Q6.16 我想修改一个String但是在String类中没找到编辑方法。<br />答：使用StringBuffer类。<br />String str = "......."; //待处理的字符串<br />StringBuffer buffer = new StringBuffer(str); //使用该字符串初始化一个StringBuf<br />fer<br />buffer.append("..."); //调用StringBuffer的相关API来编辑字符串<br />String str2 = buffer.toString(); //获得编辑后的字符串。<br />另外，如果你需要将多个字符串连接起来，请尽量避免使用+号直接连接，而是使用Strin<br />gBuffer.append()方法。</p>
		<p>Q6.17 我想处理日期/时间。<br />答：使用java.util.Date类。你可以使用java.text.SimpleDateFormat类来在String和Da<br />te间互相转换。<br />SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //规<br />定日期格式<br />Date date = formatter.parse("2003-07-26 18:30:35"); //将符合格式的String转换为<br />Date<br />String s = formatter.format(date); //将Date转换为符合格式的String<br />关于定义日期格式的详细信息请参见API。<br /> </p>
		<p>J2EE FAQ <br /> <br />目录：</p>
		<p>一、准备篇<br />Q1.1   什么是J2EE？它和普通的Java有什么不同？<br />Q1.2   J2EE好学吗？<br />Q1.3   J2EE有什么用？<br />Q1.4   学J2EE有前途吗？<br />Q1.5   据说J2EE的性能不如.NET好，是真的吗？<br />Q1.6   听你说了这么多，我想学着玩玩。<br />Q1.7   学习J2EE该怎么开始？<br />Q1.8   我下了一个J2EE服务器但是不会配置。<br />Q1.9   我发现你没有提到Tomcat。</p>
		<p>二、 Servlet/JSP篇<br />Q2.1   什么是Servlet？<br />Q2.2   我怎么获得Http请求里的参数？<br />Q2.3   我怎么返回结果？<br />Q2.4   sendRedirect()和forward()有什么不同？<br />Q2.5   我写了一个Servlet程序，怎么运行它？<br />Q2.6   EAR和WAR有什么不同？<br />Q2.7   EAR格式是怎样的？<br />Q2.8   WAR格式是怎样的？<br />Q2.9   我的普通HTML文件/JSP文件应当放在哪里？<br />Q2.10  我访问不到servlet，甚至连HTML文件都访问不到！<br />Q2.11  我能访问HTML但是访问不到servlet。<br />Q2.12  什么是JSP？它和Servlet有什么区别？<br />Q2.13  我的JSP显示的汉字是乱码。<br />Q2.14  为什么使用gb18030而不是gb2312？<br />Q2.15  在JSP里面怎么引用Java Bean。<br />Q2.16  我想在servlet间传递数据。<br />Q2.17  怎么调用cookie？<br />Q2.18  怎么在JSP里面实现文件下载？<br />Q2.19  怎么实现文件上传？<br />Q2.20  我想让页面自动刷新，比如聊天室。<br />Q2.21  我想让用户登录以后才能访问页面。<br />Q2.22  我想要能注册用户。<br />Q2.23  怎么在JSP中访问数据库？<br />Q2.24  什么是JSTL？</p>
		<p>一、准备篇</p>
		<p>Q1.1  什么是J2EE？它和普通的Java有什么不同？<br />答：J2EE全称为Java2 Platform, Enterprise Edition。<br />“J2EE平台本质上是一个分布式的服务器应用程序设计环境——一个Java环境，它提供了<br />：<br />·宿主应用的一个运行基础框架环境。<br />·一套用来创建应用的Java扩展API。”</p>
		<p>Q1.2  J2EE好学吗？<br />答：J2EE是很多技术的集合体，并且还在成长中。<br />你会遇到很多专有名词：比如(X)HTML，Servlet/JSP，JDBC，JMS，JNDI，EJB，XML，Web<br />Service……。<br />尤其是XML和Web Service正在快速成长。幸运的是，你不需要等到学会所有技术后再开始<br />编程。<br />大体上J2EE可以分成3个主要应用方式：Servlet/JSP，EJB，Web Service 和一些支撑技术<br />例如JDBC和JNDI。<br />你可以一个一个的学。</p>
		<p>Q1.3 J2EE有什么用？<br />答：用来建设大型的分布式企业级应用程序。或者用更时髦的名词说就是“电子商务”应<br />用程序。<br />这些企业可能大到拥有中心数据库服务器，Web服务器集群和遍布全国的办公终端，也可能<br />小到只不过想做一个网站。但是你肯定听过“杀鸡焉用牛刀”的古训。</p>
		<p>Q1.4 学J2EE有前途吗？<br />答：在这一市场目前只有一种技术可以和J2EE竞争，那就是Microsoft的.NET。<br />相对来说.NET要“新”一些而J2EE要“老”一些。这也意味着.NET更易用一点而J2EE更成<br />熟一点。<br />但是.NET只能用于Windows平台（Microsoft声称要开发C#在Linux上的虚拟机但是尚未兑现<br />该诺言）。<br />在过去几年，.NET的市场份额并不理想。不过Microsoft还有Longhorn这一杀手锏，鹿死谁<br />手还很难说。</p>
		<p>Q1.5 据说J2EE的性能不如.NET好，是真的吗？<br />答：在Sun公司提供的样例程序Pet Store上，Microsoft声称不如相同的.NET程序好。<br />而Sun公司反驳说这一程序不能真正体现J2EE的性能，并且指责Microsoft在数据库上做了<br />优化。<br />作者没有学习过.NET因而不能妄下断言。<br />无论如何，大型分布式程序中的性能瓶颈通常首先来自于错误的设计。</p>
		<p>Q1.6 听你说了这么多，我想学着玩玩。<br />答：除非你想靠它当饭吃或者作为技术储备，否则请不要浪费你的时间。<br />Flash要好玩得多。计算机游戏就更加好玩了。</p>
		<p>Q1.7 学习J2EE该怎么开始？<br />答：首先，下载一个免费的J2EE服务器。其次，去java.sun.com下载J2EE的API。第三，找<br />一本好的参考书。最后，找一个顺手的IDE。<br />J2EE服务器。你可以用Sun的J2EE SDK（免费），或者Weblogic（性能最好，但是太大，而<br />且作者不推荐盗版行为），<br />或者JBoss（免费，就是文档太少），或者JRun（开发版免费，作者用这个）。<br />参考书作者感觉Wrox的《J2EE服务器端高级编程》不错，但是太老（作者手头的是2001年<br />中文版）。<br />（似乎很多人不喜欢这本书......所以你得自己判断它是否适合你。）<br />你还需要去下载一些最新的技术资料（当然肯定是英文的）。<br />IDE如果你的机器配置够好（内存至少512M以上，256M或以下请勿考虑），可以用IBM的WS<br />AD，不然就继续用Eclipse或者其他。<br />你也可以经常去水木清华的Java版逛逛，但是在发贴前先看看精华区里有没有你要的答案<br />。</p>
		<p>Q1.8 我下了一个J2EE服务器但是不会配置。<br />答：请认真阅读随机指导文档，不同的服务器的配置都不一样，作者爱莫能助。</p>
		<p>Q1.9 我发现你没有提到Tomcat。<br />答：Tomcat只是一个Web服务器，更准确地说主要只是一个Web Container。<br />如果你想要学习EJB的话，Tomcat无法满足你的需要。</p>
		<p>二、 Servlet/JSP篇</p>
		<p>Q2.1 什么是Servlet？<br />答：一个Servlet是一个Java类。它处理Http(s)请求并作出响应，包括返回一个HTML页面<br />或转交给其他URL处理。<br />Servlet必须运行在一个Web Container例如Tomcat中。<br />Servlet必须是javax.servlet.http.HttpServlet的子类，<br />你可以继承doGet()或者doPost()方法，两者分别对应于Http(s)中的Get请求和Post请求。</p>
		<p>
				<br />Q2.2 我怎么获得Http请求里的参数？<br />答：HttpRequest的getParameter()方法。例如：String paramValue = request.getPara<br />meter("paramName");</p>
		<p>Q2.3 我怎么返回结果？<br />答：你可以利用相关API打开一个输出流，并向流中直接写入一个HTML页面。<br />但是作者完全不赞成这样做。一方面这样做会很罗嗦。<br />另一方面从Model-View-Controller模式（在《J2EE核心模式》中被归为Front Controlle<br />r模式）的观点来看，<br />你应当提供一些HTML或者JSP作为视图（view），而Servlet则根据请求参数决定转到哪一<br />个视图。<br />你可以利用response.sendRedirect(...)方法或request.getDispatcher(...).forward()<br />方法来实现。</p>
		<p>Q2.4 sendRedirect()和forward()有什么不同？<br />答：sendRedirect()是向浏览器发送一个redirect通知，浏览器向新的URL发送一个新的请<br />求。<br />而forward是在服务器端直接将请求转到新的URL，对于浏览器是透明的。<br />换而言之，sendRedirect()应当将共享数据放在session中，forward应当将共享数据放在<br />request中（当然你也可以放在session中，但放在request中可以有效减小session中的数<br />据量，从而改善性能）。<br />前者浏览器的地址栏显示的是新的URL，后者浏览器的地址栏显示的是Servlet的URL。<br />因而当刷新目标URL时，两者会造成一些差别。</p>
		<p>Q2.5 我写了一个Servlet程序，怎么运行它？<br />答：开发J2EE程序有一个部署（deploy）的概念，实际上是开发——部署——运行的三部<br />曲。<br />大多数服务器支持Hot deploy。你只需要在相应的Application目录（具体路径依赖于服务<br />器）下面<br />建立一个符合WAR或EAR格式（参见Q2.7，Q2.8）的目录，启动服务器，就可以通过浏览器<br />访问了。<br />特别的，你的Servlet的class文件应当放在/WEB-INF/classes目录中。<br />注意J2EE SDK不支持Hot deploy，你需要通过它的deploy tool来部署。<br />Tomcat只支持WAR格式。</p>
		<p>Q2.6 EAR和WAR有什么不同？<br />答：EAR是一个完整的J2EE应用程序，包括Web部分和EJB部分。<br />WAR只是其中的Web部分。</p>
		<p>Q2.7 EAR格式是怎样的？<br />答：一个EAR可以包含任意多个WAR或EJB JAR，并且包含一个META-INF的目录。<br />在/META-INF中包含了一个application.xml，其中描述了这个EAR包含哪些模块，以及安全<br />性配置。<br />细节请看参考书。</p>
		<p>Q2.8 WAR格式是怎样的？<br />答：一个WAR包含一个WEB-INF的目录，这个目录下包含classes目录，lib目录和web.xml。</p>
		<p>/WEB-INF/classes存放按package组织的class文件，/WEB-INF/lib目录存放jar文件，<br />web.xml描述了很多东西，请读参考书。</p>
		<p>Q2.9 我的普通HTML文件/JSP文件应当放在哪里？<br />答：放在除了/WEB-INF以外的其他地方。</p>
		<p>感谢antegg网友对于安全性的提醒：<br />如果你想直接用<a href="http://url/***.jsp">http://url/***.jsp</a>的方式来访问，就要像上面说得那样放。<br />但是这样的做法是不安全的，安全的做法是把所有的JSP页面放在/WEB-INF目录下面，并且</p>
		<p>通过WEB-CONTAINER来访问。</p>
		<p>作者意见:<br />我更喜欢用filter来做安全性检查。<br />在MVC模式中，JSP只是一个视图而已，一般无需特别担忧安全性。和普通的html放在一起<br />也利于维护。</p>
		<p>Q2.10 我访问不到servlet，甚至连HTML文件都访问不到！<br />答：<br />第一你没启动服务器。<br />第二你敲错了端口。<br />第三你没有正确配置context-path。<br />第四你的服务器不支持auto reload或者你关闭了这一选项，你得重启服务器或重新部署W<br />AR。<br />第五确认你没有把HTML放在/WEB-INF目录下，那是访问不到的。</p>
		<p>Q2.11 我能访问HTML但是访问不到servlet。<br />答：请检查你的web.xml文件。确保你正确定义了&lt;servlet&gt;和&lt;servlet-mapping&gt;元素。</p>
		<p>前者标识了一个servlet，后者将一个相对于context-path的URL映射到一个servlet。<br />在Tomcat中你可以通过/context-path/servlet/package/servletname的形式访问servlet<br />，<br />但是这只是Tomcat的便捷访问方式，并不是正式规范。<br />细节请看参考书。</p>
		<p>Q2.12  什么是JSP？它和Servlet有什么区别？<br />答：你可以将JSP当做一个可扩充的HTML来对待。<br />虽然在本质上JSP文件会被服务器自动翻译为相应的Servlet来执行。<br />可以说Servlet是面向Java程序员而JSP是面向HTML程序员的，除此之外两者功能完全等价<br />。</p>
		<p>Q2.13  我的JSP显示的汉字是乱码。<br />答：在你的JSP开头加上一行 &lt;%@ page contentType="text/html; charset=gb18030"%&gt;</p>
		<p>如果你已经声明了page我想你知道该怎么修改。</p>
		<p>Q2.14  为什么使用gb18030而不是gb2312？<br />答：gb18030是继gb2312之后的下一代汉字编码标准，最终将过渡到Unicode。</p>
		<p>Q2.15  在JSP里面怎么引用Java Bean。<br />答：首先，确认你要引用的类在/WEB-INF/classes下或在/WEB-INF/lib的某个jar内。<br />其次，在JSP里加一行 &lt;jsp:useBean id="..." scope="..." class="..."/&gt;<br />具体解释请看参考书。</p>
		<p>Q2.16  我想在servlet间传递数据。<br />答：利用session。在Servlet/JSP中，你可以在4个地方保存数据。<br />1) page，本页面。<br />2) session，用来存放客户相关的信息，比如购物车，对应接口为javax.servlet.http.H<br />ttpSession。<br />session机制实际上是cookie和URL Rewriting的抽象，服务器会自动使用cookie或URL Re<br />writing来实现。<br />3) request，可以在forward()时传递信息，对应接口为javax.servlet.http.HttpReques<br />t。<br />4) application，或称context，存放全局信息，对应接口为javax.servlet.ServletCont<br />ext。</p>
		<p>Q2.17  怎么调用cookie？<br />答：作者建议使用session，你总是会遇到某些禁用cookie的用户。这时session会自动使<br />用URL重写来实现。</p>
		<p>Q2.18  怎么在JSP里面实现文件下载？<br />答：实际上这是一个HTML的问题。答案是一个超链接&lt;a&gt;。</p>
		<p>Q2.19  怎么实现文件上传？<br />答：客户端是HTML问题，在form中设置method为post，enctype为multi-part/form-data，<br />加一个&lt;input type="file"&gt;。<br />而在接收的servlet中只是一个I/O问题，你可以使用jakarta的file-upload库。</p>
		<p>Q2.20  我想让页面自动刷新，比如聊天室。<br />答：这是一个HTML问题，在&lt;head&gt;部分中加一条&lt;meta http-equiv="refresh" content="<br />5" url="..."&gt;。<br />这是所谓的Client-pull，客户端刷新技术。<br />相对的还有Server-push，服务器端刷新技术，但是这一技术由于要占用服务器端资源而会<br />在大量访问时出现瓶颈现象，参见<a href="http://216.239.33.104/search?q=cache:autUfoakirY">http://216.239.33.104/search?q=cache:autUfoakirY</a><br />J:www.kfunigraz.ac.at/edvndwww/books/books/javaenterprise/servlet/ch06_03.htm+<br />server-push+servlet&amp;hl=zh-CN&amp;ie=UTF-8</p>
		<p>Q2.21  我想让用户登录以后才能访问页面。<br />答：使用声明式安全措施。<br />你只需要在web.xml中定义安全角色（Role），并定义受保护的URL集合只能由特定Role访<br />问。<br />大多数服务器支持基于数据库的用户映射，你只要在相应数据库中建立两张表并配置服务<br />器就可以了。<br />注意J2EE SDK不支持基于数据库的用户映射。<br />细节请看参考书和服务器文档。<br />不过在商业环境中，J2EE所提供的声明式安全措施仍然偏弱。一般商业程序会使用数据库<br />存储user-role-privilege模型来达到安全性要求，细节请询问你的构架设计师。</p>
		<p>Q2.22  我想要能注册用户。<br />答：参看Q2.21。在接受注册请求的Servlet中执行写入数据库操作即可。</p>
		<p>Q2.23  怎么在JSP中访问数据库？<br />答：标准做法是使用DAO模式，定义一个Java bean来访问数据库并在JSP中使用。<br />然而，当你的数据库模式很简单时，你可以使用JSTL中的&lt;sql:query&gt;标签来快速访问。</p>
		<p>在一般的J2EE项目中，JSP处于表示层（展现层），需要先后通过业务层和集成层才会访问<br />到数据库，所以这个问题确实只会在很小的程序中才会遇到。</p>
				Q2.24  什么是JSTL？<br />答：JSTL是Jsp Standard Tag Library的缩写。这是一组通用标签并将成为JSP 2.0的一部<br />分。<br />其中包含赋值&lt;c:set&gt;，分支&lt;c:if&gt;，循环&lt;c:forEach&gt;，查询数据库&lt;sql:query&gt;，更新数<br />据库&lt;sql:update&gt;<br />等。目前你需要像添加自定义标签库一样来添加JSTL，但是可以预计JSP 2.0会将JSTL作为<br />组成部分。<br />标签库可以在<a href="http://jakarta.apache.org/">http://jakarta.apache.org</a>下载。注意JSTL需要在支持JSP 1.2或更高版本<br />的容器下运行。<br />帮助文件可以阅读sun的JSTL正式规范<img src ="http://www.blogjava.net/ltc603/aggbug/63899.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-08-16 14:43 <a href="http://www.blogjava.net/ltc603/archive/2006/08/16/63899.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>string和stringbuffer区别--转载邢红瑞 的blog</title><link>http://www.blogjava.net/ltc603/archive/2006/03/09/34555.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Thu, 09 Mar 2006 13:45:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/03/09/34555.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/34555.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/03/09/34555.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/comm