﻿<?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-稻草软件(strawsoft)-文章分类-转帖区</title><link>http://www.blogjava.net/strawsoft/category/38157.html</link><description>Java开发</description><language>zh-cn</language><lastBuildDate>Mon, 23 Mar 2009 18:29:16 GMT</lastBuildDate><pubDate>Mon, 23 Mar 2009 18:29:16 GMT</pubDate><ttl>60</ttl><item><title>Java 理论与实践: 正确使用 Volatile 变量(转)</title><link>http://www.blogjava.net/strawsoft/articles/261164.html</link><dc:creator>strawsoft</dc:creator><author>strawsoft</author><pubDate>Sat, 21 Mar 2009 02:20:00 GMT</pubDate><guid>http://www.blogjava.net/strawsoft/articles/261164.html</guid><wfw:comment>http://www.blogjava.net/strawsoft/comments/261164.html</wfw:comment><comments>http://www.blogjava.net/strawsoft/articles/261164.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/strawsoft/comments/commentRss/261164.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/strawsoft/services/trackbacks/261164.html</trackback:ping><description><![CDATA[<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td width="100%">
            <h1><span style="color: #999999">Java 理论与实践: </span>正确使用 Volatile 变量</h1>
            <p id="subtitle"><em>volatile 变量使用指南 </em></p>
            <img class="display-img" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="1" /></td>
            <td class="no-print" width="192"></td>
        </tr>
    </tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
            <td width="100%">
            <table class="no-print" cellspacing="0" cellpadding="0" width="160" align="right" border="0">
                <tbody>
                    <tr>
                        <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
                        <td><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- this content will be automatically generated across all content areas --><!--end RESERVED FOR FUTURE USE INCLUDE FILES--><br />
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>级别： 中级</p>
            <p><a href="http://www.ibm.com/developerworks/cn/java/j-jtp06197.html#author" cmimpressionsent="1">Brian Goetz</a> (<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#98;&#114;&#105;&#97;&#110;&#46;&#103;&#111;&#101;&#116;&#122;&#64;&#115;&#117;&#110;&#46;&#99;&#111;&#109;&#63;&#115;&#117;&#98;&#106;&#101;&#99;&#116;&#61;&#27491;&#30830;&#20351;&#29992;&#32;&#86;&#111;&#108;&#97;&#116;&#105;&#108;&#101;&#32;&#21464;&#37327;" cmimpressionsent="1">brian.goetz@sun.com</a>), 高级工程师, Sun Microsystems<br />
            </p>
            <p>2007 年 7 月 05 日</p>
            <blockquote>Java&#8482; 语言包含两种内在的同步机制：同步块（或方法）和 volatile 变量。这两种机制的提出都是为了实现代码线程的安全性。其中 Volatile 变量的同步性较差（但有时它更简单并且开销更低），而且其使用也更容易出错。在这期的 <em>Java 理论与实践</em> 中，Brian Goetz 将介绍几种正确使用 volatile 变量的模式，并针对其适用性限制提出一些建议。 </blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
            <p>Java 语言中的 volatile 变量可以被看作是一种 &#8220;程度较轻的 <code>synchronized</code>&#8221;；与 <code>synchronized</code> 块相比，volatile 变量所需的编码较少，并且运行时开销也较少，但是它所能实现的功能也仅是 <code>synchronized</code> 的一部分。本文介绍了几种有效使用 volatile 变量的模式，并强调了几种不适合使用 volatile 变量的情形。 </p>
            <p>锁提供了两种主要特性：<em>互斥（mutual exclusion）</em> 和<em>可见性（visibility）</em>。互斥即一次只允许一个线程持有某个特定的锁，因此可使用该特性实现对共享数据的协调访问协议，这样，一次就只有一个线程能够使用该共享数据。可见性要更加复杂一些，它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的 —— 如果没有同步机制提供的这种可见性保证，线程看到的共享变量可能是修改前的值或不一致的值，这将引发许多严重问题。 </p>
            <p><a name="1.0"><span class="atitle">Volatile 变量</span></a></p>
            <p>Volatile 变量具有 <code>synchronized</code> 的可见性特性，但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。Volatile 变量可用于提供线程安全，但是只能应用于非常有限的一组用例：多个变量之间或者某个变量的当前值与修改后值之间没有约束。因此，单独使用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关的不变式（Invariants）的类（例如 &#8220;start &lt;=end&#8221;）。 </p>
            <p>出于简易性或可伸缩性的考虑，您可能倾向于使用 volatile 变量而不是锁。当使用 volatile 变量而非锁时，某些习惯用法（idiom）更加易于编码和阅读。此外，volatile 变量不会像锁那样造成线程阻塞，因此也很少造成可伸缩性问题。在某些情况下，如果读操作远远大于写操作，volatile 变量还可以提供优于锁的性能优势。 </p>
            <p><a name="1.1"><span class="smalltitle">正确使用 volatile 变量的条件</span></a></p>
            <p>您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全，必须同时满足下面两个条件：
            <ul>
                <li>对变量的写操作不依赖于当前值。
                <li>该变量没有包含在具有其他变量的不变式中。 </li>
            </ul>
            <p>&nbsp;</p>
            <p>实际上，这些条件表明，可以被写入 volatile 变量的这些有效值独立于任何程序的状态，包括变量的当前状态。 </p>
            <p>第一个条件的限制使 volatile 变量不能用作线程安全计数器。虽然增量操作（<code>x++</code>）看上去类似一个单独操作，实际上它是一个由读取－修改－写入操作序列组成的组合操作，必须以原子方式执行，而 volatile 不能提供必须的原子特性。实现正确的操作需要使 <code>x</code> 的值在操作期间保持不变，而 volatile 变量无法实现这点。（然而，如果将值调整为只从单个线程写入，那么可以忽略第一个条件。） </p>
            <p>大多数编程情形都会与这两个条件的其中之一冲突，使得 volatile 变量不能像 <code>synchronized</code> 那样普遍适用于实现线程安全。清单 1 显示了一个非线程安全的数值范围类。它包含了一个不变式 —— 下界总是小于或等于上界。 </p>
            <br />
            <a name="listing1"><strong>清单 1. 非线程安全的数值范围类</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">
                        @NotThreadSafe
                        public class NumberRange {
                        private int lower, upper;
                        public int getLower() { return lower; }
                        public int getUpper() { return upper; }
                        public void setLower(int value) {
                        if (value &gt; upper)
                        throw new IllegalArgumentException(...);
                        lower = value;
                        }
                        public void setUpper(int value) {
                        if (value &lt; lower)
                        throw new IllegalArgumentException(...);
                        upper = value;
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>这种方式限制了范围的状态变量，因此将 <code>lower</code> 和 upper 字段定义为 volatile 类型不能够充分实现类的线程安全；从而仍然需要使用同步。否则，如果凑巧两个线程在同一时间使用不一致的值执行 <code>setLower</code> 和 <code>setUpper</code> 的话，则会使范围处于不一致的状态。例如，如果初始状态是 <code>(0, 5)</code>，同一时间内，线程 A 调用 <code>setLower(4)</code> 并且线程 B 调用 <code>setUpper(3)</code>，显然这两个操作交叉存入的值是不符合条件的，那么两个线程都会通过用于保护不变式的检查，使得最后的范围值是 <code>(4, 3)</code> —— 一个无效值。至于针对范围的其他操作，我们需要使 <code>setLower()</code> 和 <code>setUpper()</code> 操作原子化 —— 而将字段定义为 volatile 类型是无法实现这一目的的。 </p>
            <p><a name="1.2"><span class="smalltitle">性能考虑</span></a></p>
            <p>使用 volatile 变量的主要原因是其简易性：在某些情形下，使用 volatile 变量要比使用相应的锁简单得多。使用 volatile 变量次要原因是其性能：某些情况下，volatile 变量同步机制的性能要优于锁。 </p>
            <p>很难做出准确、全面的评价，例如 &#8220;X 总是比 Y 快&#8221;，尤其是对 JVM 内在的操作而言。（例如，某些情况下 VM 也许能够完全删除锁机制，这使得我们难以抽象地比较 <code>volatile</code> 和 <code>synchronized</code> 的开销。）就是说，在目前大多数的处理器架构上，volatile 读操作开销非常低 —— 几乎和非 volatile 读操作一样。而 volatile 写操作的开销要比非 volatile 写操作多很多，因为要保证可见性需要实现内存界定（Memory Fence），即便如此，volatile 的总开销仍然要比锁获取低。 </p>
            <p>volatile 操作不会像锁一样造成阻塞，因此，在能够安全使用 volatile 的情况下，volatile 可以提供一些优于锁的可伸缩特性。如果读操作的次数要远远超过写操作，与锁相比，volatile 变量通常能够减少同步的性能开销。 </p>
            <p><a name="2.0"><span class="atitle">正确使用 volatile 的模式</span></a></p>
            <p>很多并发性专家事实上往往引导用户远离 volatile 变量，因为使用它们要比使用锁更加容易出错。然而，如果谨慎地遵循一些良好定义的模式，就能够在很多场合内安全地使用 volatile 变量。要始终牢记使用 volatile 的限制 —— <span style="color: #ff0000">只有在状态真正独立于程序内其他内容时才能使用 volatile </span>—— 这条规则能够避免将这些模式扩展到不安全的用例。 </p>
            <p><a name="2.1"><span class="smalltitle">模式 #1：状态标志</span></a></p>
            <p>也许实现 volatile 变量的规范使用仅仅是使用一个布尔状态标志，用于指示发生了一个重要的一次性事件，例如完成初始化或请求停机。 </p>
            <p>很多应用程序包含了一种控制结构，形式为 &#8220;在还没有准备好停止程序时再执行一些工作&#8221;，如清单 2 所示： </p>
            <br />
            <a name="listing2"><strong>清单 2. 将 volatile 变量作为状态标志使用</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">
                        volatile boolean shutdownRequested;
                        ...
                        public void shutdown() { shutdownRequested = true; }
                        public void doWork() {
                        while (!shutdownRequested) {
                        // do stuff
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>很可能会从循环外部调用 <code>shutdown()</code> 方法 —— 即在另一个线程中 —— 因此，需要执行某种同步来确保正确实现 <code>shutdownRequested</code> 变量的可见性。（可能会从 JMX 侦听程序、GUI 事件线程中的操作侦听程序、通过 RMI 、通过一个 Web 服务等调用）。然而，使用 <code>synchronized</code> 块编写循环要比使用清单 2 所示的 volatile 状态标志编写麻烦很多。由于 volatile 简化了编码，并且状态标志并不依赖于程序内任何其他状态，因此此处非常适合使用 volatile。 </p>
            <p>这种类型的状态标记的一个公共特性是：通常只有一种状态转换；<code>shutdownRequested</code> 标志从 <code>false</code> 转换为 <code>true</code>，然后程序停止。这种模式可以扩展到来回转换的状态标志，但是只有在转换周期不被察觉的情况下才能扩展（从 <code>false</code> 到 <code>true</code>，再转换到 <code>false</code>）。此外，还需要某些原子状态转换机制，例如原子变量。 </p>
            <p><a name="2.2"><span class="smalltitle">模式 #2：一次性安全发布（one-time safe publication）</span></a></p>
            <p>缺乏同步会导致无法实现可见性，这使得确定何时写入对象引用而不是原语值变得更加困难。在缺乏同步的情况下，可能会遇到某个对象引用的更新值（由另一个线程写入）和该对象状态的旧值同时存在。（这就是造成著名的双重检查锁定（double-checked-locking）问题的根源，其中对象引用在没有同步的情况下进行读操作，产生的问题是您可能会看到一个更新的引用，但是仍然会通过该引用看到不完全构造的对象）。 </p>
            <p>实现安全发布对象的一种技术就是将对象引用定义为 volatile 类型。清单 3 展示了一个示例，其中后台线程在启动阶段从数据库加载一些数据。其他代码在能够利用这些数据时，在使用之前将检查这些数据是否曾经发布过。 </p>
            <br />
            <a name="listing3"><strong>清单 3. 将 volatile 变量用于一次性安全发布</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">
                        public class BackgroundFloobleLoader {
                        public volatile Flooble theFlooble;
                        public void initInBackground() {
                        // do lots of stuff
                        theFlooble = new Flooble();  // this is the only write to theFlooble
                        }
                        }
                        public class SomeOtherClass {
                        public void doWork() {
                        while (true) {
                        // do some stuff...
                        // use the Flooble, but only if it is ready
                        if (floobleLoader.theFlooble != null)
                        doSomething(floobleLoader.theFlooble);
                        }
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>如果 <code>theFlooble</code> 引用不是 volatile 类型，<code>doWork()</code> 中的代码在解除对 <code>theFlooble</code> 的引用时，将会得到一个不完全构造的 <code>Flooble</code>。 </p>
            <p>该模式的一个必要条件是：被发布的对象必须是线程安全的，或者是有效的不可变对象（有效不可变意味着对象的状态在发布之后永远不会被修改）。volatile 类型的引用可以确保对象的发布形式的可见性，但是如果对象的状态在发布后将发生更改，那么就需要额外的同步。 </p>
            <p><a name="2.3"><span class="smalltitle">模式 #3：独立观察（independent observation）</span></a></p>
            <p>安全使用 volatile 的另一种简单模式是：定期 &#8220;发布&#8221; 观察结果供程序内部使用。例如，假设有一种环境传感器能够感觉环境温度。一个后台线程可能会每隔几秒读取一次该传感器，并更新包含当前文档的 volatile 变量。然后，其他线程可以读取这个变量，从而随时能够看到最新的温度值。 </p>
            <p>使用该模式的另一种应用程序就是收集程序的统计信息。清单 4 展示了身份验证机制如何记忆最近一次登录的用户的名字。将反复使用 <code>lastUser</code> 引用来发布值，以供程序的其他部分使用。 </p>
            <br />
            <a name="listing4"><strong>清单 4. 将 volatile 变量用于多个独立观察结果的发布</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">
                        public class UserManager {
                        public volatile String lastUser;
                        public boolean authenticate(String user, String password) {
                        boolean valid = passwordIsValid(user, password);
                        if (valid) {
                        User u = new User();
                        activeUsers.add(u);
                        lastUser = user;
                        }
                        return valid;
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>该模式是前面模式的扩展；将某个值发布以在程序内的其他地方使用，但是与一次性事件的发布不同，这是一系列独立事件。这个模式要求被发布的值是有效不可变的 —— 即值的状态在发布后不会更改。使用该值的代码需要清楚该值可能随时发生变化。 </p>
            <p><a name="2.4"><span class="smalltitle">模式 #4：&#8220;volatile bean&#8221; 模式</span></a></p>
            <p>volatile bean 模式适用于将 JavaBeans 作为&#8220;荣誉结构&#8221;使用的框架。在 volatile bean 模式中，JavaBean 被用作一组具有 getter 和/或 setter 方法 的独立属性的容器。volatile bean 模式的基本原理是：很多框架为易变数据的持有者（例如 <code>HttpSession</code>）提供了容器，但是放入这些容器中的对象必须是线程安全的。 </p>
            <p>在 volatile bean 模式中，JavaBean 的所有数据成员都是 volatile 类型的，并且 getter 和 setter 方法必须非常普通 —— 除了获取或设置相应的属性外，不能包含任何逻辑。此外，对于对象引用的数据成员，引用的对象必须是有效不可变的。（这将禁止具有数组值的属性，因为当数组引用被声明为 <code>volatile</code> 时，只有引用而不是数组本身具有 volatile 语义）。对于任何 volatile 变量，不变式或约束都不能包含 JavaBean 属性。清单 5 中的示例展示了遵守 volatile bean 模式的 JavaBean： </p>
            <br />
            <a name="listing5"><strong>清单 5. 遵守 volatile bean 模式的 Person 对象</strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">
                        @ThreadSafe
                        public class Person {
                        private volatile String firstName;
                        private volatile String lastName;
                        private volatile int age;
                        public String getFirstName() { return firstName; }
                        public String getLastName() { return lastName; }
                        public int getAge() { return age; }
                        public void setFirstName(String firstName) {
                        this.firstName = firstName;
                        }
                        public void setLastName(String lastName) {
                        this.lastName = lastName;
                        }
                        public void setAge(int age) {
                        this.age = age;
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p><a name="3.0"><span class="atitle">volatile 的高级模式</span></a></p>
            <p>前面几节介绍的模式涵盖了大部分的基本用例，在这些模式中使用 volatile 非常有用并且简单。这一节将介绍一种更加高级的模式，在该模式中，volatile 将提供性能或可伸缩性优势。 </p>
            <p>volatile 应用的的高级模式非常脆弱。因此，必须对假设的条件仔细证明，并且这些模式被严格地封装了起来，因为即使非常小的更改也会损坏您的代码！同样，使用更高级的 volatile 用例的原因是它能够提升性能，确保在开始应用高级模式之前，真正确定需要实现这种性能获益。需要对这些模式进行权衡，放弃可读性或可维护性来换取可能的性能收益 —— 如果您不需要提升性能（或者不能够通过一个严格的测试程序证明您需要它），那么这很可能是一次糟糕的交易，因为您很可能会得不偿失，换来的东西要比放弃的东西价值更低。 </p>
            <p><a name="3.1"><span class="smalltitle">模式 #5：开销较低的读－写锁策略</span></a></p>
            <p>目前为止，您应该了解了 volatile 的功能还不足以实现计数器。因为 <code>++x</code> 实际上是三种操作（读、添加、存储）的简单组合，如果多个线程凑巧试图同时对 volatile 计数器执行增量操作，那么它的更新值有可能会丢失。 </p>
            <p>然而，如果读操作远远超过写操作，您可以结合使用内部锁和 volatile 变量来减少公共代码路径的开销。清单 6 中显示的线程安全的计数器使用 <code>synchronized</code> 确保增量操作是原子的，并使用 <code>volatile</code> 保证当前结果的可见性。如果更新不频繁的话，该方法可实现更好的性能，因为读路径的开销仅仅涉及 volatile 读操作，这通常要优于一个无竞争的锁获取的开销。 </p>
            <br />
            <a name="listing6"><strong>清单 6. 结合使用 volatile 和 synchronized 实现 &#8220;开销较低的读－写锁&#8221; </strong></a><br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">
                        @ThreadSafe
                        public class CheesyCounter {
                        // Employs the cheap read-write lock trick
                        // All mutative operations MUST be done with the 'this' lock held
                        @GuardedBy("this") private <span class="boldcode">volatile</span> int value;
                        public int getValue() { return value; }
                        public <span class="boldcode">synchronized</span> int increment() {
                        return value++;
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>之所以将这种技术称之为 &#8220;开销较低的读－写锁&#8221; 是因为您使用了不同的同步机制进行读写操作。因为本例中的写操作违反了使用 volatile 的第一个条件，因此不能使用 volatile 安全地实现计数器 —— 您必须使用锁。然而，您可以在读操作中使用 volatile 确保当前值的<em>可见性</em>，因此可以使用锁进行所有变化的操作，使用 volatile 进行只读操作。其中，锁一次只允许一个线程访问值，volatile 允许多个线程执行读操作，因此当使用 volatile 保证读代码路径时，要比使用锁执行全部代码路径获得更高的共享度 —— 就像读－写操作一样。然而，要随时牢记这种模式的弱点：如果超越了该模式的最基本应用，结合这两个竞争的同步机制将变得非常困难。 </p>
            <p><a name="4.0"><span class="atitle">结束语</span></a></p>
            <p>与锁相比，Volatile 变量是一种非常简单但同时又非常脆弱的同步机制，它在某些情况下将提供优于锁的性能和伸缩性。如果严格遵循 volatile 的使用条件 —— 即变量真正独立于其他变量和自己以前的值 —— 在某些情况下可以使用 <code>volatile</code> 代替 <code>synchronized</code> 来简化代码。然而，使用 <code>volatile</code> 的代码往往比使用锁的代码更加容易出错。本文介绍的模式涵盖了可以使用 <code>volatile</code> 代替 <code>synchronized</code> 的最常见的一些用例。遵循这些模式（注意使用时不要超过各自的限制）可以帮助您安全地实现大多数用例，使用 volatile 变量获得更佳性能。 <br />
            <br />
            </p>
            </td>
        </tr>
    </tbody>
</table>
  <img src ="http://www.blogjava.net/strawsoft/aggbug/261164.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/strawsoft/" target="_blank">strawsoft</a> 2009-03-21 10:20 <a href="http://www.blogjava.net/strawsoft/articles/261164.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA EXCEL API简介</title><link>http://www.blogjava.net/strawsoft/articles/258499.html</link><dc:creator>strawsoft</dc:creator><author>strawsoft</author><pubDate>Sun, 08 Mar 2009 13:53:00 GMT</pubDate><guid>http://www.blogjava.net/strawsoft/articles/258499.html</guid><wfw:comment>http://www.blogjava.net/strawsoft/comments/258499.html</wfw:comment><comments>http://www.blogjava.net/strawsoft/articles/258499.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/strawsoft/comments/commentRss/258499.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/strawsoft/services/trackbacks/258499.html</trackback:ping><description><![CDATA[<p>Java Excel是一开放源码项目，通过它Java开发人员可以读取Excel文件的内容、创建新的Excel文件、更新已经存在的Excel文件。使用该API非Windows操作系统也可以通过纯Java应用来处理Excel数据表。因为是使用Java编写的，所以我们在Web应用中可以通过JSP、Servlet来调用API实现对Excel数据表的访问。<br />
<br />
提供以下功能：</p>
<ul xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <li>从Excel 95、97、2000等格式的文件中读取数据；
    <li>读取Excel公式（可以读取Excel 97以后的公式）；
    <li>生成Excel数据表（格式为Excel 97）；
    <li>支持字体、数字、日期的格式化；
    <li>支持单元格的阴影操作，以及颜色操作；
    <li>修改已经存在的数据表；
    <li>能够读取图表信息</li>
</ul>
<p>1.<a name="2"><span class="atitle2">应用示例</span></a>：<br />
包括从Excel读取数据,生成新的Excel，以及修改Excel<br />
<font face="Courier New">package common.util;</font></p>
<p><font face="Courier New">import jxl.*;<br />
import jxl.format.UnderlineStyle;<br />
import jxl.write.*;<br />
import jxl.write.Number;<br />
import jxl.write.Boolean;</font></p>
<p><font face="Courier New">import java.io.*;</font></p>
<p><font face="Courier New">/**<br />
&nbsp;* Created by IntelliJ IDEA.<br />
&nbsp;* User: xl<br />
&nbsp;* Date: 2005-7-17<br />
&nbsp;* Time: 9:33:22<br />
&nbsp;* To change this template use File | Settings | File Templates.<br />
&nbsp;*/<br />
public class ExcelHandle<br />
{<br />
&nbsp;&nbsp;&nbsp; public ExcelHandle()<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp; }</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 读取Excel<br />
&nbsp;&nbsp;&nbsp;&nbsp; *<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @param filePath<br />
&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp; public static void readExcel(String filePath)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InputStream is = new FileInputStream(filePath);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Workbook rwb = Workbook.getWorkbook(is);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Sheet st = rwb.getSheet("0")这里有两种方法获取sheet表,1为名字，而为下标，从0开始<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sheet st = rwb.getSheet("original");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Cell c00 = st.getCell(0,0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //通用的获取cell值的方式,返回字符串<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String strc00 = c00.getContents();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //获得cell具体类型值的方式<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(c00.getType() == CellType.LABEL)<br />
&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;&nbsp; LabelCell labelc00 = (LabelCell)c00;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strc00 = labelc00.getString();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //输出<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(strc00);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //关闭<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rwb.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(Exception e)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 输出Excel<br />
&nbsp;&nbsp;&nbsp;&nbsp; *<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @param os<br />
&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp; public static void writeExcel(OutputStream os)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 只能通过API提供的工厂方法来创建Workbook，而不能使用WritableWorkbook的构造函数，<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 因为类WritableWorkbook的构造函数为protected类型<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * method(1)直接从目标文件中读取WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * method(2)如下实例所示 将WritableWorkbook直接写入到输出流</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableWorkbook wwb = Workbook.createWorkbook(os);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //创建Excel工作表 指定名称和位置<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableSheet ws = wwb.createSheet("Test Sheet 1",0);</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //**************往工作表中添加数据*****************</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //1.添加Label对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label label = new Label(0,0,"this is a label test");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ws.addCell(label);</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //添加带有字型Formatting对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableFont wf = new WritableFont(WritableFont.TIMES,18,WritableFont.BOLD,true);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableCellFormat wcf = new WritableCellFormat(wf);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label labelcf = new Label(1,0,"this is a label test",wcf);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ws.addCell(labelcf);</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //添加带有字体颜色的Formatting对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableFont wfc = new WritableFont(WritableFont.ARIAL,10,WritableFont.NO_BOLD,false,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.RED);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableCellFormat wcfFC = new WritableCellFormat(wfc);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label labelCF = new Label(1,0,"This is a Label Cell",wcfFC);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ws.addCell(labelCF);</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //2.添加Number对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Number labelN = new Number(0,1,3.1415926);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ws.addCell(labelN);</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //添加带有formatting的Number对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NumberFormat nf = new NumberFormat("#.##");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableCellFormat wcfN = new WritableCellFormat(nf);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Number labelNF = new jxl.write.Number(1,1,3.1415926,wcfN);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ws.addCell(labelNF);</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //3.添加Boolean对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Boolean labelB = new jxl.write.Boolean(0,2,false);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ws.addCell(labelB);</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //4.添加DateTime对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jxl.write.DateTime labelDT = new jxl.write.DateTime(0,3,new java.util.Date());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ws.addCell(labelDT);</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //添加带有formatting的DateFormat对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DateFormat df = new DateFormat("dd MM yyyy hh:mm:ss");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableCellFormat wcfDF = new WritableCellFormat(df);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DateTime labelDTF = new DateTime(1,3,new java.util.Date(),wcfDF);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ws.addCell(labelDTF);<br />
</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //添加图片对象,jxl只支持png格式图片<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; File image = new File("f:\\2.png");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableImage wimage = new WritableImage(0,1,2,2,image);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ws.addImage(wimage);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //写入工作表<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wwb.write();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wwb.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(Exception e)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; /**<br />
&nbsp;&nbsp;&nbsp;&nbsp; * 拷贝后,进行修改,其中file1为被copy对象，file2为修改后创建的对象<br />
&nbsp;&nbsp;&nbsp;&nbsp; *&nbsp;尽单元格原有的格式化修饰是不能去掉的，我们还是可以将新的单元格修饰加上去，<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;以使单元格的内容以不同的形式表现<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @param file1<br />
&nbsp;&nbsp;&nbsp;&nbsp; * @param file2<br />
&nbsp;&nbsp;&nbsp;&nbsp; */<br />
&nbsp;&nbsp;&nbsp; public static void modifyExcel(File file1,File file2)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Workbook rwb = Workbook.getWorkbook(file1);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableWorkbook wwb = Workbook.createWorkbook(file2,rwb);//copy<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableSheet ws = wwb.getSheet(0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WritableCell wc = ws.getWritableCell(0,0);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //判断单元格的类型,做出相应的转换<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(wc.getType == CellType.LABEL)<br />
&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;&nbsp; Label label = (Label)wc;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; label.setString("The value has been modified");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wwb.write();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wwb.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rwb.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(Exception e)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;//测试<br />
&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//读Excel<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ExcelHandle.readExcel("f:/testRead.xls");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//输出Excel<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; File fileWrite = new File("f:/testWrite.xls");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fileWrite.createNewFile();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OutputStream os = new FileOutputStream(fileWrite);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ExcelHandle.writeExcel(os);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//修改Excel<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ExcelHandle.modifyExcel(new file(""),new File(""));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(Exception e)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
}<br />
<br />
</font><font face="Times New Roman">2.在jsp中做相关测试，创建一个writeExcel.jsp<br />
&lt;%<br />
<font face="Courier New">response.reset();//清除Buffer<br />
response.setContentType("application/vnd.ms-excel");<br />
File fileWrite = new File("f:/testWrite.xls");<br />
fileWrite.createNewFile();<br />
new FileOutputStream(fileWrite);<br />
ExcelHandle.writeExcel(new FileOutputStream(fileWrite));</font><br />
%&gt;<br />
在IE中浏览writeExcel.jsp就可以动态生成Excel文档了，其中</font><font face="Courier New">response.setContentType("application/vnd.ms-excel");语句必须要，才能确保不乱码，在jsp中输入&lt;%@page&nbsp;contentType="application/vnd.ms-excel;charset=GBK"%&gt;</font>不行。</p>
 <img src ="http://www.blogjava.net/strawsoft/aggbug/258499.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/strawsoft/" target="_blank">strawsoft</a> 2009-03-08 21:53 <a href="http://www.blogjava.net/strawsoft/articles/258499.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java静态内部类和非内部类的区别(转)</title><link>http://www.blogjava.net/strawsoft/articles/258368.html</link><dc:creator>strawsoft</dc:creator><author>strawsoft</author><pubDate>Sat, 07 Mar 2009 07:53:00 GMT</pubDate><guid>http://www.blogjava.net/strawsoft/articles/258368.html</guid><wfw:comment>http://www.blogjava.net/strawsoft/comments/258368.html</wfw:comment><comments>http://www.blogjava.net/strawsoft/articles/258368.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/strawsoft/comments/commentRss/258368.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/strawsoft/services/trackbacks/258368.html</trackback:ping><description><![CDATA[<div class="tit">静态嵌套类和内部类的区别[转]</div>
<div class="date">2007年12月20日 星期四 18:39</div>
<p align="right">
<table style="table-layout: fixed">
    <tbody>
        <tr>
            <td>
            <div class="cnt" id="blog_text">一. 什么是嵌套类及内部类? <br />
            可以在一个类的内部定义另一个类, 这种类称为嵌套类(nested classes),它有两种类型: <br />
            静态嵌套类和非静态嵌套类.静态嵌套类使用很少, 最重要的是非静态嵌套类, 也即是被称作为<br />
            内部类(inner).嵌套类从JDK1.1开始引入.其中inner类又可分为三种: <br />
            (1) 在一个类(外部类)中直接定义的内部类；<br />
            (2) 在一个方法(外部类的方法)中定义的内部类;<br />
            (3) 匿名内部类.<br />
            下面, 我将说明这几种嵌套类的使用及注意事项.<br />
            <br />
            二. 静态嵌套类<br />
            如下所示代码为定义一个静态嵌套类<br />
            <pre><code>
            <table class="ubb_code" style="border-collapse: collapse" cellspacing="1" cellpadding="0" width="90%" border="1">
                <tbody>
                    <tr>
                        <td>package inner;<br />
                        <br />
                        /**<br />
                        * @author: Joho Woo<br />
                        * @email: kongbowoo@163.com<br />
                        * @version: 1.0 <br />
                        * @time: 2007-11-12 下午07:52:29<br />
                        * @description: <br />
                        */<br />
                        public class StaticTest<br />
                        {<br />
                        &nbsp;&nbsp; private static String name = "woobo";<br />
                        &nbsp;&nbsp; private String num = "X001";<br />
                        <br />
                        &nbsp;&nbsp; static class Person<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; private String address = "China";<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; public String mail = "kongbowoo@yahoo.com.cn";//内部类公有成员<br />
                        <br />
                        &nbsp;&nbsp;&nbsp;&nbsp; public void display()<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //System.out.println(num);//不能直接访问外部类的非静态成员<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(name);//只能直接访问外部类的静态成员<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Inner " + address);//访问本内部类成员。<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp; }<br />
                        <br />
                        &nbsp;&nbsp; public void printInfo()<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; Person person = new Person();<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; person.display();<br />
                        <br />
                        &nbsp;&nbsp;&nbsp;&nbsp; //System.out.println(mail);//不可访问<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; //System.out.println(address);//不可访问<br />
                        <br />
                        &nbsp;&nbsp;&nbsp;&nbsp; System.out.println(person.address);//可以访问内部类的私有成员<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; System.out.println(person.mail);//可以访问内部类的公有成员<br />
                        <br />
                        &nbsp;&nbsp; }<br />
                        <br />
                        &nbsp;&nbsp; public static void main(String[] args)<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; StaticTest staticTest = new StaticTest();<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; staticTest.printInfo();<br />
                        &nbsp;&nbsp; }<br />
                        }</td>
                    </tr>
                </tbody>
            </table>
            </code></pre>
            <br />
            在静态嵌套类内部, 不能访问外部类的非静态成员, 这是由Java语法中"静态方法不能直接访问非静态成员"所限定.若想访问外部类的变量, 必须通过其它方法解决, 由于这个原因, 静态嵌套类使用很少.注意, 外部类访问内部类的的成员有些特别, 不能直接访问, 但可以通过内部类实例来访问, 这是因为静态嵌套内的所有成员和方法默认为静态的了.同时注意, 内部静态类Person只在类StaticTest 范围内可见, 若在其它类中引用或初始化, 均是错误的.<br />
            <br />
            三. 在外部类中定义内部类<br />
            如下所示代码为在外部类中定义两个内部类及它们的调用关系: <br />
            <pre><code>
            <table class="ubb_code" style="border-collapse: collapse" cellspacing="1" cellpadding="0" width="90%" border="1">
                <tbody>
                    <tr>
                        <td>class Outer<br />
                        {<br />
                        &nbsp;&nbsp; int outer_x = 100;<br />
                        &nbsp;&nbsp; private class InnerOne<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // 私有的内部类<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; public int inner_y = 10;<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; private int inner_z = 9;<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; int inner_m = 5;<br />
                        <br />
                        &nbsp;&nbsp;&nbsp;&nbsp; public void display()<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("display outer_x:" + outer_x);<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; private void display2()<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("display outer_x:" + outer_x);<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp; }<br />
                        <br />
                        &nbsp;&nbsp; public InnerOne getInnerOne()<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // 即使是对外公开的方法,外部类也无法调用<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; return new InnerOne();<br />
                        &nbsp;&nbsp; }<br />
                        &nbsp;&nbsp;<br />
                        &nbsp;&nbsp; class InnerTwo<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; InnerOne innerx = getInnerOne();// 可以访问<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; public void show()<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // System.out.println(inner_y); // 不可访问Innter的y成员<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // System.out.println(Inner.inner_y);&nbsp;&nbsp; // 不可直接访问Inner的任何成员和方法<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; innerx.display();// 可以访问<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; innerx.display2();// 可以访问<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(innerx.inner_y);// 可以访问<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(innerx.inner_z);// 可以访问<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(innerx.inner_m);// 可以访问<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp; }<br />
                        &nbsp;&nbsp;<br />
                        &nbsp;&nbsp; void test()<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; InnerOne inner = new InnerOne();// 可以访问<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; inner.display();<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; inner.display2();<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // System.out.println("Inner y:" + inner_y); // 不能访问内部内变量<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Inner y:" + inner.inner_y);// 可以访问<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Inner z:" + inner.inner_z);// 可以访问<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Inner m:" + inner.inner_m);// 可以访问<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; InnerTwo innerTwo = new InnerTwo();<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; innerTwo.show();<br />
                        &nbsp;&nbsp; }<br />
                        }<br />
                        <br />
                        public class Test<br />
                        {<br />
                        &nbsp;&nbsp; public static void main(String args[])<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; Outer outer = new Outer();<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // Outer.Inner a=outer.getInner();<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // Inner类是私有的,外部类不能访问, 如果Inner类是public ,则可以.<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; outer.test();<br />
                        &nbsp;&nbsp; }<br />
                        }</td>
                    </tr>
                </tbody>
            </table>
            </code></pre>
            <br />
            <br />
            内部类Inner及InnterTwo只在类Outer的作用域内是可知的, 如果类Outer外的任何代码尝试初始化类Inner或使用它, 编译就不会通过.同时, 内部类的变量成员只在内部内内部可见, 若外部类或同层次的内部类需要访问, 需采用示例程序<br />
            中的方法, 不可直接访问内部类的变量.<br />
            <br />
            四. 在方法中定义内部类<br />
            如下所示代码为在方法内部定义一个内部类:<br />
            <pre><code>
            <table class="ubb_code" style="border-collapse: collapse" cellspacing="1" cellpadding="0" width="90%" border="1">
                <tbody>
                    <tr>
                        <td>package inner;<br />
                        <br />
                        /**<br />
                        * @author: Joho Woo<br />
                        * @email: kongbowoo@163.com<br />
                        * @version: 1.0<br />
                        * @time: 2007-11-12 下午08:25:33<br />
                        * @description:<br />
                        */<br />
                        public class FunOuter<br />
                        {<br />
                        &nbsp;&nbsp; int out_x = 100;<br />
                        <br />
                        &nbsp;&nbsp; public void test()<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; class Inner<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String inner_x = "x";<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void display()<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(out_x);<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; Inner inner = new Inner();<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; inner.display();<br />
                        &nbsp;&nbsp; }<br />
                        &nbsp;&nbsp; public void showStr(String str)<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // public String str1 = "test Inner";<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // 不可定义, 只允许final修饰<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // static String str4 = "static Str";<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // 不可定义, 只允许final修饰<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; String str2 = "test Inner";<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; final String str3 = "final Str";<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; class InnerTwo<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void testPrint()<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(out_x);<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 可直接访问外部类的变量<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // System.out.println(str); // 不可访问本方法内部的非final变量<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // System.out.println(str2); // 不可访问本方法内部的非final变量<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(str3); // 只可访问本方法的final型变量成员<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; InnerTwo innerTwo = new InnerTwo();<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; innerTwo.testPrint();<br />
                        &nbsp;&nbsp; }<br />
                        &nbsp;&nbsp; public void use()<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // Inner innerObj = new Inner();//此时Inner己不可见了<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // System.out.println(Inner.x);//此时Inner己不可见了<br />
                        &nbsp;&nbsp; }<br />
                        &nbsp;&nbsp; public static void main(String[] args)<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; FunOuter outer = new FunOuter();<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; outer.test();<br />
                        &nbsp;&nbsp; }<br />
                        }</td>
                    </tr>
                </tbody>
            </table>
            </code></pre>
            <br />
            <br />
            从上面的例程我们可以看出定义在方法内部的内部类的可见性更小, 它只在方法内部<br />
            可见, 在外部类(及外部类的其它方法中)中都不可见了.同时, 它有一个特点, 就是方法内的内部类连本方法的成员变量都不可访问, 它只能访问本方法的final型成员.同时另一个需引起注意的是方法内部定义成员, 只允许final修饰或不加修饰符, 其它像static等均不可用.<br />
            <br />
            五. 匿名内部类<br />
            如下所示代码为定义一个匿名内部类:匿名内部类通常用在Java的事件处理上<br />
            <pre><code>
            <table class="ubb_code" style="border-collapse: collapse" cellspacing="1" cellpadding="0" width="90%" border="1">
                <tbody>
                    <tr>
                        <td>package inner;<br />
                        <br />
                        /**<br />
                        * @author: Joho Woo<br />
                        * @email: kongbowoo@163.com<br />
                        * @version: 1.0 <br />
                        * @time: 2007-11-12 下午09:01:05<br />
                        * @description: <br />
                        */<br />
                        import java.applet.*;<br />
                        import java.awt.event.*;<br />
                        <br />
                        public class AnonymousInnerClassDemo extends Applet<br />
                        {<br />
                        &nbsp;&nbsp; public void init()<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; addMouseListener(new MouseAdapter()<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void mousePressed(MouseEvent me)<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; showStatus("Mouse Pressed!");<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; });<br />
                        &nbsp;&nbsp; }<br />
                        &nbsp;&nbsp; public void showStatus(String str)<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; System.out.println(str);<br />
                        &nbsp;&nbsp; }<br />
                        }</td>
                    </tr>
                </tbody>
            </table>
            </code></pre>
            <br />
            在上面的例子中, 方法addMouseListener接受一个对象型的参数表达式, 于是, 在参数里, 我们定义了一个匿名内部类,这个类是一个MouseAdapter类型的类, 同时在这个类中定义了一个继承的方法mousePressed, 整个类做为一个参数.这个类没有名称, 但是当执行这个表达式时它被自动实例化.同时因为, 这个匿名内部类是定义在AnonymousInnerClassDemo <br />
            类内部的, 所以它可以访问它的方法showStatus.这同前面的内部类是一致的.<br />
            <br />
            六. 内部类使用的其它的问题<br />
            通过以上, 我们可以清楚地看出内部类的一些使用方法, 同时, 在许多时候, 内部类是在如Java的事件处理. 或做为值对象来使用的.同时, 我们需注意最后一个问题, 那就是, 内部类同其它类一样被定义, 同样它也可以继承外部其它包的类和实现外部其它地方的接口.同样它也可以继承同一层次的其它的内部类,甚至可以继承外部类本身.下面我们给出最后一个例子做为结束: <br />
            <pre><code>
            <table class="ubb_code" style="border-collapse: collapse" cellspacing="1" cellpadding="0" width="90%" border="1">
                <tbody>
                    <tr>
                        <td>public class Layer<br />
                        {<br />
                        &nbsp;&nbsp; // Layer类的成员变量<br />
                        &nbsp;&nbsp; private String testStr = "testStr";<br />
                        <br />
                        &nbsp;&nbsp; // Person类, 基类<br />
                        &nbsp;&nbsp; class Person<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; String name;<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; Email email;<br />
                        <br />
                        &nbsp;&nbsp;&nbsp;&nbsp; public void setName(String nameStr)<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.name = nameStr;<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; public String getName()<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.name;<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; public void setEmail(Email emailObj)<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.email = emailObj;<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; public String getEmail()<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.email.getMailStr();<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        <br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // 内部类的内部类, 多层内部类<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; class Email<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String mailID;<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String mailNetAddress;<br />
                        <br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Email(String mailId, String mailNetAddress)<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.mailID = mailId;<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.mailNetAddress = mailNetAddress;<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String getMailStr()<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.mailID + "@" + this.mailNetAddress;<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp; }<br />
                        <br />
                        &nbsp;&nbsp; // 另一个内部类继承外部类本身<br />
                        &nbsp;&nbsp; class ChildLayer extends Layer<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; void print()<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(super.testStr);// 访问父类的成员变量<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp; }<br />
                        <br />
                        &nbsp;&nbsp; // 另个内部类继承内部类Person<br />
                        &nbsp;&nbsp; class OfficePerson extends Person<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; void show()<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(name);<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(getEmail());<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; }<br />
                        &nbsp;&nbsp; }<br />
                        <br />
                        &nbsp;&nbsp; // 外部类的测试方法<br />
                        &nbsp;&nbsp; public void testFunction()<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // 测试第一个内部类<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; ChildLayer childLayer = new ChildLayer();<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; childLayer.print(); // 测试第二个内部类<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; OfficePerson officePerson = new OfficePerson();<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; officePerson.setName("abner chai");<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // 注意此处, 必须用对象.new 出来对象的子类对象<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // 而不是Person.new Email(...)<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; // 也不是new Person.Email(...)<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; officePerson<br />
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .setEmail(officePerson.new Email("josserchai", "yahoo.com"));<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; officePerson.show();<br />
                        &nbsp;&nbsp; }<br />
                        &nbsp;&nbsp; public static void main(String[] args)<br />
                        &nbsp;&nbsp; {<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; Layer layer = new Layer();<br />
                        &nbsp;&nbsp;&nbsp;&nbsp; layer.testFunction();<br />
                        &nbsp;&nbsp; }<br />
                        }</td>
                    </tr>
                </tbody>
            </table>
            </code></pre>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
原文：http://hi.baidu.com/dandelion_wei/blog/item/843b9cf3b465e854342acc46.html</p>
 <img src ="http://www.blogjava.net/strawsoft/aggbug/258368.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/strawsoft/" target="_blank">strawsoft</a> 2009-03-07 15:53 <a href="http://www.blogjava.net/strawsoft/articles/258368.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>