<?xml version="1.0" encoding="UTF-8" ?>



<rss version="2.0" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">

<channel>
<title><![CDATA[十年磨一剑]]> </title>
<description>
<![CDATA[十年磨一剑]]>
</description>
<link>http://getmc.blog.bokee.net/</link>
<language>zh-cn</language>
<creator>getmc</creator>
<pubDate>Tue, 21 Feb 2006 00:57:58 CST </pubDate>
<generatorAgent rdf:resource="http://www.bokee.net"/>
<ttl>5</ttl>

<item>
<title>建模工具</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/152185.html</link>
<description>
<![CDATA[<p>Sybase_PowerDesigner </p>
<p><a href="http://www.sanhaoonline.com/download/html/2005/200501/20050119-7328.html">http://www.sanhaoonline.com/download/html/2005/200501/20050119-7328.html</a></p>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/152185.html</guid>
<subject></subject>
<author>getmc</author>
<category></category>
<pubDate>Thu, 15 Jun 2006 22:31:34 CST </pubDate>
</item>

<item>
<title>循序渐进学习Junit</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/133203.html</link>
<description>
<![CDATA[<p><span class="boldbodycopy"><em>使用最流行的开放资源测试框架之一学习单元测试基础。</em></span></p>
<p><span class="bodycopy">使用JUnit可以大量减少Java代码中程序错误的个数，JUnit是一种流行的单元测试框架，用于在发布代码之前对其进行单元测试。现在让我们来详细研究如何使用诸如JUnit、Ant和Oracle9i JDeveloper等工具来编写和运行单元测试。</span></p>
<p><span class="parahead1">为什么使用JUnit？</span></p>
<p><span class="bodycopy">多数开发人员都同意在发布代码之前应当对其进行测试，并利用工具进行回归（regression）测试。做这项工作的一个简单方法是在所有Java类中以main()方法实施测试。例如，假设使用ISO格式（这意味着有一个以这一格式作为参数的构造器和返回一个格式化的ISO字符串的toString()方法）以及一个GMT时区来编写一个Date的子类。<a href="http://www.oracle.com/global/cn/oramag/oracle/03-may/o33junit_l1.html" target="_top"><span class="bodylink">清单1</span></a> 就是这个类的一个简单实现。</span></p>
<p><span class="bodycopy">不过，这种测试方法并不需要单元测试限定语（qualifier），原因如下：</span></p>
<p>　 </p>
<ul>
    <li><span class="bulletbodycopy">在一个类中进行测试的最小单元是方法，你应当对每个方法进行单独测试，以准确地找出哪些方法工作正常，哪些方法工作不正常。</span> </li>
    <li><span class="bulletbodycopy">即使前面的测试失败，也应当对各个方法进行测试。在此实施中，如果单个测试失败，后面的测试将根本不会运行。这就意味着你不会知道不良代码在你的实施中所占的百分比。</span> </li>
    <li><span class="bulletbodycopy">测试代码会出现在生成的类中。这在类的大小方面可能不是什么问题，但却可能会成为安全性因素之一：例如，如果你的测试嵌入了数据库连接密码，那么这一信息将很容易用于已发布的类中。</span> </li>
    <li><span class="bulletbodycopy">没有框架可以自动启动这一测试，你必须编写一个脚本来启动每一个测试。</span> </li>
    <li><span class="bulletbodycopy">在编写一个报告时，你必须编写自己的实现，并定义规则，以方便地报告错误。</span> </li>
</ul>
<p>　</p>
<p><span class="bodycopy">JUnit框架就是设计用来解决这些问题的。这一框架主要是所有测试实例（称为&quot;TestCase&quot;）的一个父类，并提供工具来运行所编写的测试、生成报告及定义测试包（test suite）。</span></p>
<p><span class="bodycopy">让我们为</span><tt>IsoDate</tt><span class="bodycopy">类编写一个测试：这个</span><tt>IsoDateTest</tt><span class="bodycopy">类类似于：</span></p>
<p>　 </p>
<pre>import java.text.ParseException;
import junit.framework.TestCase;


/**
 * Test case for &lt;code&gt;IsoDate&lt;/code&gt;.
 */
public class IsoDateTest extends TestCase {
    
  public void testIsoDate() throws 
    Exception {
      IsoDate epoch=new IsoDate(
       &quot;1970-01-01 00:00:00 GMT&quot;);
      assertEquals(0,epoch.getTime());

      IsoDate eon=new IsoDate(
       &quot;2001-09-09 01:46:40 GMT&quot;);
      assertEquals(
        1000000000L*1000,eon.getTime());
    }
    
  public void testToString() throws   
    ParseException {
      IsoDate epoch=new IsoDate(0);
      assertEquals(&quot;1970-01-01 
        00:00:00 GMT&quot;,epoch.toString());

      IsoDate eon=new IsoDate(
        1000000000L*1000);
      assertEquals(&quot;2001-09-09 
        01:46:40 GMT&quot;,eon.toString());
  }
}
</pre>
<p><span class="bodycopy">本例中要注意的重点是已经编写了一个用于测试的独立类，因此可以对这些文件进行过滤，以避免将这一代码嵌入到将要发布的文档中。另外，本例还为你希望在你的代码中测试的每个方法编写了一个专用测试方法，因此你将确切地知道需要对哪些方法进行测试、哪些方法工作正常以及哪些方法工作不正常。如果在编写实施文档之前已经编写了该测试，你就可以利用它来衡量工作的进展情况。</span></p>
<p><span class="parahead1">安装并运行JUnit</span></p>
<p><span class="bodycopy">要运行此示例测试实例，必须首先下载并安装JUnit。JUnit的最新版本可以在JUnit的网站<a href="http://www.junit.org/" target="_blank"><span class="bodylink"> www.junit.org</span></a>免费下载。该软件包很小（约400KB），但其中包括了源代码和文档。要安装此程序，应首先对该软件包进行解压缩（junitxxx.zip）。它将创建一个目录（junitxxx），在此目录下有文档（在doc目录中）、框架的应用编程接口（API）文档（在javadoc目录中）、运行程序的库文件（junit.jar）以及示例测试实例（在junit目录中）。截至我撰写本文时，JUnit的最新版本为3.8.1，我是在此版本上对示例进行测试的。</span></p>
<p><a name="f1">
<table width="100%" align="center">
    <tbody>
        <tr>
            <td align="center"><img height="160" alt="IsoDate Test" src="http://otn.oracle.com/oramag/oracle/03-may/images/o33junit_f1.jpg" width="450" border="0" /></td>
        </tr>
    </tbody>
</table>
</a></p>
<p>　</p>
<p>&nbsp;</p>
<center><span class="bodycopy">图1 运行IsoDate测试</span></center>
<p>　</p>
<p><span class="bodycopy">要运行此测试实例，将源文件（<a href="http://otn.oracle.com/oramag/oracle/03-may/IsoDate.java" target="_top"><span class="bodylink">IsoDate.java</span></a>和<a href="http://otn.oracle.com/oramag/oracle/03-may/IsoDateTest.java" target="_top"><span class="bodylink">IsoDateTest.java</span></a>）拷贝到Junit的安装目录下，打开终端，进入该目录，然后输入以下命令行（如果你正在使用UNIX）：</span></p>
<p>　 </p>
<pre>export CLASSPATH=.:./junit.jar
javac *.java
或者，如果你正在Windows，输入以下命令行

set CLASSPATH=.;junit.jar
javac *.java
</pre>
<p>　</p>
<p><span class="bodycopy">这些命令行对CLASSPATH进行设置，使其包含当前目录中的类和junit.jar库，并编译Java源文件。</span></p>
<p><span class="bodycopy">要在终端上运行该测试，输入以下命令行：</span><br /></p>
<p>　 </p>
<pre>java junit.textui.TestRunner IsoDateTest
</pre>
<p>　</p>
<p><span class="bodycopy">此命令行将运行该测试，并在<a href="http://www.uml.org.cn/j2ee/xxjuint.htm#f1"><span class="bodylink">图 1</span></a>所示的控制台上显示测试结果。</span></p>
<p><span class="bodycopy">才在此工具可以运行类名被传递到命令行中的单个测试。注意：只有对命令行的最后测试才在考虑之内，以前的测试都被忽略了。（看起来像一个程序错误，是吧？）</span></p>
<p><span class="bodycopy">JUnit还提供了利用AWT（抽象窗口工具包）或Swing运行测试的图形界面。为了利用此图形界面运行测试，在终端上输入以下命令行：</span><br /></p>
<p>　 </p>
<pre>java junit.awtui.TestRunner IsoDateTest
</pre>
<p>　</p>
<p><span class="bodycopy">或者使用Swing界面：</span></p>
<p>　 </p>
<pre>java junit.swingui.TestRunner IsoDateTest
</pre>
<p>　</p>
<p><span class="bodycopy">此命令行将显示<a href="http://www.uml.org.cn/j2ee/xxjuint.htm#f2"><span class="bodylink">图 2</span></a>所示的界面。要选择一个测试并使其运行，点击带有三个点的按钮。这将显示CLASSPATH（还有测试包，但我们将在后面讨论）中所有测试的列表。要运行测试，点击&quot;Run&quot;按钮。测试应当正确运行，并在<a href="http://www.uml.org.cn/j2ee/xxjuint.htm#f2"><span class="bodylink">图 2</span></a>所示的界面中显示结果。</span></p>
<p><span class="bodycopy">在此界面中你应当选中复选框&quot;Reload Classes Every Run&quot;，以便运行器在运行测试类之前对它们进行重新加载。这样就可以方便地编辑、编译并运行测试，而不需要每次都启动图形界面。</span></p>
<p><span class="bodycopy">在该复选框下面是一个进度条，在运行较大的测试包时，该进度条非常有用。运行的测试、错误和失败的数量都会在进度条下面显示出来。再下面是一个失败列表和一个测试层次结构。失败消息显示在底部。通过点击Test Hierarchy（测试层次结构）面板，然后再点击窗口右上角的&quot;Run&quot;按钮，即可运行单个测试方法。请记住，使用命令行工具是不可能做到这些的。</span><br /></p>
<p><span class="bodycopy">注意，当运行工具来启动测试类时，这些类必须存在于CLASSPATH中。但是如果测试类存储在jar文件中，那么即使这些jar文件存在于CLASSPATH中，JUnit也不能找到这些测试类。</span></p>
<p><a name="f2">
<table width="100%" align="center">
    <tbody>
        <tr>
            <td align="center"><img height="396" alt="Swing interface" src="http://otn.oracle.com/oramag/oracle/03-may/images/o33junit_f2.jpg" width="400" border="0" /></td>
        </tr>
    </tbody>
</table>
</a></p>
<p>　</p>
<p>&nbsp;</p>
<center><span class="bodycopy">图2 用于运行测试的Swing界面</span></center>
<p>　</p>
<p>　</p>
<p><span class="bodycopy">这并不是一种启动测试的方便方法，但幸运的是，JUnit已经被集成到了其他工具（如Ant和Oracle9i JDeveloper）中，以帮助你开发测试并使测试能够自动运行。</span></p>
<p><span class="parahead1">编写Junit测试实例</span></p>
<p><span class="bodycopy">你已经看到了测试类的源代码对IsoDate实施进行了询问。现在让我们来研究这样的测试文件的实施。</span></p>
<p><span class="bodycopy">测试实例由junit.frameword.TestCase继承而来是为了利用JUnit框架的优点。这个类的名字就是在被测试类的名字上附加&quot;Test&quot;。因为你正在测试一个名为IsoDate的类，所以其测试类的名字就是IsoDateTest。为了访问除私有方法之外的所有方法，这个类通常与被测类在同一个包中。<br /></span></p>
<p><span class="bodycopy">注意，你必须为你希望测试的在类中定义的每个方法都编写一个方法。你要测试构造器或使用了ISO日期格式的方法，因此你将需要为以ISO格式的字符串作为参数的构造器和toString()方法编写一个测试方法。其命名方式与测试类的命名方式类似：在被测试方法（或构造器）前面附加&quot;test&quot;。</span></p>
<p><span class="bodycopy">测试方法的主体通过验证assertion（断言）对被测方法进行询问。例如，在toString()实施的测试方法中，你希望确认该方法已经对时间的设定进行了很好的说明（对于UNIX系统来说，最初问世的时间为1970年1月1日的午夜）。要实施assertion，你可以使用Junit框架提供的assertion方法。这些方法在该框架的junit.framework.Assert类中被实施，并且可以在你的测试中被访问，这是因为Assert是TestCase的父类。这些方法可与Java中的关键字assert（是在J2EE 1.4中新出现的）相比。一些assertion方法可以检查原始类型（如布尔型、整型等）之间或对象之间是否相等（利用equals()方法检查两个对象是否相等）。其他assertion方法检查两个对象是否相同、一个对象是否为&quot;空&quot;或&quot;非空&quot;，以及一个布尔值（通常由一个表达式生成）是&quot;真&quot;还是&quot;假&quot;。在<a href="http://www.oracle.com/global/cn/oramag/oracle/03-may/o33junit_2.html#t1"><span class="bodylink">表 1</span></a>中对这些方法进行了总结。</span></p>
<p><span class="bodycopy">对于那些采用浮点类型或双精度类型参数的assertion，存在一个第三种方法，即采用一个delta值作为参数进行比较。另外还要注意，assertEquals()和assertSame()方法一般不会产生相同的结果。（两个具有相同值的字符串可以不相同，因为它们是两个具有不同内存地址的不同对象。）因此，assertEquals()将会验证assertion的有效性，而assertSame()则不会。注意，对于<a href="http://www.oracle.com/global/cn/oramag/oracle/03-may/o33junit_2.html#t1"><span class="bodylink">表 1</span></a></span> <span class="bodycopy">中的每个assertion方法，你还有一种选择，就是引入另一个参数，如果assertion失败，该参数就会给出一条解释性消息。例如，assertEquals（int 期望值, int 实际值）就可以与一个诸如assertEquals（字符串消息，int期望值，int实际值）的消息一起使用。</span></p>
<p><span class="bodycopy">当一个assertion失败时，该assertion方法会抛出一个AssertFailedError或ComparisonFailure。AssertionFailedError由java.lang.Error继承而来，因此你不必在测试方法的throws语句中对其进行声明。而ComparisonFailure由AssertionFailedError继承而来，因此你也不必对其进行声明。因为当一个assertion失败时会在测试方法中抛出一个错误，所以后面的assertion将不会继续运行。框架捕捉到这些错误并认定该测试已经失败后，就会打印出一条说明错误的消息。这个消息由assertion生成，并且被传递到assertion方法（如果有的话）。</span></p>
<p><span class="bodycopy">现在将下面一行语句添加到testIsoDate()方法的末尾：</span><br /></p>
<p>　 </p>
<pre>assertEquals(&quot;This is a test&quot;,1,2);
</pre>
<p><span class="bodycopy">现在编译并运行测试：</span></p>
<p>　 </p>
<pre>$ javac *.java
$ java junit.textui.TestRunner IsoDateTest
.F.
Time: 0,348
There was 1 failure:
1) testIsoDate(IsoDateTest)junit.framework
.AssertionFailedError: This is a test expected:&lt;1&gt; but was:&lt;2&gt;
      at IsoDateTest.testIsoDate
      (IsoDateTest.java:29)

FAILURES!!!
Tests run: 2,  Failures: 1,  Errors: 0
</pre>
<p>　</p>
<p><span class="bodycopy">JUnit为每个已处理的测试打印一个点，显示字母&quot;F&quot;来表示失败，并在assertion失败时显示一条消息。此消息由你发送到assertion方法的注释和assertion的结果组成（自动生成）。从这里可以看出assertion方法的参数顺序对于生成的消息非常重要。第一个参数是期望值，而第二个参数则是实际值。</span></p>
<p><span class="bodycopy">如果在测试方法中出现了某种错误（例如，抛出了一个异常），该工具就会将其显示为一个错误（而不是由assertion失败而产生的一个&quot;失败&quot;）。现在对IsoDateTest类进行修改，以将前面增加的一行语句用以下语句代替：</span><br /></p>
<p>　 </p>
<pre>throw new Exception(&quot;This is a test&quot;); 
</pre>
<p><span class="bodycopy">然后编译并运行测试：</span></p>
<p>　 </p>
<pre>$ javac *.java
$ java junit.textui.TestRunner IsoDateTest 
.E.
Time: 0,284
There was 1 error:
1) testIsoDate(IsoDateTest)java.lang.
   Exception: This is a test at IsoDate
   Test.testIsoDate(IsoDateTest.java:30)

FAILURES!!!
Tests run: 2,  Failures: 0,  Errors: 1
</pre>
<p>　</p>
<p><span class="bodycopy">该工具将该异常显示为一个错误。因此，一个错误表示一个错误的测试方法，而不是表示一个错误的测试实施。</span></p>
<p><span class="bodycopy">Assert类还包括一个fail()方法（该版本带有解释性消息），该方法将通过抛出AssertionFailedError来中断正在运行的测试。当你希望一个测试失败而不会调用一个判定方法时，fail()方法是非常有用的。例如，如果一段代码应当抛出一个异常而未抛出，那么可以调用fail()方法使该测试失败，方法如下：</span><br /></p>
<p>　 </p>
<pre>public void testIndexOutOfBounds() {
  try {
       ArrayList list=new ArrayList();
       list.get(0);
       fail(&quot;IndexOutOfBoundsException   
           not thrown&quot;);
  } catch(IndexOutOfBoundsException e) {}
}
</pre>
<p>　</p>
<p><span class="parahead1">JUnit的高级特性</span></p>
<p><span class="bodycopy">在示例测试实例中，你已经同时运行了所有的测试。在现实中，你可能希望运行一个给定的测试方法来询问你正编写的实施方法，所以你需要定义一组要运行的测试。这就是框架的junit.framework.TestSuite类的目的，这个类其实只是一个容器，你可以向其中添加一系列测试。如果你正在进行toString()实施，并希望运行相应的测试方法，那么你可以通过重写测试的suite()方法来通知运行器，方法如下：</span><br /></p>
<p>　 </p>
<pre>public static Test suite() {

  TestSuite suite= new TestSuite();
  suite.addTest(new IsoDateTest
(&quot;testToString&quot;));
  return suite;
}
</pre>
<p>　</p>
<p><span class="bodycopy">在此方法中，你用具体示例说明了一个TestSuite对象，并向其中添加了测试。为了在方法级定义测试，你可以利用构造器将方法名作为参数使测试类实例化。此构造器可按如下方法实施：</span></p>
<p>　 </p>
<pre>public IsoDateTest(String name) {
  super(name);
}
</pre>
<p>　</p>
<p><span class="bodycopy">将上面的构造器和方法添加到IsoDateTest类（还需要引入junit.framework.Test和junit.framework.TestSuite），并在终端上输入：</span></p>
<p><a name="f3">
<table width="100%" align="center">
    <tbody>
        <tr>
            <td align="center"><img height="144" alt="selecting a test method" src="http://otn.oracle.com/oramag/oracle/03-may/images/o33junit_f3.jpg" width="400" border="0" /></td>
        </tr>
    </tbody>
</table>
</a></p>
<center>
<p><span class="bodycopy">图3：选择一个测试方法</span></p>
</center>
<p>&nbsp;</p>
<p>　 </p>
<pre> 
$ javac *.java
$ java junit.textui.TestRunner IsoDateTest
.
Time: 0,31
OK (1 test)
</pre>
<p>　</p>
<p><span class="bodycopy">注意，在添加到测试包中的测试方法中，只运行了一个测试方法，即toString()方法。</span></p>
<p><span class="bodycopy">你也可以利用图形界面，通过在图3所示的Test Hierarchy面板中选择测试方法来运行一个给定的测试方法。但是，要注意当整个测试包被运行一次后，该面板将被填满。</span></p>
<p><span class="bodycopy">当你希望将一个测试实例中的所有测试方法添加到一个TestSuite对象中时，可以使用一个专用构造器，该构造器将此测试实例的类对象作为参数。例如，你可以使用IsoDateTest类实施suite()方法，方法如下：</span><br /></p>
<p>　 </p>
<pre>public static Test suite() {
  return new TestSuite(IsoDateTest.class);
}
</pre>
<p>　</p>
<p><span class="bodycopy">还有一些情况，你可能希望运行一组由其他测试（如在工程发布之前的所有测试）组成的测试。在这种情况下，你必须编写一个实施suite()方法的类，以建立希望运行的测试包。例如，假定你已经编写了测试类Atest和Btest。为了定义那些包含了类ATest中的所有测试和在BTest中定义的测试包的集合，可以编写下面的类：</span></p>
<p>　 </p>
<pre>import junit.framework.*;

/**
 * TestSuite that runs all tests.
 */
public class AllTests {

  public static Test suite() {
     TestSuite suite= new TestSuite(&quot;All Tests&quot;);
     suite.addTestSuite(ATest.class);
     suite.addTest(BTest.suite());
     return suite;
  }
}
</pre>
<p>　</p>
<p><span class="bodycopy">你完全可以像运行单个测试实例那样运行这个测试包。注意，如果一个测试在一个套件中添加了两次，那么运行器将运行它两次（测试包和运行器都不会检查该测试是否是唯一的）。为了了解实际的测试包的实施，应当研究Junit本身的测试包。这些类的源代码存在于JUnit安装的junit/test目录下。</span></p>
<p><a name="f4">
<table width="100%" align="center">
    <tbody>
        <tr>
            <td align="center"><img height="272" alt="test results" src="http://otn.oracle.com/oramag/oracle/03-may/images/o33junit_f4.jpg" width="400" border="0" /></td>
        </tr>
    </tbody>
</table>
</a></p>
<p>　</p>
<p>&nbsp;</p>
<center><span class="bodycopy">图4：显示测试结果的报告</span></center>
<p>　</p>
<p><span class="bodycopy">将一个main()方法添加到一个测试或一个测试包中有时是非常方便的，因此可以在不使用运行器的情况下启动测试。例如，要将AllTests测试包作为一个标准的Java程序启动，可以将下面的main()方法添加到类中：</span></p>
<p>　 </p>
<pre>public static void main(String[] args) {
  junit.textui.TestRunner.run(suite());
}
</pre>
<p>　</p>
<p><span class="bodycopy">现在可以通过输入java AllTests来启动这个测试包。</span></p>
<p><span class="bodycopy">JUnit框架还提供了一种有效利用代码的方法，即将资源集合到被称为fixture的对象集中。例如，该示例测试实例利用两个叫作epoch和eon的参考日期。将这些日期重新编译到每个方法测试中只是浪费时间（而且还可能出现错误）。你可以用fixture重新编写测试，如<a href="http://www.oracle.com/global/cn/oramag/oracle/03-may/o33junit_l2.html" target="_top"><span class="bodylink">清单2</span></a>所示。</span></p>
<p><span class="bodycopy">你定义了两个参考日期，作为测试类的段，并将它们编译到一个setUp()方法中。这一方法在每个测试方法之前被调用。与其对应的方法是tearDown()方法，它将在每个测试方法运行之后清除所有的资源（在这个实施中，该方法事实上什么也没做，因为垃圾收集器为我们完成了这项工作）。现在编译这个测试实例（其源代码应当放在JUnit的安装目录中）并运行它：</span></p>
<p>　 </p>
<pre>$ javac *.java
$ java junit.textui.TestRunner IsoDateTest2
.setUp()
testIsoDate()
tearDown()
.setUp()
testToString()
tearDown()

Time: 0,373

OK (2 tests)
</pre>
<p>　</p>
<p><span class="bodycopy">注意：在该测试实例中建立了参考日期，因此在任何测试方法中修改这些日期都不会对其他测试产生不利影响。你可以将代码放到这两个方法中，以建立和释放每个测试所需要的资源（如数据库连接）。</span></p>
<p><span class="bodycopy">JUnit发布版还提供了扩展模式（在包junit.extensions中），即test decor-ators，以提供像重复运行一个给定的测试这样的新功能。它还提供了一个TestSuite，以方便你在独立的线程中同时运行所有测试，并在所有线程中的测试都完成时停止。</span><br /></p>
<p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top" align="left">
            <td valign="top" align="center" width="100%">
            <table cellspacing="10" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr valign="top" align="left">
                        <td width="100%">
                        <table cellspacing="0" cellpadding="0" width="100%" border="0">
                            <tbody>
                                <tr>
                                    <td><center>
                                    <p><span class="topstoryhead">循序渐进学习JUnit：第二部分</span></p>
                                    <p><span class="italicbodycopy">作者：Michel Casabianca</span></p>
                                    </center>
                                    <p><span class="parahead1">利用Ant使测试自动化</span></p>
                                    <p><span class="bodycopy">如前面所述，测试运行器是非常原始的。如果你正在运行Ant来编译你的工程，那么编译文件是运行单元测试的好方法。（关于Ant的介绍，请参考我的文章《Ant简介》（<a href="http://www.oracle.com/technology/oramag/oracle/02-nov/o62odev_ant.html" target="_top"><span class="bodylink">Starting with Ant</span></a>），发表于Oracle杂志2002年11／12月号中）。</span></p>
                                    <p><span class="bodycopy">假设你的源文件在src目录中，所生成的类在tmp目录中，并且junit.jar库位于工程的libdirectory目录中，那么你可以编译Java源文件，并使用<a href="http://www.oracle.com/global/cn/oramag/oracle/03-may/o33junit_l3.html" target="_top">清单3</a>中所示的编译文件（在工程的根目录中）运行单元测试。</span></p>
                                    <p><span class="bodycopy">这个编译文件的核心是运行单元测试的测试目标。运行这些测试是这个目标junit的唯一任务。为了运行这一可选任务，必须首先将junit.jar库放到Ant安装目录下的lib目录中，然后下载并安装同一目录中的Ant可选任务库。<a href="http://www.oracle.com/oramag/oracle/03-may/o33junit_l3.html" target="_top">清单3</a>中的示例嵌套了一个classpath类，它包括JUnit库和工程的类；示例中还嵌套了一个batchtest元素，它利用一个选择适当源文件的fileset元素定义了将要运行的测试。这个任务还包括haltonfilure和haltonerror属性，它们告诉Ant在遇到一个失败或错误时是否应当停止。如果将它们的值设置为&quot;真&quot;，那么Ant在遇到第一个失败或错误时将会停止，编译将会失败（显然，这表示在运行测试过程中存在有问题）。另一方面，如果将它们的值设置为&quot;假&quot;，其结果就不是非常明确了（即使测试失败，编译也会成功），但所有测试仍将运行。printsummary属性指示Ant是否显示运行测试的输出。数值withOutAndErr可以在开发测试时方便地告诉Ant显示标准输出和错误输出。数值off表示不显示任何内容，而on只显示测试报告（没有测试类的输出）。junit任务具有很多属性，详细内容请参考Ant的文档。</span></p>
                                    <p><span class="bodycopy">为了测试这一编译文件，你需要建立名字为src、tmp和lib的目录。将junit.jar库放到lib目录中，并将前面看到的示例Java源文件放到src目录中。打开终端，进入该工程的根目录，并输入ant，其结果为：</span></p>
                                    <p>　 </p>
                                    <pre>$ ant
Buildfile: build.xml

clean:
   [delete] Deleting directory 
     /Users/casa/doc/oracle

     /junit/prj/tmp
   [mkdir] Created dir: /Users/casa
     /doc/oracle/junit/prj/tmp

bin:
    [javac] Compiling 4 source files 
      to /Users/casa/doc/oracle
      /junit/prj/tmp


test:
    [junit] Running IsoDateTest
    [junit] Tests run: 1, Failures: 
      0, Errors: 0, Time elapsed: 
           0,005 sec
    [junit] Running IsoDateTest2
    [junit] Tests run: 2, Failures: 0, 
      Errors: 0, Time elapsed: 0,031 sec
    [junit] Output:
    [junit] setUp()

    [junit] testIsoDate()
    [junit] tearDown()
    [junit] setUp()
    [junit] testToString()
    [junit] tearDown()

all:

BUILD SUCCESSFUL
Total time: 8 seconds
</pre>
                                    <p>　</p>
                                    <p><span class="bodycopy">Ant还可以生成非常有用的HTML格式的测试报告。为了生成这样的报告，将前面的测试目标用以下目标代替：</span></p>
                                    <p>　 </p>
                                    <pre>&lt;target name=&quot;test&quot; depends=&quot;bin&quot;
       description=&quot;Run JUnit tests&quot;&gt;
  &lt;junit haltonfailure=&quot;false&quot;
        printsummary=&quot;withOutAndErr&quot;&gt;
    &lt;classpath refid=&quot;cp&quot;/&gt;
    &lt;batchtest todir=&quot;${tmp}&quot;&gt;
      &lt;fileset dir=&quot;${src}&quot; 
              includes=&quot;**/*Test*.java&quot;/&gt;
     &lt;/batchtest&gt;

     &lt;formatter type=&quot;xml&quot;/&gt;
  &lt;/junit&gt;
  &lt;junitreport todir=&quot;${tmp}&quot;&gt;
    &lt;fileset dir=&quot;${tmp}&quot; 
            includes=&quot;TEST-*.xml&quot;/&gt;
    &lt;report format=&quot;frames&quot; 
           todir=&quot;${tmp}&quot;/&gt;
  &lt;/junitreport&gt;
&lt;/target&gt;
</pre>
                                    <p>　</p>
                                    <p><span class="bodycopy">这一目标与前面的目标相同，只是该目标在batchtext元素中增加了一个新的属性--todir，它告诉Ant在tmp目录中生成可扩展的标记语言（XML）报告。该目标还增加了一个新的junitreport元素，以便由XML文件生成一个HTML报告。这一元素要求在安装Ant的lib目录中安装Xalan库（详细内容见Ant文档的junitreport部分：<a href="http://ant.apache.org/manual/install.html" target="_blank"><span class="bodylink">ant.apache.org/manual/install.html</span></a>）。这一元素还定义了使用todir属性生成的文件的目标目录。通过嵌套一个fileset元素来定义为生成这一报告而需要处理的XML文件。期望的输出格式利用嵌套的报告元素来实现。该对象将生成一个诸如<a href="http://www.oracle.com/oramag/oracle/03-may/o33junit.html#f4">图4</a>所示的报告。</span></p>
                                    <p><span class="bodycopy">这类报告在使单元测试自动运行时特别有用（比如在夜间编译期间）。在这些情况下，错误或失败不会中断测试，因此你必须将前面提到的junit任务的haltonfailure和haltonerror属性设置为&quot;假&quot;。这一报告对于衡量实施进程也非常有用（比如当你必须重写已有代码时，或者在实施之前已经编写了测试的情况下）。</span></p>
                                    <p><span class="bodycopy">Ant对启动JUnit图形运行器也非常有用。下面的对象将会启动Swing测试运行器：</span></p>
                                    <p>　 </p>
                                    <pre>&lt;target name=&quot;testui&quot; depends=&quot;bin&quot;
        description=&quot;Run graphical JUnit&quot;&gt;
&lt;java classname=&quot;junit.swingui.TestRunner&quot;
      classpathref=&quot;cp&quot; 
      fork=&quot;true&quot;/&gt;
&lt;/target&gt;
</pre>
                                    <p>　</p>
                                    <p><span class="bodycopy">你应当在终端中运行这一对象，并且在另一个终端或你喜欢的IDE中使用Ant对其进行编译。这种方式使你不必在每次想要测试代码时都启动图形运行器。</span></p>
                                    <p><span class="parahead1">在Oracle9<em>i</em> Jdeveloper中的JUnit集成</span></p>
                                    <p><span class="bodycopy">Oracle9<em>i</em> JDeveloper并没有基于网络集成JUnit，但是下载并安装这一插件只需要几分钟的时间。为了完成此过程，选择JDeveloper的&quot;Help&quot;菜单下的&quot;Check for Updates&quot;项。这样将会打开IDE更新向导，以连接到Oracle技术网站，下载该插件并安装它。当安装该插件后，需要关闭并重启Oracle9<em>i</em> JDeveloper。注意，向导还会下载相关的文档。</span></p>
                                    <p><span class="bodycopy">通过为每个任务提供向导，这个插件极大地提高了开发人员编写测试实例、测试包和fixture等的工作效率。要调用这些向导，点击&quot;File&quot;菜单下的&quot;New&quot;项，然后选择&quot;General/Unit Tests&quot;类，并从右侧的窗体中选择合适的向导。你也可以从界面上启动测试套件。</span></p>
                                    <p><span class="bodycopy">当准备好对项目进行代码测试后，应当首先使用专用向导来编写fixture，然后测试实例向导可以利用它们集成到测试实例中。另外，还有一些用来生成自定义测试fixture的向导以及生成商务组件和数据库连接测试fixture的向导。这后两种向导生成专用代码，以使用setUp()和tearDown()方法设置和发布商务组件或数据库连接。</span></p>
                                    <p><span class="bodycopy">当完成fixture后，下一步应当使用合适的向导来生成测试实例，这些向导可以让你选择要测试的类和方法。你还可以选择在这个测试中使用的fixture。这将生成一个使用测试方法的主体完成的代码框架。最后应当生成套件来运行你的测试。这个专用向导让你选择要包括在套件中的测试，并为你生成整个类。要启动一个测试套件，点击浏览器中的文件，并选择Run。这将会启动图形界面并运行套件的测试。</span></p>
                                    <p><span class="bodycopy">在&quot;Help&quot;菜单中选择&quot;Help Topics&quot;，你将会在JDeveloper文档中找到关于如何使用这些向导的详细教程。这会打开帮助系统窗口。点击&quot;Unit Testing with JUnit&quot;项，然后选择合适的教程。</span></p>
                                    <p><span class="bodycopy">JUnit和JDeveloper之间的这种集成使你能够只编写单元测试中你感兴趣的那部分的代码，而让工具为你编写重复的代码。</span>
                                    <table cellpadding="5" width="35%" align="right" bgcolor="#f7f7e7" border="1" hspace="5" vspace="5">
                                        <tbody>
                                            <tr>
                                                <td><center><span class="parahead1">下一步</span></center>
                                                <p><span class="boldbodycopy">访问JUnit网站</span><br /><a href="http://www.junit.org/" target="_blank"><span class="bodylink">www.junit.org</span></a></p>
                                                <p><a href="http://www.junit.org/" target="_blank"><span class="boldbodycopy">下载</span><br /><span class="boldbodycopy">Oracle9<em>i</em> Jdeveloper</span><br /></a><a href="http://www.oracle.com/technology/software/products/jdev/index.html" target="_top"><span class="bodylink">otn.oracle.com/software/products/jdev/</span></a></p>
                                                <p><span class="boldbodycopy">Oracle9<em>i</em>应用服务器</span><br /><a href="http://www.oracle.com/technology/software/products/ias/index.html" target="_top"><span class="bodylink">otn.oracle.com/software/products/ias/</span></a></p>
                                                <p><span class="boldbodycopy">学习Oracle9<em>i</em> JDeveloper扩展</span><br /><a href="http://otn.oracle.com/products/jdev/htdocs/partners/addins" target="_top"><span class="bodylink">otn.oracle.com/products/jdev/htdocs/partners/addins</span></a></p>
                                                <p><span class="boldbodycopy">阅读Oracle9<em>i</em> JDeveloper文档</span><br /><a href="http://www.oracle.com/technology/docs/products/jdev/index.html" target="_top"><span class="bodylink">otn.oracle.com/docs/products/jdev/</span></a></p>
                                                </td>
                                            </tr>
                                        </tbody>
                                    </table>
                                    </p>
                                    <p>　</p>
                                    <p><span class="parahead1">JUnit最佳实践</span></p>
                                    <p><span class="bodycopy">下面是一些在使用JUnit时应当注意的技巧：</span></p>
                                    <p>　 </p>
                                    <ul>
                                        <li><span class="bulletbodycopy">在实施之前编写测试代码。这是一种合同驱动的实施方式。</span> </li>
                                        <li><span class="bulletbodycopy">只测试那些可能会中断的方法（也就是说，在多数情况下不应测试setter和getter方法）。要尽可能地多进行测试，以避免回归测试。当测试一个较大的应用程序时，你可以在夜间编译时运行所有测试。</span> </li>
                                        <li><span class="bulletbodycopy">一定要使用正确的JUnit扩展来测试特殊的应用程序（如使用Castus测试J2EE应用程序）。</span> </li>
                                    </ul>
                                    <p><span class="parahead1">值得花费的时间</span></p>
                                    <p><span class="bodycopy">到现在，你应当已经清楚地知道使用JUnit框架和合适的工具实施单元测试是多么快速而简单。关于单元测试的下一个目标是使你的CTO相信你在实施测试时所必须花费的时间是为了以后节省更多的时间。但是，当你考虑在检查老代码、修正错误和发布一个调试过的版本上所花费的时间（它可能花费整个一天）时，在开发过程的早期阶段捕获的代码错误毫无疑问是一项很好的投资。这里并没有考虑当错误代码不再位于块的顶部时开发人员必须遵循的&quot;black magic&quot;步骤，这些步骤包括：标记代码，制作一个分支、修正代码错误、进行发布，以及将代码修正合并到块中。所有这些步骤都非常耗时，并且容易产生错误。</span></p>
                                    <p><span class="bodycopy">要开始使用单元测试和JUnit，请访问JUnit网站： <a href="http://www.junit.org/" target="_blank"><span class="bodylink">www.junit.org</span></a>。你将找到大量有用的文档（包括使用JUnit实施测试的详细说明书）、一个与JUnit集成的IDE列表，以及关于JUnit扩展工具的详细内容。</span></p>
                                    <p><span class="italicbodycopy"><strong>Michel Casabianca</strong> (<a href="mailto:casa@sweetohm.net"><span class="bodylink"> casa@sweetohm.net</span></a>)</span><span class="bodycopy">是In-Fusio（一家为移动用户提供游戏服务的法国公司）的一名软件工程师，同时也是XML Pocket Reference（O'Reilly出版，2001年）一书的合著者。</span></p>
                                    <p>　
                                    <table cellpadding="5" width="100%" align="center" bgcolor="#f7f7e7" border="1" hspace="5" vspace="5">
                                        <tbody>
                                            <tr>
                                                <td colspan="2"><center><a name="t1"><span class="parahead1">表1：编写测试实例中所使用的判定方法</span></a></center>
                                                <p>　</p>
                                                </td>
                                            </tr>
                                            <tr bgcolor="#fffff0">
                                                <td><span class="bodycopy">assertEquals(期望原型,实际原型)</span></td>
                                                <td><span class="bodycopy">检查两个原型是否相等</span></td>
                                            </tr>
                                            <tr>
                                                <td><span class="bodycopy">assertEquals(期望对象,实际对象)</span></td>
                                                <td><span class="bodycopy">利用对象的equals()方法检查两个对象是否相等</span></td>
                                            </tr>
                                            <tr bgcolor="#fffff0">
                                                <td><span class="bodycopy">assertSame(期望对象，实际对象)</span></td>
                                                <td><span class="bodycopy">检查具有相同内存地址的两个对象是否相等</span></td>
                                            </tr>
                                            <tr>
                                                <td><span class="bodycopy">assertNotSame(期望对象,实际对象)</span></td>
                                                <td><span class="bodycopy">检查具有不同内存地址的两个对象是否不相等</span></td>
                                            </tr>
                                            <tr bgcolor="#fffff0">
                                                <td><span class="bodycopy">assertNull(对象 对象)</span></td>
                                                <td><span class="bodycopy">检查一个对象是否为空</span></td>
                                            </tr>
                                            <tr>
                                                <td><span class="bodycopy">assertNotNull(对象 对象)</span></td>
                                                <td><span class="bodycopy">检查一个对象是否为非空</span></td>
                                            </tr>
                                            <tr bgcolor="#fffff0">
                                                <td><span class="bodycopy">assertTrue(布尔条件)</span></td>
                                                <td><span class="bodycopy">检查条件是否为真</span></td>
                                            </tr>
                                            <tr>
                                                <td><span class="bodycopy">assertFalse(布尔条件)</span></td>
                                                <td><span class="bodycopy">检查条件是否为假</span></td>
                                            </tr>
                                        </tbody>
                                    </table>
                                    </p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
        <!-- Fake RS 2-->
    </tbody>
</table>
</p>
<p>　</p>
<!-- #EndEditable -->]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/133203.html</guid>
<subject></subject>
<author>getmc</author>
<category></category>
<pubDate>Mon, 29 May 2006 15:06:19 CST </pubDate>
</item>

<item>
<title>hibernate3.0最新教程</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/117300.html</link>
<description>
<![CDATA[<a href="http://lib.verycd.com/2005/11/22/0000076092.html">http://lib.verycd.com/2005/11/22/0000076092.html</a>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/117300.html</guid>
<subject>读书</subject>
<author>getmc</author>
<category>读书</category>
<pubDate>Mon, 15 May 2006 08:39:55 CST </pubDate>
</item>

<item>
<title>《JAVA核心技术 JAVA数据结构》</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/117154.html</link>
<description>
<![CDATA[<a href="http://lib.verycd.com/2005/04/13/0000046285.html">http://lib.verycd.com/2005/04/13/0000046285.html</a>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/117154.html</guid>
<subject>读书</subject>
<author>getmc</author>
<category>读书</category>
<pubDate>Sun, 14 May 2006 22:55:54 CST </pubDate>
</item>

<item>
<title> 学编程的人不能不看的好文章[转] </title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/112836.html</link>
<description>
<![CDATA[<table cellspacing="0" cellpadding="0" width="98%" align="center" border="0">
    <tbody>
        <tr>
            <td colspan="2" height="17"><span class="style2"><strong>学编程的人不能不看的好文章 </strong></span><strong><br /></strong><hr align="left" width="95%" color="#597282" noshade="noshade" size="1" /></td>
        </tr>
        <tr>
            <td colspan="2"><br /><span class="style6">看了下面的这篇文章，深有感触，枣子碰到的问题也是我们大多数程序员的通病，也许我们大多数人都只是在做一些比较小型的软件，对软件运行的效率不在乎，就算对速度和效率在乎的也可能是一些在数据库操作方面的。大家看完了，也许会有很多感想，但这只是我同意枣子的个人观点。<br />(枣子原创--www.vcroad.net&nbsp;wutao8@263.net)&nbsp;<br />做为一名大四的学生,我面试过不少的单位，有成功的也有失败的，但是对我来说所有的失败在某种意义上都是一种成功，特别是我下面写的这些，写这篇文章的时候，我已经签了南京的一家软件公司，但是想起今年2月21日我面试苏州台湾的IT公司的经历联想到我们现在&nbsp;<br />学习编程的一些情况我真的深有感触，这次面试使我深深的体会到了失败但也收获了很多。我要说的将分成三部分，1.是&nbsp;<br />我面试的具体经过2.是由面试想到的3.现今我应该做的。当然这些话很大程度上是我个人的意见，不可能完全得到大家的赞同，所以&nbsp;<br />在某些观点上如果哪位朋友觉得跟我的有很大出入，请不要介意，也不要对我攻击，就当我没有说过，欢迎和我联系共同探讨这些问题！我的EMAIL:wutao8@263.net&nbsp;<br />1.面试经过&nbsp;<br />大约在年前我接到了台湾瑞晟(Realtek)苏州公司的面试通知，通知我2月21日到苏州工业园区面试，接到面试后的几天我把一些专业&nbsp;<br />课温习了一遍，特别是C++和数据结构，由于大学几年里，我一直专研这些方面，加上通过了高级程序员的考试，对于一些常用的算法&nbsp;<br />我差不多也达到了烂熟于胸的地步，当时的感觉是如果问了我这些方面的问题我应该是没有问题的！&nbsp;<br />21日那天我被安排在4：30面试，由一位技术人员单独给我面试，在问了一些简单的问题之后他给我出了一道编程题目，题目是这样的：&nbsp;<br />(由于具体面试的题目比较烦琐，我将其核心思想提取出来分解成了两个独立的简单的问题，有可能问题分解的不当，请大家见谅，实际面试了一个的问题但比其复杂很多，而且涉及一些高等数学变换)&nbsp;<br />1)&nbsp;写一个函数计算当参数为n(n很大)时的值&nbsp;1-2+3-4+5-6+7......+n&nbsp;<br />哼，我的心里冷笑一声！没想到这么简单，我有点紧张的心情顿时放松起来！&nbsp;<br />于是很快我给出我的解法：&nbsp;<br />long&nbsp;fn(long&nbsp;n)&nbsp;<br />{&nbsp;<br />long&nbsp;temp=0;&nbsp;<br />int&nbsp;i,flag=1;&nbsp;<br />if(n&lt;=0)&nbsp;<br />{&nbsp;<br />printf(&quot;error:&nbsp;n&nbsp;must&nbsp;&gt;&nbsp;0);&nbsp;<br />exit(1);&nbsp;<br />}&nbsp;<br />for(i=1;i&lt;=n;i++)&nbsp;<br />{&nbsp;<br />temp=temp+flag*i;&nbsp;<br />flag=(-1)*flag;&nbsp;<br />}&nbsp;<br />return&nbsp;temp;&nbsp;<br />}&nbsp;<br />搞定！当我用期待的目光看着面试官的时候，他微笑着跟我说，执行结果肯定是没有问题！但当n很大的时候我这个程序执行效率很低，&nbsp;<br />在嵌入式系统的开发中，程序的运行效率很重要，能让CPU少执行一条指令都是好的，他让我看看这个程序还有什么可以修改的地方，&nbsp;<br />把程序优化一下！听了这些话，我的心情当时变的有点沉重，没想到他的要求很严格，之后我对程序进行了严格的分析，给出了改进了的方案！&nbsp;<br />long&nbsp;fn(long&nbsp;n)&nbsp;<br />{&nbsp;<br />long&nbsp;temp=0;&nbsp;<br />int&nbsp;j=1,i=1,flag=1;&nbsp;<br />if(n&lt;=0)&nbsp;<br />{&nbsp;<br />printf(&quot;error:&nbsp;n&nbsp;must&nbsp;&gt;&nbsp;0);&nbsp;<br />exit(1);&nbsp;<br />}&nbsp;<br />while(j&lt;=n)&nbsp;<br />{&nbsp;<br />temp=temp+i;&nbsp;<br />i=-i;&nbsp;<br />i&gt;0?i++:i--;&nbsp;<br />j++;&nbsp;<br />}&nbsp;<br />return&nbsp;temp;&nbsp;<br />}&nbsp;<br />虽然我不敢保证我这个算法是最优的，但是比起上一个程序，我将所有涉及到乘法指令的语句改为执行加法指令，既达到要题目的要求而且&nbsp;<br />运算时间上缩短了很多！而代价仅仅是增加了一个整型变量！但是我现在的信心已经受了一点打击，我将信将疑的看者面试官，他还是微笑着&nbsp;<br />跟我说：&ldquo;不错，这个程序确实在效率上有的很大的提高！&rdquo;我心里一阵暗喜！但他接着说这个程序仍然不能达到他的要求，要我给出更优的&nbsp;<br />方案！天啊！还有优化！我当时真的有点崩溃了，想了一会后，我请求他给出他的方案！然后他很爽快的给出了他的程序！&nbsp;<br />long&nbsp;fn(long&nbsp;n)&nbsp;<br />{&nbsp;<br />if(n&lt;=0)&nbsp;<br />{&nbsp;<br />printf(&quot;error:&nbsp;n&nbsp;must&nbsp;&gt;&nbsp;0);&nbsp;<br />exit(1);&nbsp;<br />}&nbsp;<br />if(0==n%2)&nbsp;<br />return&nbsp;(n/2)*(-1);&nbsp;<br />else&nbsp;<br />return&nbsp;(n/2)*(-1)+n;&nbsp;<br />}&nbsp;<br /><br />搞笑，当时我目瞪口呆，没想到他是这个意思，这么简单的代码我真的不会写吗，但是我为什么没有往那方面上想呢！他说的没有错，在n很大很大的时候这三个程序运行时间的差别简直是天壤之别！当我刚想开口说点什么的时候，他却先开口了：&ldquo;不要认为CPU运算速度快就把所有的问题都推给它去做，程序员应该将代码优化再优化，我们自己能做的决不要让CPU做，因为CPU是为用户服务的，不是为我们&nbsp;<br />程序员服务的！&rdquo;多么精辟的语言，我已经不想再说什么了！接着是第二个问题：&nbsp;<br />2),他要求我用一种技巧性的编程方法来用一个函数实现两个函数的功能n为如：fn1(n)=n/2!+n/3!+n/4!+n/5!+n/6!&nbsp;<br />fn2(n)=n/5!+n/6!+n/7!+n/8!+n/9!&nbsp;现在用一个函数fn(int&nbsp;n,int&nbsp;flag)实现，当flag为0时，实现fn1功能，如果flag为1时&nbsp;<br />实现fn2功能！他的要求还是效率，效率，效率！说实在话，如果我心情好的话我应该能给出一种比较好的算法，但我那时真的没有什么心思&nbsp;<br />再想了，我在纸上胡乱画了一些诸如6!=6*5!的公式后直截了当的跟他说要他给出他的答案！面试官也没有说什么，给出了他的思路：&nbsp;<br />定义一个二维数组&nbsp;float&nbsp;t[2][5]存入[2!,3!,4!,5!,6!},{5!,6!,7!,8!,9!]然后给出一个循环：&nbsp;<br /><br />for(i=0;i&lt;6;i++)&nbsp;<br />{&nbsp;<br />temp=temp+n/t[flag];&nbsp;<br />}&nbsp;<br />最后得到计算值！呵呵，典型的空间换时间的算法！&nbsp;<br />这些总共花了50分钟的时间，还有十分钟我就跟他很随意的聊聊天，聊了一些编程以及生活的问题，那时的我已经很放松了，&nbsp;<br />因为我知道这次面试结果只有一个：失败。5:30的时候面试官要我等通知，于是我离开了他们公司。这就是面试的整个经过！&nbsp;<br /><br />2.由面试想到的&nbsp;<br /><br />真的是很失败啊！我记得那天下好大的雨，气温也很低，我边走边想，从5:30一直走到7:30，全身都湿透了，又冷又饿，但是我只是一直走，脑子里面充满了疑惑，我也想让雨把自己淋醒！看到这里有些朋友可能觉得那些面试题目不算什么如果让自己做的话肯定能全部答对，我肯定相信你，因为我从未怀疑过中国程序员的能力，我认为中国有世界上最好的程序员，我也从未认为自己是高手，所以我做不出来不代表中国程序员比台湾或者别的地方的程序员差，所以我就从我的角度，我的所见所想来谈一些感想：&nbsp;<br /><br />不错全世界都有优秀的程序员，中国也不例外，但是我疑惑的是：到底中国和台湾或者国外的优秀的程序员的比例到底是多少？台湾我不知道，中国100个程序员里有几个是优秀的呢?我根本算不上，从上面的表现就足以说明一切了！是1个？5个？10个？50个？这个数字我不敢乱猜，恐遭网友一顿痛骂，那么我们国内有多少人学习计算机呢？拿我们学校来说，计算机97级4个班，98级5个班，99级10个班，2000级17个班，人多了，老师怎么办？我们学校的做法是让研究生上课，然后呢？补考一抓一大把，大把大把的补考费落入了学校的口袋，还说现在的学生素质低！真是好笑，我都不知道学校这么做是为了什么，为国内培养大量的程序员吗？学生们能真正学到计算机知识吗？好了，我敢讲，在我们学校学习编程学生和优秀程序员(注意我指的是优秀，只会编几个糟烂程序的人算不上)的比例应该是100:0.1&nbsp;<br />在这种比例下虽然我们中国学习编程的人铺天盖地，但是想想有多少个人能真正为中国软件业发展作出贡献，有多少人能真正写出优秀的程序名扬海外！&nbsp;<br /><br />我从学习编程以来，不管是自学还是老师指导，从来都是解决问题就好，编出程序来就行，我的疑惑是：我们有真正的强调过程序的效率，程序的质量吗？我们有仔细分析过我们写的东西，看看有没有可以改进的地方，看看有没有简单的方法来达到同样的目的呢？我问心自问，我发现，我从来没有对我写出来的程序进行过优化，最多就是进行详细的测试，然后Debug，但是这就足够了吗？这些天我偶尔发现我曾经写过的一个游戏，那是一年前我刚加入www.vcroad.net做为其中一员时候，感觉应该拿点东西出来，然后花了一个星期的时间写出来的！程序不算复杂，但是用到了不少数据结构的东西，也用到了一些精彩的算法，加上windows的界面和游戏的可玩性，写完后受到了不少好评，我当时真的很佩服自己！但是现在看呢：没有一句注释，好多丑陋的函数名比如：void&nbsp;chushihua()，好多没有必要的变量，可以用简单语句完成工作的我使用华丽的算法，大量使用全局变量.....,说不好听的话，六百多行的程序除了能运行之外就是一陀屎！如果一年前我能听到一些反面意见的话，大概我能早一点觉悟，但是自从原代码在网站发布以来听到的都是赞美之词，没有一个人向我提出程序改进的意见，这又说明了一个什么问题呢？很值得思考啊！&nbsp;<br /><br />还有一个疑惑是：我们说的和做的真的一样吗？我在学校的时候曾经受学院指派承办过一个计算机大赛，请了一个老师出决赛的题目，主要是一些算法题目，这个老师可能是我上大学以来唯一敬佩的老师了，从程序调试到打分，对于每个程序都仔细分析其时间效率和空间效率，然后综合打分，四十个人的卷子，老师从下午三点一直调试到晚上十点，在有些写的精彩的语句后还加上批注。我真是高兴很遇到这样的老师并且和他做深入的交流，但在事后，却发生了一件不愉快的事，在比赛中获得第二名的学生找到我，说他程序全部调试成功应该给他满分，并且应该得第一，我说不过他，最后调出了他的原程序和第一名的原程序对比，不错，两个程序都运行的很好，这时，那个同学开口了：&ldquo;我的程序写的十分简捷明了，仅仅数行就完成了题目要求，而他的却写了一大堆，为什么给他的分多过给我的分。&rdquo;我当时很是气愤，如果不是老师负责的话，那么现在第一名和第二名的位置真的要互调了，拜托，不是程序的行数越少程序的质量就越高，我记得我跟他大谈这方面的道理，最后说服他了！哈哈，但是我，只能说说而已，我不知道还有多少人一样，说起来头头是道，但心里却压根就从未重视过它！&nbsp;<br /><br />3.我打算做的！&nbsp;<br />其实那天我想到的远不止上面那么多，但是我不想再说了，因为我猜想看这篇文章的网友大概都有一肚子的感想，一肚子的抱怨，借用这篇文章发泄可不是我想达到的目的，在上面我把自己骂的一文不值也不是妄自菲薄，但是在某些方面我真的做错了，或者说是偏离了正确方向，现在是矫正方向和重整旗鼓的时候了，就象我前面说过的，我相信中国有世界上最好的程序员，我也相信我的水平不会一直保持现状，我现在就收拾起牢骚真正的实干起来！&nbsp;<br />真的很巧，就写到这里的时候我在网上偶尔发现了这篇手册，我不知道这预示着什么，但是我想如果我照下面这个基本原则一直踏实做下去，我一定会实现我的理想---一名优秀的软件设计师!&nbsp;<br /><br />（下面这些文字不是我的原创，是我偶尔在网上发现的，我真的很幸运能看到这些，这篇文章也随着下面的文字而结束，我真心的希望您能从这篇文章中得到启发,这篇文章欢迎大家随意转载，您可以不写作者是谁，但是请您写上www.vcroad.net原创，谢谢您的支持）&nbsp;<br /><br />作者：金蝶中间件公司CTO袁红岗&nbsp;<br /><br />不知不觉做软件已经做了十年，有成功的喜悦，也有失败的痛苦，但总不敢称自己是高手，因为和我心目中真正的高手们比起来，还差的太远。世界上并没有成为高手的捷径，但一些基本原则是可以遵循的。&nbsp;<br /><br />　　1.&nbsp;扎实的基础。数据结构、离散数学、编译原理，这些是所有计算机科学的基础，如果不掌握他们，很难写出高水平的程序。据我的观察，学计算机专业的人比学其他专业的人更能写出高质量的软件。程序人人都会写，但当你发现写到一定程度很难再提高的时候，就应该想想是不是要回过头来学学这些最基本的理论。不要一开始就去学OOP，即使你再精通OOP，遇到一些基本算法的时候可能也会束手无策。&nbsp;<br /><br />　　2.&nbsp;丰富的想象力。不要拘泥于固定的思维方式，遇到问题的时候要多想几种解决问题的方案，试试别人从没想过的方法。丰富的想象力是建立在丰富的知识的基础上，除计算机以外，多涉猎其他的学科，比如天文、物理、数学等等。另外，多看科幻电影也是一个很好的途径。&nbsp;<br /><br />　　3.&nbsp;最简单的是最好的。这也许是所有科学都遵循的一条准则，如此复杂的质能互换原理在爱因斯坦眼里不过是一个简单得不能再简单的公式：E=mc2。简单的方法更容易被人理解，更容易实现，也更容易维护。遇到问题时要优先考虑最简单的方案，只有简单方案不能满足要求时再考虑复杂的方案。&nbsp;<br /><br />　　4.&nbsp;不钻牛角尖。当你遇到障碍的时候，不妨暂时远离电脑，看看窗外的风景，听听轻音乐，和朋友聊聊天。当我遇到难题的时候会去玩游戏，而且是那种极暴力的打斗类游戏，当负责游戏的那部分大脑细胞极度亢奋的时候，负责编程的那部分大脑细胞就得到了充分的休息。当重新开始工作的时候，我会发现那些难题现在竟然可以迎刃而解。&nbsp;<br /><br />　　5.&nbsp;对答案的渴求。人类自然科学的发展史就是一个渴求得到答案的过程，即使只能知道答案的一小部分也值得我们去付出。只要你坚定信念，一定要找到问题的答案，你才会付出精力去探索，即使最后没有得到答案，在过程中你也会学到很多东西。&nbsp;<br /><br />　　6.&nbsp;多与别人交流。三人行必有我师，也许在一次和别人不经意的谈话中，就可以迸出灵感的火花。多上上网，看看别人对同一问题的看法，会给你很大的启发。&nbsp;<br /><br />　　7.&nbsp;良好的编程风格。注意养成良好的习惯，代码的缩进编排，变量的命名规则要始终保持一致。大家都知道如何排除代码中错误，却往往忽视了对注释的排错。注释是程序的一个重要组成部分，它可以使你的代码更容易理解，而如果代码已经清楚地表达了你的思想，就不必再加注释了，如果注释和代码不一致，那就更加糟糕。&nbsp;<br /><br />　　8.&nbsp;韧性和毅力。这也许是&quot;高手&quot;和一般程序员最大的区别。A&nbsp;good&nbsp;programming&nbsp;is&nbsp;99&nbsp;weat&nbsp;and&nbsp;1?ffee。高手们并不是天才，他们是在无数个日日夜夜中磨练出来的。成功能给我们带来无比的喜悦，但过程却是无比的枯燥乏味。你不妨做个测试，找个10000以内的素数表，把它们全都抄下来，然后再检查三遍，如果能够不间断地完成这一工作，你就可以满足这一条。&nbsp;</span></td>
        </tr>
    </tbody>
</table>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/112836.html</guid>
<subject></subject>
<author>getmc</author>
<category></category>
<pubDate>Wed, 10 May 2006 03:24:10 CST </pubDate>
</item>

<item>
<title>java中的abstract class和interface</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/110499.html</link>
<description>
<![CDATA[java中的abstract&nbsp;class和interface<br /><br />java中的abstract&nbsp;class和interface&nbsp;<br />abstract&nbsp;class和interface是Java语言中对于抽象类定义进行支持的两种机制，正是由于这两种机制的存在，才赋予了Java强大的面向对象能力。abstract&nbsp;class和interface之间在对于抽象类定义的支持方面具有很大的相似性，甚至可以相互替换，因此很多开发者在进行抽象类定义时对于abstract&nbsp;class和interface的选择显得比较随意。其实，两者之间还是有很大的区别的，对于它们的选择甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。本文将对它们之间的区别进行一番剖析，试图给开发者提供一个在二者之间进行选择的依据。&nbsp;<br />理解抽象类&nbsp;<br /><br />abstract&nbsp;class和interface在Java语言中都是用来进行抽象类（本文中的抽象类并非从abstract&nbsp;class翻译而来，它表示的是一个抽象体，而abstract&nbsp;class为Java语言中用于定义抽象类的一种方法，请读者注意区分）定义的，那么什么是抽象类，使用抽象类能为我们带来什么好处呢？&nbsp;<br /><br />在面向对象的概念中，我们知道所有的对象都是通过类来描绘的，但是反过来却不是这样。并不是所有的类都是用来描绘对象的，如果一个类中没有包含足够的信息来描绘一个具体的对象，这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、设计中得出的抽象概念，是对一系列看上去不同，但是本质上相同的具体概念的抽象。比如：如果我们进行一个图形编辑软件的开发，就会发现问题领域存在着圆、三角形这样一些具体概念，它们是不同的，但是它们又都属于形状这样一个概念，形状这个概念在问题领域是不存在的，它就是一个抽象概念。正是因为抽象的概念在问题领域没有对应的具体概念，所以用以表征抽象概念的抽象类是不能够实例化的。&nbsp;<br /><br />在面向对象领域，抽象类主要用来进行类型隐藏。我们可以构造出一个固定的一组行为的抽象描述，但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类，而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体，因此它可以是不允许修改的；同时，通过从这个抽象体派生，也可扩展此模块的行为功能。熟悉OCP的读者一定知道，为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed&nbsp;Principle)，抽象类是其中的关键所在。&nbsp;<br /><br />从语法定义层面看abstract&nbsp;class和interface&nbsp;<br /><br />在语法层面，Java语言对于abstract&nbsp;class和interface给出了不同的定义方式，下面以定义一个名为Demo的抽象类为例来说明这种不同。&nbsp;<br /><br />使用abstract&nbsp;class的方式定义Demo抽象类的方式如下：&nbsp;<br /><br />abstract&nbsp;class&nbsp;Demo&nbsp;｛&nbsp;<br />abstract&nbsp;void&nbsp;method1();&nbsp;<br />abstract&nbsp;void&nbsp;method2();&nbsp;<br />&hellip;&nbsp;<br />｝&nbsp;<br /><br /><br /><br />使用interface的方式定义Demo抽象类的方式如下：&nbsp;<br /><br />interface&nbsp;Demo&nbsp;{&nbsp;<br />void&nbsp;method1();&nbsp;<br />void&nbsp;method2();&nbsp;<br />&hellip;&nbsp;<br />}&nbsp;<br /><br /><br /><br />在abstract&nbsp;class方式中，Demo可以有自己的数据成员，也可以有非abstarct的成员方法，而在interface方式的实现中，Demo只能够有静态的不能被修改的数据成员（也就是必须是static&nbsp;final的，不过在interface中一般不定义数据成员），所有的成员方法都是abstract的。从某种意义上说，interface是一种特殊形式的abstract&nbsp;class。&nbsp;<br /><br />对于abstract&nbsp;class和interface在语法定义层面更多的细节问题，不是本文的重点，不再赘述，读者可以参阅参考文献〔1〕获得更多的相关内容。&nbsp;<br /><br />从编程层面看abstract&nbsp;class和interface&nbsp;<br /><br />从编程的角度来看，abstract&nbsp;class和interface都可以用来实现&quot;design&nbsp;by&nbsp;contract&quot;的思想。但是在具体的使用上面还是有一些区别的。&nbsp;<br /><br />首先，abstract&nbsp;class在Java语言中表示的是一种继承关系，一个类只能使用一次继承关系。但是，一个类却可以实现多个interface。也许，这是Java语言的设计者在考虑Java对于多重继承的支持方面的一种折中考虑吧。&nbsp;<br /><br />其次，在abstract&nbsp;class的定义中，我们可以赋予方法的默认行为。但是在interface的定义中，方法却不能拥有默认行为，为了绕过这个限制，必须使用委托，但是这会&nbsp;增加一些复杂性，有时会造成很大的麻烦。&nbsp;<br /><br />在抽象类中不能定义默认行为还存在另一个比较严重的问题，那就是可能会造成维护上的麻烦。因为如果后来想修改类的界面（一般通过abstract&nbsp;class或者interface来表示）以适应新的情况（比如，添加新的方法或者给已用的方法中添加新的参数）时，就会非常的麻烦，可能要花费很多的时间（对于派生类很多的情况，尤为如此）。但是如果界面是通过abstract&nbsp;class来实现的，那么可能就只需要修改定义在abstract&nbsp;class中的默认行为就可以了。&nbsp;<br /><br />同样，如果不能在抽象类中定义默认行为，就会导致同样的方法实现出现在该抽象类的每一个派生类中，违反了&quot;one&nbsp;rule，one&nbsp;place&quot;原则，造成代码重复，同样不利于以后的维护。因此，在abstract&nbsp;class和interface间进行选择时要非常的小心。&nbsp;<br /><br />从设计理念层面看abstract&nbsp;class和interface&nbsp;<br /><br />上面主要从语法定义和编程的角度论述了abstract&nbsp;class和interface的区别，这些层面的区别是比较低层次的、非本质的。本小节将从另一个层面：abstract&nbsp;class和interface所反映出的设计理念，来分析一下二者的区别。作者认为，从这个层面进行分析才能理解二者概念的本质所在。&nbsp;<br /><br />前面已经提到过，abstarct&nbsp;class在Java语言中体现了一种继承关系，要想使得继承关系合理，父类和派生类之间必须存在&quot;is&nbsp;a&quot;关系，即父类和派生类在概念本质上应该是相同的（参考文献〔3〕中有关于&quot;is&nbsp;a&quot;关系的大篇幅深入的论述，有兴趣的读者可以参考）。对于interface&nbsp;来说则不然，并不要求interface的实现者和interface定义在概念本质上是一致的，仅仅是实现了interface定义的契约而已。为了使论述便于理解，下面将通过一个简单的实例进行说明。&nbsp;<br /><br />考虑这样一个例子，假设在我们的问题领域中有一个关于Door的抽象概念，该Door具有执行两个动作open和close，此时我们可以通过abstract&nbsp;class或者interface来定义一个表示该抽象概念的类型，定义方式分别如下所示：&nbsp;<br /><br />使用abstract&nbsp;class方式定义Door：&nbsp;<br /><br />abstract&nbsp;class&nbsp;Door&nbsp;{&nbsp;<br />abstract&nbsp;void&nbsp;open();&nbsp;<br />abstract&nbsp;void&nbsp;close()；&nbsp;<br />}&nbsp;<br /><br /><br /><br />使用interface方式定义Door：&nbsp;<br /><br />interface&nbsp;Door&nbsp;{&nbsp;<br />void&nbsp;open();&nbsp;<br />void&nbsp;close();&nbsp;<br />}&nbsp;<br /><br /><br /><br />其他具体的Door类型可以extends使用abstract&nbsp;class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract&nbsp;class和interface没有大的区别。&nbsp;<br /><br />如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢（在本例中，主要是为了展示abstract&nbsp;class和interface反映在设计理念上的区别，其他方面无关的问题都做了简化或者忽略）？下面将罗列出可能的解决方案，并从设计理念层面对这些不同的方案进行分析。&nbsp;<br /><br />解决方案一：&nbsp;<br /><br />简单的在Door的定义中增加一个alarm方法，如下：&nbsp;<br /><br />abstract&nbsp;class&nbsp;Door&nbsp;{&nbsp;<br />abstract&nbsp;void&nbsp;open();&nbsp;<br />abstract&nbsp;void&nbsp;close()；&nbsp;<br />abstract&nbsp;void&nbsp;alarm();&nbsp;<br />}&nbsp;<br /><br /><br /><br />或者&nbsp;<br /><br />interface&nbsp;Door&nbsp;{&nbsp;<br />void&nbsp;open();&nbsp;<br />void&nbsp;close();&nbsp;<br />void&nbsp;alarm();&nbsp;<br />}&nbsp;<br /><br /><br /><br />那么具有报警功能的AlarmDoor的定义方式如下：&nbsp;<br /><br />class&nbsp;AlarmDoor&nbsp;extends&nbsp;Door&nbsp;{&nbsp;<br />void&nbsp;open()&nbsp;{&nbsp;&hellip;&nbsp;}&nbsp;<br />void&nbsp;close()&nbsp;{&nbsp;&hellip;&nbsp;}&nbsp;<br />void&nbsp;alarm()&nbsp;{&nbsp;&hellip;&nbsp;}&nbsp;<br />}&nbsp;<br /><br /><br /><br />或者&nbsp;<br /><br />class&nbsp;AlarmDoor&nbsp;implements&nbsp;Door&nbsp;｛&nbsp;<br />void&nbsp;open()&nbsp;{&nbsp;&hellip;&nbsp;}&nbsp;<br />void&nbsp;close()&nbsp;{&nbsp;&hellip;&nbsp;}&nbsp;<br />void&nbsp;alarm()&nbsp;{&nbsp;&hellip;&nbsp;}&nbsp;<br />｝&nbsp;<br /><br /><br /><br />这种方法违反了面向对象设计中的一个核心原则ISP（Interface&nbsp;Segregation&nbsp;Priciple），在Door的定义中把Door概念本身固有的行为方法和另外一个概念&quot;报警器&quot;的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为&quot;报警器&quot;这个概念的改变（比如：修改alarm方法的参数）而改变，反之依然。&nbsp;<br /><br />解决方案二：&nbsp;<br /><br />既然open、close和alarm属于两个不同的概念，根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有：这两个概念都使用abstract&nbsp;class方式定义；两个概念都使用interface方式定义；一个概念使用abstract&nbsp;class方式定义，另一个概念使用interface方式定义。&nbsp;<br /><br />显然，由于Java语言不支持多重继承，所以两个概念都使用abstract&nbsp;class方式定义是不可行的。后面两种方式都是可行的，但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。&nbsp;<br /><br />如果两个概念都使用interface方式来定义，那么就反映出两个问题：1、我们可能没有理解清楚问题领域，AlarmDoor在概念本质上到底是Door还是报警器？2、如果我们对于问题领域的理解没有问题，比如：我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的，那么我们在实现时就没有能够正确的揭示我们的设计意图，因为在这两个概念的定义上（均使用interface方式定义）反映不出上述含义。&nbsp;<br /><br />如果我们对于问题领域的理解是：AlarmDoor在概念本质上是Door，同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢？前面已经说过，abstract&nbsp;class在Java语言中表示一种继承关系，而继承关系在本质上是&quot;is&nbsp;a&quot;关系。所以对于Door这个概念，我们应该使用abstarct&nbsp;class方式来定义。另外，AlarmDoor又具有报警功能，说明它又能够完成报警概念中定义的行为，所以报警概念可以通过interface方式定义。如下所示：&nbsp;<br /><br />abstract&nbsp;class&nbsp;Door&nbsp;{&nbsp;<br />abstract&nbsp;void&nbsp;open();&nbsp;<br />abstract&nbsp;void&nbsp;close()；&nbsp;<br />}&nbsp;<br />interface&nbsp;Alarm&nbsp;{&nbsp;<br />void&nbsp;alarm();&nbsp;<br />}&nbsp;<br />class&nbsp;AlarmDoor&nbsp;extends&nbsp;Door&nbsp;implements&nbsp;Alarm&nbsp;{&nbsp;<br />void&nbsp;open()&nbsp;{&nbsp;&hellip;&nbsp;}&nbsp;<br />void&nbsp;close()&nbsp;{&nbsp;&hellip;&nbsp;}&nbsp;<br />void&nbsp;alarm()&nbsp;{&nbsp;&hellip;&nbsp;}&nbsp;<br />}&nbsp;<br /><br /><br /><br />这种实现方式基本上能够明确的反映出我们对于问题领域的理解，正确的揭示我们的设计意图。其实abstract&nbsp;class表示的是&quot;is&nbsp;a&quot;关系，interface表示的是&quot;like&nbsp;a&quot;关系，大家在选择时可以作为一个依据，当然这是建立在对问题领域的理解上的，比如：如果我们认为AlarmDoor在概念本质上是报警器，同时又具有Door的功能，那么上述的定义方式就要反过来了。&nbsp;<br /><br />结论&nbsp;<br /><br />abstract&nbsp;class和interface是Java语言中的两种定义抽象类的方式，它们之间有很大的相似性。但是对于它们的选择却又往往反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理，因为它们表现了概念间的不同的关系（虽然都能够实现需求的功能）。这其实也是语言的一种的惯用法，希望读者朋友能够细细体会。&nbsp;<br /><br />我再补充一个更加通俗的例子。<br />抽象类（abstract&nbsp;class）：家用电器（HomeAlliances）<br />接口（interface）：音量调节（VolumeControl）<br /><br />继承类：电视机(TV)，洗衣机（Washing）<br /><br />实现电视机的方法：<br />pulbic&nbsp;class&nbsp;TV&nbsp;extends&nbsp;HomeAlliances&nbsp;implements&nbsp;VolumeControl{<br />}<br /><br />实现洗衣机的方法（洗衣机不具有音量调节功能）：<br />public&nbsp;class&nbsp;Washing&nbsp;extends&nbsp;HomeAlliances{<br />}<br /><br />大家可以细细体会抽象类同接口的区别。]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/110499.html</guid>
<subject></subject>
<author>getmc</author>
<category></category>
<pubDate>Sat, 06 May 2006 11:34:23 CST </pubDate>
</item>

<item>
<title>关于Java中各种修饰符与访问修饰符的说明</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/110495.html</link>
<description>
<![CDATA[<div align="center"><strong><font size="6"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体">关于</span><span style="FONT-SIZE: 16pt">Java</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体">中各种修饰符与访问修饰符的说明</span></font></strong></div>
<div><strong><font size="5"><span style="FONT-FAMILY: 黑体">类：</span></font></strong></div>
<div><em><span style="FONT-FAMILY: 宋体">访问修饰符</span>&nbsp;</em><em><span style="FONT-FAMILY: 宋体">修饰符</span>&nbsp;</em><strong>class </strong><strong><span style="FONT-FAMILY: 宋体">类名称</span></strong><em> extends </em><em><span style="FONT-FAMILY: 宋体">父类名称</span> implement </em><em><span style="FONT-FAMILY: 宋体">接口名称</span></em></div>
<div><span style="FONT-FAMILY: 宋体">（访问修饰符与修饰符的位置可以互换）</span></div>
<table style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="568" colspan="3">
            <div><strong><span style="FONT-FAMILY: 宋体">访问修饰符</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 59.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="79">
            <div><strong><span style="FONT-FAMILY: 宋体">名称</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 171pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="228">
            <div><strong><span style="FONT-FAMILY: 宋体">说明</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><strong><span style="FONT-FAMILY: 宋体">备注</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 59.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="79">
            <div>public</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 171pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="228">
            <div><span style="FONT-FAMILY: 宋体">可以被所有类访问（使用）</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div>public<span style="FONT-FAMILY: 宋体">类必须定义在和类名相同的同名文件中</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 59.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="79">
            <div>package</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 171pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="228">
            <div><span style="FONT-FAMILY: 宋体">可以被同一个包中的类访问（使用）</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><span style="FONT-FAMILY: 宋体">默认的访问权限，可以省略此关键字，可以定义在和</span>public<span style="FONT-FAMILY: 宋体">类的同一个文件中</span></div>
            </td>
        </tr>
    </tbody>
</table>
<div>&nbsp;</div>
<table style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="568" colspan="3">
            <div><strong><span style="FONT-FAMILY: 宋体">修饰符</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 59.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="79">
            <div><strong><span style="FONT-FAMILY: 宋体">名称</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 171pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="228">
            <div><strong><span style="FONT-FAMILY: 宋体">说明</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><strong><span style="FONT-FAMILY: 宋体">备注</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 59.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="79">
            <div>final</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 171pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="228">
            <div><span style="FONT-FAMILY: 宋体">使用此修饰符的类不能够被继承</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div>&nbsp;</div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 59.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="79">
            <div>abstract</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 171pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="228">
            <div><span style="FONT-FAMILY: 宋体">如果要使用</span>abstract<span style="FONT-FAMILY: 宋体">类，之前必须首先建一个继承</span>abstract<span style="FONT-FAMILY: 宋体">类的新类，新类中实现</span>abstract<span style="FONT-FAMILY: 宋体">类中的抽象方法。</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><span style="FONT-FAMILY: 宋体">类只要有一个</span>abstract<span style="FONT-FAMILY: 宋体">方法，类就必须定义为</span>abstract<span style="FONT-FAMILY: 宋体">，但</span>abstract<span style="FONT-FAMILY: 宋体">类不一定非要保护</span>abstract<span style="FONT-FAMILY: 宋体">方法不可</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 59.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="79">
            <div>&nbsp;</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 171pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="228">
            <div>&nbsp;</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div>&nbsp;</div>
            </td>
        </tr>
    </tbody>
</table>
<div>&nbsp;</div>
<div><strong><font size="5"><span style="FONT-FAMILY: 黑体">变量</span></font></strong></div>
<div style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt"><span style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>Java<span style="FONT-FAMILY: 宋体">中没有全局变量，只有方法变量、实例变量（类中的非静态变量）、类变量（类中的静态变量）。</span></div>
<div style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt"><span style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">方法中的变量不能够有访问修饰符。所以下面<strong>访问修饰符表</strong>仅针对于在类中定义的变量。</span></div>
<div style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt"><span style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">声明实例变量时，如果没有赋初值，将被初始化为</span>null<span style="FONT-FAMILY: 宋体">（引用类型）或者</span>0<span style="FONT-FAMILY: 宋体">、</span>false<span style="FONT-FAMILY: 宋体">（原始类型）。</span></div>
<div style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt"><span style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">可以通过实例变量初始化器来初始化较复杂的实例变量，实例变量初始化器是一个用</span>{}<span style="FONT-FAMILY: 宋体">包含的语句块，在类的构造器被调用时运行，运行于父类构造器之后，构造器之前。</span></div>
<div style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt"><span style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">类变量（静态变量）也可以通过类变量初始化器来进行初始化，类变量初始化器是一个用</span>static{}<span style="FONT-FAMILY: 宋体">包含的语句块，只可能被初始化一次。</span></div>
<table style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="568" colspan="3">
            <div><strong><span style="FONT-FAMILY: 宋体">访问修饰符</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div><strong><span style="FONT-FAMILY: 宋体">名称</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><strong><span style="FONT-FAMILY: 宋体">说明</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><strong><span style="FONT-FAMILY: 宋体">备注</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>public</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">可以被任何类访问</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div>&nbsp;</div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>protected</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">可以被同一包中的所有类访问</span></div>
            <div><span style="FONT-FAMILY: 宋体">可以被所有子类访问</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><span style="FONT-FAMILY: 宋体">子类没有在同一包中也可以访问</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>private</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">只能够被当前类的方法访问</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div>&nbsp;</div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div><span style="FONT-FAMILY: 宋体">缺省</span></div>
            <div><span style="FONT-FAMILY: 宋体">无访问修饰符</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">可以被同一包中的所有类访问</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><span style="FONT-FAMILY: 宋体">如果子类没有在同一个包中，也不能访问</span></div>
            </td>
        </tr>
    </tbody>
</table>
<div>&nbsp;</div>
<table style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="568" colspan="3">
            <div><strong><span style="FONT-FAMILY: 宋体">修饰符</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div><strong><span style="FONT-FAMILY: 宋体">名称</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><strong><span style="FONT-FAMILY: 宋体">说明</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><strong><span style="FONT-FAMILY: 宋体">备注</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>static</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">静态变量（又称为类变量，其它的称为实例变量）</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><span style="FONT-FAMILY: 宋体">可以被类的所有实例共享。</span></div>
            <div><span style="FONT-FAMILY: 宋体">并不需要创建类的实例就可以访问静态变量</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>final</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">常量，值只能够分配一次，不能更改</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><span style="FONT-FAMILY: 宋体">注意不要使用</span>const<span style="FONT-FAMILY: 宋体">，虽然它和</span>C<span style="FONT-FAMILY: 宋体">、</span>C++<span style="FONT-FAMILY: 宋体">中的</span>const<span style="FONT-FAMILY: 宋体">关键字含义一样</span></div>
            <div><span style="FONT-FAMILY: 宋体">可以同</span>static<span style="FONT-FAMILY: 宋体">一起使用，避免对类的每个实例维护一个拷贝</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>transient</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">告诉编译器，在类对象序列化的时候，此变量不需要持久保存</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><span style="FONT-FAMILY: 宋体">主要是因为改变量可以通过其它变量来得到，使用它是为了性能的问题</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>volatile</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">指出可能有多个线程修改此变量，要求编译器优化以保证对此变量的修改能够被正确的处理</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div>&nbsp;</div>
            </td>
        </tr>
    </tbody>
</table>
<div>&nbsp;</div>
<div><strong><font size="5"><span style="FONT-FAMILY: 黑体">方法</span></font></strong></div>
<div><em><span style="FONT-FAMILY: 宋体">访问修饰符</span> </em><em><span style="FONT-FAMILY: 宋体">修饰符</span> </em><em><span style="FONT-FAMILY: 宋体">返回类型</span></em> <strong><span style="FONT-FAMILY: 宋体">方法名称（</span></strong><em><span style="FONT-FAMILY: 宋体">参数列表</span></em><strong><span style="FONT-FAMILY: 宋体">）</span></strong><em>throws </em><em><span style="FONT-FAMILY: 宋体">违例列表</span></em></div>
<div style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt"><span style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">类的构造器方法不能够有修饰符、返回类型和</span>throws<span style="FONT-FAMILY: 宋体">子句</span></div>
<div style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt"><span style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">类的构造器方法被调用时，它首先调用父类的构造器方法，然后运行实例变量和静态变量的初始化器，然后才运行构造器本身。</span></div>
<div style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt"><span style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">如果构造器方法没有显示的调用一个父类的构造器，那么编译器会自动为它加上一个默认的</span>super()<span style="FONT-FAMILY: 宋体">，而如果父类又没有默认的无参数构造器，编译器就会报错。</span>super<span style="FONT-FAMILY: 宋体">必须是构造器方法的第一个子句。</span></div>
<div style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt"><span style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">注意理解</span>private<span style="FONT-FAMILY: 宋体">构造器方法的使用技巧。</span></div>
<table style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="568" colspan="3">
            <div><strong><span style="FONT-FAMILY: 宋体">访问修饰符</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div><strong><span style="FONT-FAMILY: 宋体">名称</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><strong><span style="FONT-FAMILY: 宋体">说明</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><strong><span style="FONT-FAMILY: 宋体">备注</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>public</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">可以从所有类访问</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div>&nbsp;</div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>protected</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">可以被同一包中的所有类访问</span></div>
            <div><span style="FONT-FAMILY: 宋体">可以被所有子类访问</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><span style="FONT-FAMILY: 宋体">子类没有在同一包中也可以访问</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>private</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">只能够被当前类的方法访问</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div>&nbsp;</div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div><span style="FONT-FAMILY: 宋体">缺省</span></div>
            <div><span style="FONT-FAMILY: 宋体">无访问修饰符</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">可以被同一包中的所有类访问</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><span style="FONT-FAMILY: 宋体">如果子类没有在同一个包中，也不能访问</span></div>
            </td>
        </tr>
    </tbody>
</table>
<div>&nbsp;</div>
<table style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="568" colspan="3">
            <div><strong><span style="FONT-FAMILY: 宋体">修饰符</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div><strong><span style="FONT-FAMILY: 宋体">名称</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><strong><span style="FONT-FAMILY: 宋体">说明</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><strong><span style="FONT-FAMILY: 宋体">备注</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>static</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">静态方法（又称为类方法，其它的称为实例方法）</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><span style="FONT-FAMILY: 宋体">提供不依赖于类实例的服务</span></div>
            <div><span style="FONT-FAMILY: 宋体">并不需要创建类的实例就可以访问静态方法</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>final</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">防止任何子类重载该方法</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><span style="FONT-FAMILY: 宋体">注意不要使用</span>const<span style="FONT-FAMILY: 宋体">，虽然它和</span>C<span style="FONT-FAMILY: 宋体">、</span>C++<span style="FONT-FAMILY: 宋体">中的</span>const<span style="FONT-FAMILY: 宋体">关键字含义一样</span></div>
            <div><span style="FONT-FAMILY: 宋体">可以同</span>static<span style="FONT-FAMILY: 宋体">一起使用，避免对类的每个实例维护一个拷贝</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>abstract</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">抽象方法，类中已声明而没有实现的方法</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><span style="FONT-FAMILY: 宋体">不能将</span>static<span style="FONT-FAMILY: 宋体">方法、</span>final<span style="FONT-FAMILY: 宋体">方法或者类的构造器方法声明为</span>abstract</div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>native</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">用该修饰符定义的方法在类中没有实现，而大多数情况下该方法的实现是用</span>C<span style="FONT-FAMILY: 宋体">、</span>C++<span style="FONT-FAMILY: 宋体">编写的。</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><span style="FONT-FAMILY: 宋体">参见</span>Sun<span style="FONT-FAMILY: 宋体">的</span>Java Native<span style="FONT-FAMILY: 宋体">接口（</span>JNI<span style="FONT-FAMILY: 宋体">），</span>JNI<span style="FONT-FAMILY: 宋体">提供了运行时加载一个</span>native<span style="FONT-FAMILY: 宋体">方法的实现，并将其于一个</span>Java<span style="FONT-FAMILY: 宋体">类关联的功能</span></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>synchronized</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">多线程的支持</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><span style="FONT-FAMILY: 宋体">当一个此方法被调用时，没有其它线程能够调用该方法，其它的</span>synchronized<span style="FONT-FAMILY: 宋体">方法也不能调用该方法，直到该方法返回</span></div>
            </td>
        </tr>
    </tbody>
</table>
<div>&nbsp;</div>
<div><strong><font size="5"><span style="FONT-FAMILY: 黑体">接口</span></font></strong></div>
<div><em><span style="FONT-FAMILY: 宋体">访问修饰符</span></em><strong> interface </strong><strong><span style="FONT-FAMILY: 宋体">接口名称</span></strong><em> extends </em><em><span style="FONT-FAMILY: 宋体">接口列表</span></em></div>
<div style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt"><span style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">接口不能够定义其声明的方法的任何实现</span></div>
<div style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt"><span style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体">接口中的变量总是需要定义为&ldquo;</span>public static final <span style="FONT-FAMILY: 宋体">接口名称&rdquo;，但可以不包含这些修饰符，编译器默认就是这样，显示的包含修饰符主要是为了程序清晰</span></div>
<table style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="568" colspan="3">
            <div><strong><span style="FONT-FAMILY: 宋体">访问修饰符</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div><strong><span style="FONT-FAMILY: 宋体">名称</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><strong><span style="FONT-FAMILY: 宋体">说明</span></strong></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div><strong><span style="FONT-FAMILY: 宋体">备注</span></strong></div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div>public</div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">所有</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div>&nbsp;</div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 77.4pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="103">
            <div><span style="FONT-FAMILY: 宋体">无访问修饰符（默认）</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 153pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="204">
            <div><span style="FONT-FAMILY: 宋体">同一个包内</span></div>
            </td>
            <td style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 195.7pt; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 0.5pt solid" valign="top" width="261">
            <div>&nbsp;</div>
            </td>
        </tr>
    </tbody>
</table>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/110495.html</guid>
<subject></subject>
<author>getmc</author>
<category></category>
<pubDate>Sat, 06 May 2006 11:18:48 CST </pubDate>
</item>

<item>
<title>JSP避免Form重复提交的三种方案</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/109044.html</link>
<description>
<![CDATA[<br /><br /><span style="FONT-SIZE: 12px">1 javascript ，设置一个变量，只允许提交一次。　 <br /><br />　　&lt;script language=&quot;javascript&quot;&gt; <br /><br />　　 var checkSubmitFlg = false; <br /><br />　　 function checkSubmit() { <br /><br />　　 if (checkSubmitFlg == true) { <br /><br />　　 return false; <br /><br />　　 } <br /><br />　　 checkSubmitFlg = true; <br /><br />　　 return true; <br /><br />　　 } <br /><br />　　 document.ondblclick = function docondblclick() { <br /><br />　　 window.event.returnValue = false; <br /><br />　　 } <br /><br />　　 document.onclick = function doconclick() { <br /><br />　　 if (checkSubmitFlg) { <br /><br />　　 window.event.returnValue = false; <br /><br />　　 } <br /><br />　　 } <br /><br />　　&lt;/script&gt; <br /><br />　　 <br /><br />　　&lt;html:form action=&quot;myAction.do&quot; method=&quot;post&quot; onsubmit=&quot;return checkSubmit();&quot;&gt;　　 <br /><br />　　2 还是javascript，将提交按钮或者image置为disable　　 <br /><br />　　 &lt;html:form action=&quot;myAction.do&quot; method=&quot;post&quot; <br /><br />　　 onsubmit=&quot;getElById('submitInput').disabled = true; return true;&quot;&gt; 　　 <br /><br />　　 &lt;html:image styleId=&quot;submitInput&quot; src=&quot;images/ok_b.gif&quot; border=&quot;0&quot; /&gt;　 <br /><br />　　 &lt;/html:form&gt;　　 <br /><br />　　3 利用struts的同步令牌机制　　 <br /><br />　　利用同步令牌（Token）机制来解决Web应用中重复提交的问题，Struts也给出了一个参考实现。 <br /><br />　　基本原理：　 <br /><br />　　服务器端在处理到达的请求之前，会将请求中包含的令牌值与保存在当前用户会话中的令牌值进行比较，看是否匹配。在处理完该请求后，且在答复发送给客户端之前，将会产生一个新的令牌，该令牌除传给客户端以外，也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话，客户端传过来的令牌就和服务器端的令牌不一致，从而有效地防止了重复提交的发生。　 <br /><br />　　if (isTokenValid(request, true)) { <br /><br />　　 // your code here <br /><br />　　 return mapping.findForward(&quot;success&quot;); <br /><br />　　} else { <br /><br />　　 saveToken(request); <br /><br />　　 return mapping.findForward(&quot;submitagain&quot;); <br /><br />　　}　 <br /><br />　　Struts根据用户会话ID和当前系统时间来生成一个唯一（对于每个会话）令牌的，具体实现可以参考TokenProcessor类中的generateToken()方法。　　 <br /><br />　　1. //验证事务控制令牌,&lt;html:form &gt;会自动根据session中标识生成一个隐含input代表令牌，防止两次提交 <br /><br />　　2. 在action中：　　 <br /><br />　　 //&lt;input type=&quot;hidden&quot; name=&quot;org.apache.struts.taglib.html.TOKEN&quot; <br /><br />　　 // value=&quot;6aa35341f25184fd996c4c918255c3ae&quot;&gt; <br /><br />　　 if (!isTokenValid(request)) <br /><br />　　 errors.add(ActionErrors.GLOBAL_ERROR, <br /><br />　　 new ActionError(&quot;error.transaction.token&quot;)); <br /><br />　　 resetToken(request); //删除session中的令牌　　 <br /><br />　　3. action有这样的一个方法生成令牌　　 <br /><br />　　 protected String generateToken(HttpServletRequest request) {　　 <br /><br />　　 HttpSession session = request.getSession(); <br /><br />　　 try { <br /><br />　　 byte id[] = session.getId().getBytes(); <br /><br />　　 byte now[] = <br /><br />　　 new Long(System.currentTimeMillis()).toString().getBytes(); <br /><br />　　 MessageDigest md = MessageDigest.getInstance(&quot;MD5&quot;); <br /><br />　　 md.update(id); <br /><br />　　 md.update(now); <br /><br />　　 return (toHex(md.digest())); <br /><br />　　 } catch (IllegalStateException e) { <br /><br />　　 return (null); <br /><br />　　 } catch (NoSuchAlgorithmException e) { <br /><br />　　 return (null); <br /><br />　　 } <br /><br />　　 } 　　</span> <br />]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/109044.html</guid>
<subject></subject>
<author>getmc</author>
<category></category>
<pubDate>Thu, 04 May 2006 10:15:13 CST </pubDate>
</item>

<item>
<title>Java中Static、this、super、final用法</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/106633.html</link>
<description>
<![CDATA[一、Static
<p>&nbsp;</p>
<p>请先看下面这段程序：</p>
<p>TER&gt; public class Hello<br />{<br />&nbsp;&nbsp;&nbsp; public static void main(String[] args)<br />&nbsp;{<br />&nbsp;//(1)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;Hello,world!&quot;);<br />&nbsp;&nbsp; //(2)<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; }<br />&nbsp;<br />看过这段程序，对于大多数学过Java的从来说，都不陌生。即使没有学过Java，而学过其它的高级语言，例如C，那你也应该能看懂这段代码的意思。它只是简单的输出&ldquo;Hello,world&rdquo;，一点别的用处都没有，然而，它却展示了static关键字的主要用法。 </p>
<p>在1处，我们定义了一个静态的方法名为main，这就意味着告诉Java编译器，我这个方法不需要创建一个此类的对象即可使用。你还得你是怎么运行这个程序吗？一般，我们都是在命令行下，打入如下的命令(加下划线为手动输入)： </p>
<p>javac Hello.java<br />java Hello<br />Hello,world!<br />&nbsp;<br />这就是你运行的过程，第一行用来编译Hello.java这个文件，执行完后，如果你查看当前，会发现多了一个Hello.class文件，那就是第一行产生的Java二进制字节码。第二行就是执行一个Java程序的最普遍做法。 </p>
<p>执行结果如你所料。在2中，你可能会想，为什么要这样才能输出。好，我们来分解一下这条语句。（如果没有安装Java文档，请到Sun的官方网站浏览J2SE API）首先，System是位于java.lang包中的一个核心类，如果你查看它的定义，你会发现有这样一行： </p>
<p>public static final PrintStream out;<br />&nbsp;<br />接着在进一步，点击PrintStream这个超链接，在METHOD页面，你会看到大量定义的方法，查找println，会有这样一行： </p>
<p>public void println(String x)<br />&nbsp;<br />好了，现在你应该明白为什么我们要那样调用了，out是System的一个静态变量，所以可以直接使用，而out所属的类有一个println方法。 </p>
<p>静态方法 </p>
<p>通常，在一个类中定义一个方法为static，那就是说，无需本类的对象即可调用此方法。如下所示： </p>
<p>class Simple<br />{<br />&nbsp;&nbsp; static void go()<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;Go...&quot;);<br />&nbsp;&nbsp; }<br />}<br />public class Cal<br />{<br />&nbsp; public static void main(String[] args)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; Simple.go();<br />&nbsp; }<br />}<br />&nbsp;<br />调用一个静态方法就是&ldquo;类名.方法名&rdquo;,静态方法的使用很简单如上所示。一般来说，静态方法常常为应用程序中的其它类提供一些实用工具所用，在Java的类库中大量的静态方法正是出于此目的而定义的。 </p>
<p>静态变量 </p>
<p>静态变量与静态方法类似。所有此类实例共享此静态变量，也就是说在类装载时，只分配一块存储空间，所有此类的对象都可以操控此块存储空间，当然对于final则另当别论了。看下面这段代码： </p>
<p>class Value<br />{<br />&nbsp; static int c=0;<br />&nbsp; static void inc()<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; c++;<br />&nbsp; }<br />}<br />class Count<br />{<br />&nbsp; public static void prt(String s)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; System.out.println(s);<br />&nbsp; }<br />&nbsp; public static void main(String[] args)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; Value v1,v2;<br />&nbsp;&nbsp;&nbsp; v1=new Value();<br />&nbsp;&nbsp;&nbsp; v2=new Value();<br />&nbsp;&nbsp;&nbsp; prt(&quot;v1.c=&quot;+v1.c+&quot;&nbsp; v2.c=&quot;+v2.c);<br />&nbsp;&nbsp;&nbsp; v1.inc();<br />&nbsp;&nbsp;&nbsp; prt(&quot;v1.c=&quot;+v1.c+&quot;&nbsp; v2.c=&quot;+v2.c);&nbsp; <br />&nbsp; }<br />}<br />&nbsp;<br />结果如下： </p>
<p>v1.c=0&nbsp; v2.c=0<br />v1.c=1&nbsp; v2.c=1<br />&nbsp;<br />由此可以证明它们共享一块存储区。static变量有点类似于C中的全局变量的概念。值得探讨的是静态变量的初始化问题。我们修改上面的程序： </p>
<p><br />class Value{<br />&nbsp; static int c=0;<br />&nbsp; Value(){<br />&nbsp;&nbsp;&nbsp; c=15;<br />&nbsp; }<br />&nbsp; Value(int i){<br />&nbsp;&nbsp;&nbsp; c=i;<br />&nbsp; }<br />&nbsp; static void inc(){<br />&nbsp;&nbsp;&nbsp; c++;<br />&nbsp; }<br />}<br />class Count{<br />&nbsp; public static void prt(String s){<br />&nbsp;&nbsp;&nbsp; System.out.println(s);<br />&nbsp; }<br />&nbsp;&nbsp;&nbsp; Value v=new Value(10);<br />&nbsp;&nbsp;&nbsp; static Value v1,v2;<br />&nbsp;&nbsp;&nbsp; static{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prt(&quot;v1.c=&quot;+v1.c+&quot; <br />&nbsp;&nbsp; v2.c=&quot;+v2.c);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v1=new Value(27);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prt(&quot;v1.c=&quot;+v1.c+&quot; <br />&nbsp;&nbsp; v2.c=&quot;+v2.c);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v2=new Value(15);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; prt(&quot;v1.c=&quot;+v1.c+&quot; <br />&nbsp;&nbsp; v2.c=&quot;+v2.c);<br />&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp; public static void main<br />&nbsp; (String[] args)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; Count ct=new Count();<br />&nbsp;&nbsp;&nbsp; prt(&quot;ct.c=&quot;+ct.v.c);<br />&nbsp;&nbsp;&nbsp; prt(&quot;v1.c=&quot;+v1.c+&quot;&nbsp; v2.c=&quot;+v2.c);<br />&nbsp;&nbsp;&nbsp; v1.inc();<br />&nbsp;&nbsp;&nbsp; prt(&quot;v1.c=&quot;+v1.c+&quot;&nbsp; v2.c=&quot;+v2.c);<br />&nbsp;&nbsp;&nbsp; prt(&quot;ct.c=&quot;+ct.v.c);<br />&nbsp; }<br />}<br />&nbsp;<br />运行结果如下： </p>
<p>v1.c=0&nbsp; v2.c=0<br />v1.c=27&nbsp; v2.c=27<br />v1.c=15&nbsp; v2.c=15<br />ct.c=10<br />v1.c=10&nbsp; v2.c=10<br />v1.c=11&nbsp; v2.c=11<br />ct.c=11<br />&nbsp;<br />这个程序展示了静态初始化的各种特性。如果你初次接触Java，结果可能令你吃惊。可能会对static后加大括号感到困惑。首先要告诉你的是，static定义的变量会优先于任何其它非static变量，不论其出现的顺序如何。 </p>
<p>正如在程序中所表现的，虽然v出现在v1和v2的前面，但是结果却是v1和v2的初始化在v的前面。在static{后面跟着一段代码，这是用来进行显式的静态变量初始化，这段代码只会初始化一次，且在类被第一次装载时。 </p>
<p>如果你能读懂并理解这段代码，会帮助你对static关键字的认识。在涉及到继承的时候，会先初始化父类的static变量，然后是子类的，依次类推。非静态变量不是本文的主题，在此不做详细讨论，请参考Think in Java中的讲解。</p>
<p>静态类</p>
<p>通常一个普通类不允许声明为静态的，只有一个内部类才可以。这时这个声明为静态的内部类可以直接作为一个普通类来使用，而不需实例一个外部类。如下代码所示：</p>
<p>public class StaticCls<br />{<br />&nbsp; public static void <br />&nbsp; main(String[] args)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; OuterCls.InnerCls<br />&nbsp;oi=new OuterCls.InnerCls();<br />&nbsp; }<br />}<br />class OuterCls{<br />&nbsp; public static class InnerCls{<br />&nbsp;&nbsp;&nbsp; InnerCls(){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;InnerCls&quot;);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp; }<br />}<br />&nbsp;<br />输出结果会如你所料： </p>
<p>InnerCls<br />&nbsp;<br />和普通类一样。 </p>
<p>二、this &amp; super </p>
<p>在上一篇拙作中，我们讨论了static的种种用法，通过用static来定义方法或成员，为我们编程提供了某种便利，从某种程度上可以说它类似于C语言中的全局函数和全局变量。但是，并不是说有了这种便利，你便可以随处使用，如果那样的话，你便需要认真考虑一下自己是否在用面向对象的思想编程，自己的程序是否是面向对象的。好了，现在开始讨论this&amp;super这两个关键字的意义和用法。 </p>
<p>在Java中，this通常指当前对象，super则指父类的。当你想要引用当前对象的某种东西，比如当前对象的某个方法，或当前对象的某个成员，你便可以利用this来实现这个目的，当然，this的另一个用途是调用当前对象的另一个构造函数，这些马上就要讨论。 </p>
<p>如果你想引用父类的某种东西，则非super莫属。由于this与super有如此相似的一些特性和与生俱来的某种关系，所以我们在这一块儿来讨论，希望能帮助你区分和掌握它们两个。 </p>
<p>在一般方法中 </p>
<p>最普遍的情况就是，在你的方法中的某个形参名与当前对象的某个成员有相同的名字，这时为了不至于混淆，你便需要明确使用this关键字来指明你要使用某个成员，使用方法是&ldquo;this.成员名&rdquo;，而不带this的那个便是形参。 </p>
<p>另外，还可以用&ldquo;this.方法名&rdquo;来引用当前对象的某个方法，但这时this就不是必须的了，你可以直接用方法名来访问那个方法，编译器会知道你要调用的是那一个。下面的代码演示了上面的用法： </p>
<p>public class DemoThis<br />{<br />&nbsp; private String name;<br />&nbsp; private int age;<br />&nbsp; DemoThis(String name,int age)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; setName(name);<br />&nbsp;//你可以加上this来调用方法，<br />&nbsp;像这样：this.setName(name);<br />&nbsp;但这并不是必须的<br />&nbsp;&nbsp;&nbsp; setAge(age);<br />&nbsp;&nbsp;&nbsp; this.print();<br />&nbsp; }&nbsp;&nbsp; <br />&nbsp; public void setName(String name)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; this.name=name;<br />&nbsp;//此处必须指明你要引用成员变量<br />&nbsp; }<br />&nbsp; public void setAge(int age)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; this.age=age;<br />&nbsp; }<br />&nbsp; public void print()<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; System.out.println<br />&nbsp;(&quot;Name=&quot;+name+&quot; Age=&quot;+age);<br />&nbsp;//在此行中并不需要用this，<br />&nbsp;因为没有会导致混淆的东西<br />&nbsp; }<br />&nbsp; public static void main(String[] args)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; DemoThis dt=new DemoThis(&quot;Kevin&quot;,&quot;22&quot;);<br />&nbsp; }<br />}<br />&nbsp;<br />这段代码很简单，不用解释你也应该能看明白。在构造函数中你看到用this.print(),你完全可以用print()来代替它，两者效果一样。下面我们修改这个程序，来演示super的用法。 </p>
<p>class Person<br />{<br />&nbsp; public int c;<br />&nbsp; private String name;<br />&nbsp; private int age;<br />&nbsp; protected void setName<br />&nbsp; (String name)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; this.name=name;<br />&nbsp; }<br />&nbsp; protected void setAge(int age)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; this.age=age;<br />&nbsp; }<br />&nbsp; protected void print()<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; System.out.println<br />&nbsp;(&quot;Name=&quot;+name+&quot; Age=&quot;+age);<br />&nbsp; }<br />}<br />public class DemoSuper <br />extends Person<br />{<br />&nbsp; public void print()<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; System.out.println(&quot;DemoSuper:&quot;);<br />&nbsp;&nbsp;&nbsp; super.print();<br />&nbsp; }<br />&nbsp; public static void main(String[] args)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; DemoSuper ds=new DemoSuper();<br />&nbsp;&nbsp;&nbsp; ds.setName(&quot;kevin&quot;);<br />&nbsp;&nbsp;&nbsp; ds.setAge(22);<br />&nbsp;&nbsp;&nbsp; ds.print();<br />&nbsp; }<br />}<br />&nbsp;<br />在DemoSuper中，重新定义的print方法覆写了父类的print方法，它首先做一些自己的事情，然后调用父类的那个被覆写了的方法。输出结果说明了这一点： </p>
<p>DemoSuper:<br />Name=kevin Age=22<br />&nbsp;<br />这样的使用方法是比较常用的。另外如果父类的成员可以被子类访问，那你可以像使用this一样使用它，用&ldquo;super.父类中的成员名&rdquo;的方式，但常常你并不是这样来访问父类中的成员名的。 </p>
<p>在构造函数中 </p>
<p>构造函数是一种特殊的方法，在对象初始化的时候自动调用。在构造函数中，this和super也有上面说的种种使用方式，并且它还有特殊的地方，请看下面的例子： </p>
<p>class Person<br />{<br />&nbsp; public static void prt(String s)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; System.out.println(s);<br />&nbsp; }<br />&nbsp; Person(){<br />&nbsp;&nbsp;&nbsp; prt(&quot;A Person.&quot;);<br />&nbsp; }<br />&nbsp; Person(String name)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; prt(&quot;A person name is:&quot;+name);<br />&nbsp; }<br />}<br />public class Chinese extends Person{<br />&nbsp; Chinese(){<br />&nbsp;&nbsp;&nbsp; super();&nbsp; <br />&nbsp;//调用父类构造函数（1）<br />&nbsp;&nbsp;&nbsp; prt(&quot;A chinese.&quot;);<br />&nbsp;//(4)<br />&nbsp; }<br />&nbsp; Chinese(String name)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; super(name);<br />&nbsp;//调用父类具有相同形参的构造函数（2）<br />&nbsp;&nbsp;&nbsp; prt(&quot;his name is:&quot;+name);<br />&nbsp; }<br />&nbsp; Chinese(String name,int age){<br />&nbsp;&nbsp;&nbsp; this(name);<br />&nbsp;//调用当前具有相同形参的构造函数（3）<br />&nbsp;&nbsp;&nbsp; prt(&quot;his age is:&quot;+age);<br />&nbsp; }<br />&nbsp; public static void main<br />&nbsp; (String[] args)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; Chinese cn=new Chinese();<br />&nbsp;&nbsp;&nbsp; cn=new Chinese(&quot;kevin&quot;);<br />&nbsp;&nbsp;&nbsp; cn=new Chinese(&quot;kevin&quot;,22);<br />&nbsp; }<br />}<br />&nbsp;<br />在这段程序中，this和super不再是像以前那样用&ldquo;.&rdquo;连接一个方法或成员，而是直接在其后跟上适当的参数，因此它的意义也就有了变化。super后加参数的是用来调用父类中具有相同形式的构造函数，如1和2处。this后加参数则调用的是当前具有相同参数的构造函数，如3处。 </p>
<p>当然，在Chinese的各个重载构造函数中，this和super在一般方法中的各种用法也仍可使用，比如4处，你可以将它替换为&ldquo;this.prt&rdquo;(因为它继承了父类中的那个方法）或者是&ldquo;super.prt&rdquo;（因为它是父类中的方法且可被子类访问），它照样可以正确运行。但这样似乎就有点画蛇添足的味道了。 </p>
<p>最后，写了这么多，如果你能对&ldquo;this通常指代当前对象，super通常指代父类&rdquo;这句话牢记在心，那么本篇便达到了目的，其它的你自会在以后的编程实践当中慢慢体会、掌握。另外关于本篇中提到的继承，请参阅相关Java教程。 <br />三、final</p>
<p>final在Java中并不常用，然而它却为我们提供了诸如在C语言中定义常量的功能，不仅如此，final还可以让你控制你的成员、方法或者是一个类是否可被覆写或继承等功能，这些特点使final在Java中拥有了一个不可或缺的地位，也是学习Java时必须要知道和掌握的关键字之一。</p>
<p>final成员</p>
<p>当你在类中定义变量时，在其前面加上final关键字，那便是说，这个变量一旦被初始化便不可改变，这里不可改变的意思对基本类型来说是其值不可变，而对于对象变量来说其引用不可再变。</p>
<p>其初始化可以在两个地方，一是其定义处，也就是说在final变量定义时直接给其赋值，二是在构造函数中。这两个地方只能选其一，要么在定义时给值，要么在构造函数中给值，不能同时既在定义时给了值，又在构造函数中给另外的值。下面这段代码演示了这一点：</p>
<p>import java.util.List;<br />import java.util.ArrayList;<br />import java.util.LinkedList;<br />public class Bat<br />{<br />&nbsp;&nbsp;&nbsp; final PI=3.14;<br />&nbsp;//在定义时便给址值<br />&nbsp;&nbsp;&nbsp; final int i;<br />&nbsp;//因为要在构造函数中进行初始化，<br />&nbsp;所以此处便不可再给值<br />&nbsp;&nbsp;&nbsp; final List list;<br />&nbsp;//此变量也与上面的一样<br />&nbsp;&nbsp;&nbsp; Bat(){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i=100;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list=new LinkedList();<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; Bat(int ii,List l){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i=ii;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list=l;<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; public static void <br />&nbsp;main(String[] args)<br />&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bat b=new Bat();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b.list.add(new Bat());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //b.i=25;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //b.list=new ArrayList();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;I=&quot;+b.i+&quot; <br />&nbsp;&nbsp;List Type:&quot;+b.list.getClass());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b=new Bat(23,new ArrayList());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b.list.add(new Bat());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;I=&quot;+b.i+&quot;<br />&nbsp;&nbsp;List Type:&quot;+b.list.getClass());<br />&nbsp;&nbsp;&nbsp; }<br />}<br />&nbsp;<br />此程序很简单的演示了final的常规用法。在这里使用在构造函数中进行初始化的方法，这使你有了一点灵活性。如Bat的两个重载构造函数所示，第一个缺省构造函数会为你提供默认的值，重载的那个构造函数会根据你所提供的值或类型为final变量初始化。 </p>
<p>然而有时你并不需要这种灵活性，你只需要在定义时便给定其值并永不变化，这时就不要再用这种方法。 </p>
<p>在main方法中有两行语句注释掉了，如果你去掉注释，程序便无法通过编译，这便是说，不论是i的值或是list的类型，一旦初始化，确实无法再更改。然而b可以通过重新初始化来指定i的值或list的类型，输出结果中显示了这一点： </p>
<p>I=100 List Type:class <br />java.util.LinkedList<br />I=23 List Type:class <br />java.util.ArrayList<br />&nbsp;<br />还有一种用法是定义方法中的参数为final，对于基本类型的变量，这样做并没有什么实际意义，因为基本类型的变量在调用方法时是传值的，也就是说你可以在方法中更改这个参数变量而不会影响到调用语句。 </p>
<p>然而对于对象变量，却显得很实用，因为对象变量在传递时是传递其引用，这样你在方法中对对象变量的修改也会影响到调用语句中的对象变量，当你在方法中不需要改变作为参数的对象变量时，明确使用final进行声明，会防止你无意的修改而影响到调用方法。 </p>
<p>另外方法中的内部类在用到方法中的参变量时，此参变也必须声明为final才可使用，如下代码所示： </p>
<p>public class INClass<br />{<br />&nbsp;&nbsp; void innerClass<br />&nbsp;&nbsp; (final String str)<br />&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class IClass<br />&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IClass()<br />&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(str);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IClass ic=new IClass();<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; public static void main<br />&nbsp; (String[] args)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; INClass inc=new INClass();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inc.innerClass(&quot;Hello&quot;);<br />&nbsp; }<br />}<br />&nbsp;<br />final方法 </p>
<p>将方法声明为final，那就说明你已经知道这个方法提供的功能已经满足你要求，不需要进行扩展，并且也不允许任何从此类继承的类来覆写这个方法，但是继承仍然可以继承这个方法，也就是说可以直接使用。 </p>
<p>另外有一种被称为inline的机制，它会使你在调用final方法时，直接将方法主体插入到调用处，而不是进行例行的方法调用，例如保存断点，压栈等，这样可能会使你的程序效率有所提高，然而当你的方法主体非常庞大时，或你在多处调用此方法，那么你的调用主体代码便会迅速膨胀，可能反而会影响效率，所以你要慎用final进行方法定义。 </p>
<p>final类 </p>
<p>当你将final用于类身上时，你就需要仔细考虑，因为一个final类是无法被任何人继承的，那也就意味着此类在一个继承树中是一个叶子类，并且此类的设计已被认为很完美而不需要进行修改或扩展。 </p>
<p>对于final类中的成员，你可以定义其为final，也可以不是final。而对于方法，由于所属类为final的关系，自然也就成了final型的。你也可以明确的给final类中的方法加上一个final，但这显然没有意义。 </p>
<p>下面的程序演示了final方法和final类的用法： </p>
<p>final class final<br />{<br />&nbsp;&nbsp;&nbsp; final String str=&quot;final Data&quot;;<br />&nbsp;&nbsp;&nbsp; public String str1=&quot;non final data&quot;;<br />&nbsp;&nbsp;&nbsp; final public void print()<br />&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println<br />&nbsp;&nbsp;(&quot;final method.&quot;);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; public void what()<br />&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(str+&quot;\n&quot;+str1);<br />&nbsp;&nbsp;&nbsp; }<br />}<br />public class FinalDemo <br />{&nbsp; <br />//extends final 无法继承 <br />&nbsp;&nbsp;&nbsp; public static void <br />&nbsp;main(String[] args)<br />&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final f=new final();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; f.what();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; f.print();<br />&nbsp;&nbsp;&nbsp; }<br />}<br />&nbsp;<br />从程序中可以看出，final类与普通类的使用几乎没有差别，只是它失去了被继承的特性。final方法与非final方法的区别也很难从程序行看出，只是记住慎用。 </p>
<p>final在设计模式中的应用 </p>
<p>在设计模式中有一种模式叫做不变模式，在Java中通过final关键字可以很容易的实现这个模式，在讲解final成员时用到的程序Bat.java就是一个不变模式的例子。 </p>
<p>到此为止，this,static,supert和final的使用已经说完了，如果你对这四个关键字已经能够大致说出它们的区别与用法，那便说明你基本已经掌握。 </p>
<p>然而，世界上的任何东西都不是完美无缺的，Java提供这四个关键字，给程序员的编程带来了很大的便利，但并不是说要让你到处使用，一旦达到滥用的程序，便适得其反，所以在使用时请一定要认真考虑。 <br /></p>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/106633.html</guid>
<subject></subject>
<author>getmc</author>
<category></category>
<pubDate>Sun, 30 Apr 2006 10:22:23 CST </pubDate>
</item>

<item>
<title>利用Digester把XML转换为Java对象</title>
<link>http://www.bokee.net/blogmodule/weblogcomment_viewEntry/105345.html</link>
<description>
<![CDATA[<p>在一个比较完整的应用系统里，经常需要有一些配置文件。简单的属性使用.properties文件即可，但要配置一些复杂对象，则应该考虑使用xml文件。一般用来读取xml文件的工具包有DOM、SAX和JDOM等，但用过的人都知道，它们属于比较底层的API，写起来代码量很大，而且如果修改了xml文件的格式，代码也要做大幅度的改动。Jakarta Commons项目里的<a href="http://jakarta.apache.org/commons/digester/">Digester</a>包，可以轻松实现xml文件到Java对象的转换，看下面这个例子。</p>
<p>在一个项目里，需要提供一些统计图，但图的内容暂时未能确定。所以我决定让图可以配置，所有定义保存在一个名为charts.xml（或国际化后的文件名如charts_zh_CN.xml，这里只考虑缺省语言）的文件内，下面是该文件的部分内容：</p>
<p>&nbsp;</p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<div><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" /><span style="COLOR: #0000ff">&lt;?</span><span style="COLOR: #ff00ff">xml&nbsp;version=&quot;1.0&quot;&nbsp;encoding=&quot;UTF-8&quot;&nbsp;</span><span style="COLOR: #0000ff">?&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" /></span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">charts</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">chart&nbsp;</span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">=&quot;BarChart1&quot;</span><span style="COLOR: #ff0000">&nbsp;</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">title</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">统计图一</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">title</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">legendVisible</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">false</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">legendVisible</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">toolTipsVisible</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">true</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">toolTipsVisible</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">type</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">Bar</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">type</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">labelx</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">时间</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">labelx</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">labely</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">数据</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">labely</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">width</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">500</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">width</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">height</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">360</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">height</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">hql</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">select&nbsp;count(c),c.department.name&nbsp;from&nbsp;edu.pku.pub.aims.model.business.Client&nbsp;c&nbsp;group&nbsp;by&nbsp;c.department</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">hql</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">description</span><span style="COLOR: #0000ff">&gt;&lt;/</span><span style="COLOR: #800000">description</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">chart</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" /></span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">charts</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" /></span></div>
</div>
<p>可以看出，我为每个图定义了id、title、legendVisible等等属性，这些属性的意义都很明显，它们将影响统计图的数据和在页面中的表现。在程序里，我需要把这个文件里的定义读到一个注册表类ChartRegistry里，该注册表维护一个java.util.List类型的registry变量，其中每个元素是一个ChartConfig类。现在Digester该显示它的价值了。</p>
<p>为了方便使用Digester，我们让ChartConfig也具有统计图的每个属性（id、title、legendVisible等等），名称与charts.xml里的<chart></chart>元素的属性（子元素）一一对应，并且都具有getter和setter方法，也就是说，ChartConfig是一个bean类。在ChartRegistry类里定义一个deregister()方法，它的作用是用Digester读入并解析指定的xml文件，代码如下；还有一个register()方法用来把ChartConfig对象加到registry里。</p>
<p>&nbsp;</p>
<div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<div><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" /><span style="COLOR: #000000">public&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;deregister(URL&nbsp;url)&nbsp;throws&nbsp;IOException,SAXException{<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;InputStream&nbsp;is&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;FileInputStream(url.getFile());<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;Digester&nbsp;digester&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;Digester();<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.push(</span><span style="COLOR: #0000ff">this</span><span style="COLOR: #000000">);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.setValidating(</span><span style="COLOR: #0000ff">false</span><span style="COLOR: #000000">);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.addObjectCreate(</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">charts/chart</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">,&nbsp;ChartConfig.class);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.addSetProperties(</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">charts/chart</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.addBeanPropertySetter(</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">charts/chart/legendVisible</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.addBeanPropertySetter(</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">charts/chart/toolTipsVisible</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.addBeanPropertySetter(</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">charts/chart/title</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.addBeanPropertySetter(</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">charts/chart/type</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.addBeanPropertySetter(</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">charts/chart/labelx</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.addBeanPropertySetter(</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">charts/chart/labely</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.addBeanPropertySetter(</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">charts/chart/width</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.addBeanPropertySetter(</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">charts/chart/height</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.addBeanPropertySetter(</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">charts/chart/hql</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.addBeanPropertySetter(</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">charts/chart/description</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.addSetNext(</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">charts/chart</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">register</span><span style="COLOR: #000000">&quot;</span><span style="COLOR: #000000">);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;digester.parse(is);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />&nbsp;&nbsp;&nbsp;&nbsp;Collections.sort(registry);<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" />}<br /><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top" alt="" /></span></div>
</div>
<p>基本上来说，Digester和SAX解析xml的过程很像，它的原理就是制定一些规则，在遍历每个节点时检查是否有匹配的规则，如果有就执行对应的操作。例如，上面的代码中，&ldquo;digester.addObjectCreate(&quot;charts/chart&quot;, ChartConfig.class);&rdquo;这一句的作用是告诉Digester：如果遇到匹配&ldquo;charts/chart&rdquo;形式的节点，就执行一个&ldquo;对象创建&rdquo;操作，创建什么对象呢，应该创建Class为&ldquo;ChartConfig.class&rdquo;的对象；类似的，addSetProperties()是告诉Digester将指定节点的属性全部映射到对象的属性，在这个例子里指的就是id属性；addBeanPropertySetter()是将子节点转换为对象的属性，这个方法还可以有第二个参数，当对象的属性名和子节点的名字不一样时用来指定对象的属性名；addSetNext()是说在遇到匹配节点后，对当前对象的父对象执行一个方法，参数是当前参数，对这个例子来说就是执行ChartConfig.register(ChartConfig)方法。因此这样构造得到的Digester会把charts.xml里的每个<chart></chart>元素转换为一个ChartConfig对象，并register到ChartRegistry里。</p>
<p>顺利得到了ChartRegister对象，我就可以在程序里根据它的内容构造统计图了（统计图一般使用jfreechart来生成，这里就不赘述了）。与Digester具有类似功能的工具包其实还有不少，例如Caster、Jato等等，我没有实际使用过它们，但因为我对用过的Jakarta其他项目都很满意（例如<a href="http://www.cnblogs.com/bjzhanghao/archive/2004/07/30/28607.html">BeanUtils</a>、HttpClient，品牌效应？），所以一开始就选择了Digester：简单方便。</p>]]>
</description>
<guid isPermaLink="false">http://www.bokee.net/blogmodule/weblogcomment_viewEntry/105345.html</guid>
<subject></subject>
<author>getmc</author>
<category></category>
<pubDate>Fri, 28 Apr 2006 14:57:00 CST </pubDate>
</item>

</channel>
</rss>
