﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-&lt;font face=华文隶书 color=salmon pink size=6&gt;&lt;b&gt;工作小驿&lt;/b&gt;</title><link>http://www.blogjava.net/wjun530/</link><description>&lt;a href="http://www.fodey.com/generators/animated/ninjatext.asp"&gt;&lt;img src="http://r3.fodey.com/14c8ffb1600cc41aaaa8e03a1cf049678.1.gif" border=0 width="437" height="146" alt="Ninja!"&gt;&lt;/a&gt;</description><language>zh-cn</language><lastBuildDate>Wed, 29 Apr 2026 16:47:53 GMT</lastBuildDate><pubDate>Wed, 29 Apr 2026 16:47:53 GMT</pubDate><ttl>60</ttl><item><title> java中相对路径，绝对路径问题总结</title><link>http://www.blogjava.net/wjun530/archive/2007/10/09/151383.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Tue, 09 Oct 2007 06:10:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/10/09/151383.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/151383.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/10/09/151383.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/151383.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/151383.html</trackback:ping><description><![CDATA[1.基本概念的理解<br />
<br />
　　绝对路径：绝对路径就是你的主页上的文件或目录在硬盘上真正的路径，(URL和物理路径)例如：<br />
C:\xyz\test.txt&nbsp;代表了test.txt文件的绝对路径。<a href="http://www.sun.com/index.htm">http://www.sun.com/index.htm</a>也代表了一个<br />
URL绝对路径。<br />
<br />
　　相对路径：相对与某个基准目录的路径。包含Web的相对路径（HTML中的相对目录），例如：在<br />
Servlet中，"/"代表Web应用的跟目录。和物理路径的相对表示。例如："./"&nbsp;代表当前目录,<br />
"../"代表上级目录。这种类似的表示，也是属于相对路径。<br />
<br />
另外关于URI，URL,URN等内容，请参考RFC相关文档标准。<br />
<br />
RFC&nbsp;2396:&nbsp;Uniform&nbsp;Resource&nbsp;Identifiers&nbsp;(URI):&nbsp;Generic&nbsp;Syntax,&nbsp;<br />
(<a href="http://www.ietf.org/rfc/rfc2396.txt">http://www.ietf.org/rfc/rfc2396.txt</a>)<br />
<br />
<br />
2.关于JSP/Servlet中的相对路径和绝对路径。<br />
<br />
2.1服务器端的地址&nbsp;<br />
<br />
　　&nbsp;服务器端的相对地址指的是相对于你的web应用的地址，这个地址是在服务器端解析的<br />
（不同于html和javascript中的相对地址，他们是由客户端浏览器解析的）也就是说这时候<br />
在jsp和servlet中的相对地址应该是相对于你的web应用，即相对于<a href="http://192.168.0.1/webapp/">http://192.168.0.1/webapp/</a>的。&nbsp;<br />
<br />
　　其用到的地方有：&nbsp;<br />
&nbsp;forward：servlet中的request.getRequestDispatcher(address);这个address是<br />
在服务器端解析的，所以，你要forward到a.jsp应该这么写：<br />
request.getRequestDispatcher(&#8220;/user/a.jsp&#8221;)这个/相对于当前的web应用webapp，<br />
其绝对地址就是：<a href="http://192.168.0.1/webapp/user/a.jsp">http://192.168.0.1/webapp/user/a.jsp</a>。&nbsp;<br />
sendRedirect：在jsp中&lt;%response.sendRedirect("/rtccp/user/a.jsp");%&gt;&nbsp;<br />
<br />
2.22、客户端的地址&nbsp;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;所有的html页面中的相对地址都是相对于服务器根目录(<a href="http://192.168.0.1/">http://192.168.0.1/</a>)的，<br />
而不是(跟目录下的该Web应用的目录)<a href="http://192.168.0.1/webapp/">http://192.168.0.1/webapp/</a>的。&nbsp;<br />
&nbsp;Html中的form表单的action属性的地址应该是相对于服务器根目录(<a href="http://192.168.0.1/">http://192.168.0.1/</a>)的，<br />
所以，如果提交到a.jsp为：action＝"/webapp/user/a.jsp"或action="&lt;%=request.getContextPath()%&gt;"/user/a.jsp；<br />
　　提交到servlet为actiom＝"/webapp/handleservlet"&nbsp;&nbsp;<br />
&nbsp;　　Javascript也是在客户端解析的，所以其相对路径和form表单一样。&nbsp;<br />
&nbsp;<br />
<br />
　　因此，一般情况下，在JSP/HTML页面等引用的CSS,Javascript.Action等属性前面最好都加上<br />
&lt;%=request.getContextPath()%&gt;,以确保所引用的文件都属于Web应用中的目录。<br />
另外，应该尽量避免使用类似".","./","../../"等类似的相对该文件位置的相对路径，这样<br />
当文件移动时，很容易出问题。<br />
<br />
<br />
3.&nbsp;JSP/Servlet中获得当前应用的相对路径和绝对路径<br />
3.1&nbsp;JSP中获得当前应用的相对路径和绝对路径<br />
&nbsp;根目录所对应的绝对路径:request.getRequestURI()<br />
&nbsp;文件的绝对路径&nbsp;&nbsp;&nbsp;&nbsp;　:application.getRealPath(request.getRequestURI());<br />
&nbsp;当前web应用的绝对路径&nbsp;:application.getRealPath("/");<br />
&nbsp;取得请求文件的上层目录:new&nbsp;File(application.getRealPath(request.getRequestURI())).getParent()<br />
<br />
3.2&nbsp;Servlet中获得当前应用的相对路径和绝对路径<br />
&nbsp;根目录所对应的绝对路径:request.getServletPath();<br />
&nbsp;文件的绝对路径&nbsp;&nbsp;&nbsp;&nbsp;:request.getSession().getServletContext().getRealPath<br />
(request.getRequestURI())&nbsp;&nbsp;&nbsp;<br />
&nbsp;当前web应用的绝对路径&nbsp;:servletConfig.getServletContext().getRealPath("/");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(ServletContext对象获得几种方式：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;javax.servlet.http.HttpSession.getServletContext()&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;javax.servlet.jsp.PageContext.getServletContext()&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;javax.servlet.ServletConfig.getServletContext()&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)<br />
<br />
4.java&nbsp;的Class中获得相对路径，绝对路径的方法<br />
4.1单独的Java类中获得绝对路径<br />
　　根据java.io.File的Doc文挡，可知:<br />
&nbsp;默认情况下new&nbsp;File("/")代表的目录为：System.getProperty("user.dir")。<br />
&nbsp;一下程序获得执行类的当前路径<br />
package&nbsp;org.cheng.file;<br />
import&nbsp;java.io.File;<br />
<br />
public&nbsp;class&nbsp;FileTest&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;throws&nbsp;Exception&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<br />
&nbsp;&nbsp;System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<br />
&nbsp;&nbsp;System.out.println(FileTest.class.getClassLoader().getResource(""));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<br />
　　System.out.println(ClassLoader.getSystemResource(""));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;System.out.println(FileTest.class.getResource(""));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;System.out.println(FileTest.class.getResource("/"));&nbsp;//Class文件所在路径&nbsp;&nbsp;<br />
&nbsp;&nbsp;System.out.println(new&nbsp;File("/").getAbsolutePath());&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;System.out.println(System.getProperty("user.dir"));&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;}<br />
}<br />
<br />
4.2服务器中的Java类获得当前路径（来自网络）<br />
(1).Weblogic<br />
<br />
WebApplication的系统文件根目录是你的weblogic安装所在根目录。<br />
例如：如果你的weblogic安装在c:\bea\weblogic700.....<br />
那么，你的文件根路径就是c:\.<br />
所以，有两种方式能够让你访问你的服务器端的文件：<br />
a.使用绝对路径：<br />
比如将你的参数文件放在c:\yourconfig\yourconf.properties，<br />
直接使用&nbsp;new&nbsp;FileInputStream("yourconfig/yourconf.properties");<br />
b.使用相对路径：<br />
相对路径的根目录就是你的webapplication的根路径，即WEB-INF的上一级目录，将你的参数文件放<br />
<br />
在yourwebapp\yourconfig\yourconf.properties，<br />
这样使用：<br />
new&nbsp;FileInputStream("./yourconfig/yourconf.properties");<br />
这两种方式均可，自己选择。<br />
<br />
(2).Tomcat<br />
<br />
在类中输出System.getProperty("user.dir");显示的是%Tomcat_Home%/bin<br />
<br />
(3).Resin<br />
<br />
不是你的JSP放的相对路径,是JSP引擎执行这个JSP编译成SERVLET<br />
的路径为根.比如用新建文件法测试File&nbsp;f&nbsp;=&nbsp;new&nbsp;File("a.htm");<br />
这个a.htm在resin的安装目录下&nbsp;<br />
<br />
(4).如何读相对路径哪？<br />
<br />
在Java文件中getResource或getResourceAsStream均可<br />
<br />
例：getClass().getResourceAsStream(filePath);//filePath可以是"/filename",这里的/代表web<br />
<br />
发布根路径下WEB-INF/classes<br />
<br />
默认使用该方法的路径是：WEB-INF/classes。已经在Tomcat中测试。<br />
<br />
5.读取文件时的相对路径，避免硬编码和绝对路径的使用。（来自网络）<br />
5.1&nbsp;采用Spring的DI机制获得文件，避免硬编码。<br />
&nbsp;&nbsp;&nbsp;参考下面的连接内容：<br />
&nbsp;&nbsp;&nbsp;<a href="http://www.javajia.net/viewtopic.php?p=90213&amp;">http://www.javajia.net/viewtopic.php?p=90213&amp;</a><br />
5.2&nbsp;配置文件的读取<br />
&nbsp;参考下面的连接内容：<br />
&nbsp;<a href="http://dev.csdn.net/develop/article/39/39681.shtm">http://dev.csdn.net/develop/article/39/39681.shtm</a><br />
5.3&nbsp;通过虚拟路径或相对路径读取一个xml文件，避免硬编码<br />
&nbsp;参考下面的连接内容：<br />
&nbsp;<a href="http://club.gamvan.com/club/clubPage.jsp?iPage=1&amp;tID=10708&amp;ccID=8">http://club.gamvan.com/club/clubPage.jsp?iPage=1&amp;tID=10708&amp;ccID=8</a><br />
&nbsp;<br />
6.Java中文件的常用操作（复制，移动，删除，创建等）（来自网络）<br />
&nbsp;常用&nbsp;java&nbsp;File&nbsp;操作类&nbsp;<br />
&nbsp;<a href="http://www.easydone.cn/014/200604022353065155.htm">http://www.easydone.cn/014/200604022353065155.htm</a><br />
&nbsp;<br />
&nbsp;Java文件操作大全（JSP中）<br />
&nbsp;<a href="http://www.pconline.com.cn/pcedu/empolder/gj/java/0502/559401.html">http://www.pconline.com.cn/pcedu/empolder/gj/java/0502/559401.html</a><br />
<br />
&nbsp;java文件操作详解（Java中文网）<br />
&nbsp;<a href="http://www.51cto.com/html/2005/1108/10947.htm">http://www.51cto.com/html/2005/1108/10947.htm</a><br />
<br />
&nbsp;JAVA&nbsp;如何创建\删除\修改\复制目录及文件<br />
&nbsp;<a href="http://www.gamvan.com/developer/java/2005/2/264.html">http://www.gamvan.com/developer/java/2005/2/264.html</a><br />
<br />
总结：<br />
&nbsp;通过上面内容的使用，可以解决在Web应用服务器端，移动文件，查找文件，复制<br />
&nbsp;删除文件等操作，同时对服务器的相对地址，绝对地址概念更加清晰。<br />
建议参考URI,的RFC标准文挡。同时对Java.io.File.&nbsp;Java.net.URI.等内容了解透彻<br />
对其他方面的理解可以更加深入和透彻。
<img src ="http://www.blogjava.net/wjun530/aggbug/151383.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-10-09 14:10 <a href="http://www.blogjava.net/wjun530/archive/2007/10/09/151383.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java对象持久化技术Hibernate 二</title><link>http://www.blogjava.net/wjun530/archive/2007/10/09/151379.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Tue, 09 Oct 2007 05:59:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/10/09/151379.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/151379.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/10/09/151379.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/151379.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/151379.html</trackback:ping><description><![CDATA[创建数据库Schema 在本例中，与Customer类对应的数据库表名为CUSTOMERS，它在MySQL数据库中的DDL定义如下： <br />
<br />
<br />
create table CUSTOMERS (<br />
　　ID bigint not null primary key,<br />
　　NAME varchar(15) not null,<br />
　　EMAIL varchar(128) not null,<br />
　　PASSWORD varchar(8) not null,<br />
　　PHONE int ,<br />
　　ADDRESS varchar(255),<br />
　　SEX char(1) ,<br />
　　IS_MARRIED bit,<br />
　　DESCRIPTION text,<br />
　　IMAGE blob,<br />
　　BIRTHDAY date,<br />
　　REGISTERED_TIME timestamp<br />
　　);<br />
<br />
<br />
　　 <br />
CUSTOMERS表有一个ID字段，它是表的主键，它和Customer类的id属性对应。CUSTOMERS表中的字段使用了各种各样的SQL类型<br />
<br />
<br />
<br />
2.4 创建对象-关系映射文件 <br />
<br />
Hibernate采用XML格式的文件来指定对象和关系数据之间的映射。在运行时，Hibernate将根据这个映射文件来生成各种SQL语句。在本例中，将创建一个名为Customer.hbm.xml的文件，它用于把Customer类映射到CUSTOMERS表，这个文件应该和Customer.class文件存放在同一个目录下。例程2-3为Customer.hbm.xml文件的代码。 <br />
<br />
例程2-3 Customer.hbm.xml <br />
<br />
<br />
&lt;?xml version="1.0"?&gt;<br />
　　&lt;!DOCTYPE hibernate-mapping PUBLIC "-<br />
　　//Hibernate/Hibernate Mapping DTD 2.0<br />
　　//EN"<br />
　　"http://hibernate.sourceforge.net<br />
　　/hibernate-mapping-2.0.dtd"&gt;<br />
　　<br />
　　&lt;hibernate-mapping&gt;<br />
　　&lt;class name="mypack.Customer"<br />
　　table="CUSTOMERS"&gt;<br />
　　<br />
　　&lt;id name="id" column="ID" type="long"&gt;<br />
　　&lt;generator class="increment"/&gt;<br />
　　&lt;/id&gt;<br />
　　<br />
　　&lt;property name="name"<br />
　　column="NAME"　type="string"<br />
　　not-null="true" /&gt;<br />
　　&lt;property name="email"<br />
　　column="EMAIL"　　 type="string"<br />
　　not-null="true" /&gt;<br />
　　&lt;property name="password"<br />
　　column="PASSWORD"　type="string"<br />
　　not-null="true"/&gt;<br />
　　&lt;property name="phone"<br />
　　column="PHONE"　　 type="int" /&gt;<br />
　　&lt;property name="address"<br />
　　column="ADDRESS"　 type="string" /&gt;<br />
　　&lt;property name="sex"<br />
　　column="SEX"　　　 type="character"/&gt;<br />
　　&lt;property name="married"<br />
　　column="IS_MARRIED"　type="boolean"/&gt;<br />
　　&lt;property name="description"<br />
　　column="DESCRIPTION"　type="text"/&gt;<br />
　　&lt;property name="image"<br />
　　column="IMAGE"　　　　type="binary"/&gt;<br />
　　&lt;property name="birthday"<br />
　　column="BIRTHDAY"　　 type="date"/&gt;<br />
　　&lt;property name="registeredTime"<br />
　　column="REGISTERED_TIME"<br />
　　type="timestamp"/&gt;<br />
　　&lt;/class&gt;<br />
　　&lt;/hibernate-mapping&gt;<br />
<br />
<br />
　　 <br />
2.4.1 映射文件的文档类型定义（DTD） <br />
<br />
在例程2-3的Customer.hbm.xml文件的开头声明了DTD（Document Type Definition，文档类型定义），它对XML文件的语法和格式做了定义。Hibernate的XML解析器将根据DTD来核对XML文件的语法。 <br />
<br />
每一种XML文件都有独自的DTD文件。Hibernate的对象-关系映射文件使用的DTD文件的下载网址为：http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd。此外，在Hibernate软件包的src<br />
etsfhibernate目录下也提供了hibernate-mapping-2.0.dtd文件。在这个文件中，描述顶层元素的代码如下： <br />
<br />
<br />
&lt;!ELEMENT hibernate-mapping (meta*,<br />
　　import*, (class|subclass|joined-subclass)*,<br />
　　query*,sql-query*)&gt;<br />
<br />
<br />
　　 <br />
描述顶层元素的子元素的代码如下： <br />
<br />
<br />
&lt;!ELEMENT class (<br />
　　meta*,<br />
　　(cache|jcs-cache)?,<br />
　　(id|composite-id),<br />
　　discriminator?,<br />
　　(version|timestamp)?,<br />
　　(property|many-to-one|one-to-one<br />
　　|component|dynamic-component|any<br />
　　|map|set|list|bag|idbag|array<br />
　　|primitive-array)*,<br />
　　((subclass*)|(joined-subclass*))<br />
　　)&gt;<br />
<br />
<br />
　　 <br />
元素是对象-关系映射文件的根元素，其他元素（即以上DTD代码中括号以内的元素，如子元素）必须嵌入在元素以内。在元素中又嵌套了好多子元素。 <br />
<br />
在以上DTD代码中，还使用了一系列的特殊符号来修饰元素，表2-3描述了这些符号的作用。在创建自己的对象-关系映射文件时，如果不熟悉某种元素的语法，可以参考DTD文件。 <br />
<br />
<br />
<br />
此外，在映射文件中，父元素中的各种子元素的定义必须符合特定的顺序。例如，根据元素的DTD可以看出，必须先定义子元素，再定义子元素，以下映射代码颠倒了和子元素的位置： <br />
<br />
<br />
&lt;class name="mypack.Customer" <br />
table="CUSTOMERS"&gt;<br />
&lt;property name="name" <br />
column="NAME" type="string"<br />
not-null="true" /&gt;<br />
&lt;property name="email" <br />
column="EMAIL" <br />
type="string" not-null="true" /&gt; <br />
<br />
&lt;id name="id" column="ID" type="long"&gt;<br />
&lt;generator class="increment"/&gt;<br />
&lt;/id&gt;<br />
&#8230;&#8230;<br />
&lt;/class&gt;<br />
<br />
<br />
Hibernate的XML解析器在运行时会抛出MappingException： <br />
<br />
<br />
<br />
[java] 21:27:51,610 ERROR XMLHelper:<br />
48 - Error parsing XML: <br />
XML InputStream (24)<br />
The content of element type "class"<br />
must match "(meta*,(cache|jcs-cache)?,<br />
(<br />
id|composite-id),<br />
discriminator?,(version|timestamp)?,<br />
(property|many-to-one|one-to-one|component|<br />
dynamic-component|any|map|set<br />
|list|bag|idbag|array|primitive-array)*,<br />
(subclass*|joined-subclass*))".<br />
<br />
[java] net.sf.hibernate.MappingException:<br />
Error reading resource: <br />
mypack/Customer.hbm.xml<br />
at net.sf.hibernate.cfg.Configuration.addClass<br />
(Configuration.java:357)<br />
<br />
<br />
<br />
2.4.2 把Customer持久化类映射到CUSTOMERS表 <br />
<br />
<br />
例程2-3的Customer.hbm.xml文件用于映射Customer类。如果需要映射多个持久化类，那么既可以在同一个映射文件中映射所有类，也可以为每个类创建单独的映射文件，映射文件和类同名，扩展名为"hbm.xml"。后一种做法更值得推荐，因为在团队开发中，这有利于管理和维护映射文件。 <br />
<br />
<br />
元素指定类和表的映射，它的name属性设定类名，table属性设定表名。以下代码表明和Customer类对应的表为CUSTOMERS表： <br />
<br />
<br />
&lt;class name="mypack.Customer" table="CUSTOMERS"&gt;<br />
<br />
<br />
如果没有设置元素的table属性，Hibernate将直接以类名作为表名，也就是说，在默认情况下，与mypack.Customer类对应的表为Customer表。 <br />
<br />
元素包含一个子元素及多个子元素。子元素设定持久化类的OID和表的主键的映射。以下代码表明Customer类的id属性和CUSTOMERS表中的ID字段对应。 <br />
<br />
<br />
&lt;id name="id" column="ID" type="long"&gt;<br />
&lt;generator class="increment"/&gt;<br />
&lt;/id&gt;<br />
<br />
<br />
<br />
元素的子元素指定对象标识符生成器，它负责为OID生成惟一标识符。本书第5章（映射对象标识符）详细介绍了Hibernate提供的各种对象标识符生成器的用法。 <br />
<br />
<br />
子元素设定类的属性和表的字段的映射。子元素主要包括name、type、column和not-null属性。 <br />
<br />
<br />
1．元素的name属性 <br />
<br />
<br />
元素的name属性指定持久化类的属性的名字。 <br />
<br />
<br />
2．元素的type属性 <br />
<br />
<br />
元素的type属性指定Hibernate映射类型。Hibernate映射类型是Java类型与SQL类型的桥梁。表2-4列出了Customer类的属性的Java类型、Hibernate映射类型，以及CUSTOMERS表的字段的SQL类型这三者之间的对应关系。 <br />
<br />
<br />
表2-4 Java类型、Hibernate映射类型以及SQL类型之间的对应关系 <br />
<br />
Customer类的属性 Java类型 Hibernate映射类型 CUSTOMERS表的字段 SQL类型 <br />
name java.lang.String string NAME VARCHAR(15) <br />
email java.lang.String string EMAIL VARCHAR(128) <br />
password java.lang.String string PASSWORD VARCHAR(8) <br />
phone int int PHONE INT <br />
address java.lang.String string ADDRESS VARCHAR(255) <br />
sex char character SEX CHAR(1) <br />
married boolean boolean IS_MARRIED BIT <br />
description java.lang.String text DESCRIPTION TEXT <br />
image byte[] binary IMAGE BLOB <br />
birthday java.sql.Date date BIRTHDAY DATE <br />
registeredTime java.sql.Timestamp timestamp REGISTERED_TIME TIMESTAMP <br />
<br />
<br />
<br />
从表2-4看出，如果Customer类的属性为java.lang.String类型，并且与此对应的CUSTOMERS表的字段为VARCHAR类型，那么应该把Hibernate映射类型设为string，例如： <br />
<br />
<br />
&lt;property name="name" <br />
column="NAME" type="string" <br />
not-null="true" /&gt;<br />
<br />
<br />
<br />
如果Customer类的属性为java.lang.String类型，并且与此对应的CUSTOMERS表的字段为TEXT类型，那么应该把Hibernate映射类型设为text，例如： <br />
<br />
<br />
&lt;property name="description" column="DESCRIPTION" type="text"/&gt;<br />
<br />
<br />
如果Customer类的属性为byte[]类型，并且与此对应的CUSTOMERS表的字段为BLOB类型，那么应该把Hibernate映射类型设为binary，例如： <br />
<br />
<br />
&lt;property name="image" column="IMAGE" type="binary"/&gt;<br />
<br />
<br />
如果没有显式设定映射类型，Hibernate会运用Java反射机制先识别出持久化类的属性的Java类型，然后自动使用与之对应的默认的Hibernate映射类型。例如，Customer类的address属性为java.lang.String类型，与java.lang.String对应的默认的映射类型为string，因此以下两种设置方式是等价的： <br />
<br />
<br />
&lt;property name="address" column="ADDRESS"/&gt;<br />
<br />
<br />
或者： <br />
<br />
<br />
&lt;property name="address" type="string" /&gt;<br />
<br />
<br />
对于Customer类的description属性，尽管它是java.lang.String类型，由于CUSTOMERS表的DESCRIPTION字段为text类型，因此必须显式地把映射类型设为text。 <br />
<br />
3．元素的not-null属性 <br />
<br />
如果元素的not-null属性为true，表明不允许为null，默认为false。例如以下代码表明不允许Customer类的name属性为null： <br />
<br />
<br />
&lt;property name="name" column="NAME" type="string" not-null="true" /&gt;<br />
<br />
<br />
Hibernate在持久化一个Customer对象时，会先检查它的name属性是否为null，如果为null，就会抛出以下异常： <br />
<br />
<br />
net.sf.hibernate.PropertyValueException: <br />
not-null property references <br />
a null or transient value:<br />
mypack.Customer.name<br />
<br />
<br />
如果数据库中CUSTOMERS表的NAME字段不允许为null，但在映射文件中没有设置not-null属性： <br />
<br />
<br />
&lt;property name="name" column="NAME" type="string" /&gt;<br />
<br />
<br />
那么Hibernate在持久化一个Customer对象时，不会先检查它的name属性是否为null，而是直接通过JDBC API向CUSTOMERS表插入相应的数据，由于CUSTOMERS表的NAME字段设置了not null约束，因此数据库会抛出错误： <br />
<br />
<br />
708 ERROR JDBCExceptionReporter:<br />
58 - General error, message from server: <br />
"Column NAME cannot be null"<br />
<br />
<br />
<br />
值得注意的是，对于实际Java应用，当持久化一个Java对象时，不应该依赖Hibernate或数据库来负责数据验证。在四层应用结构中，应该由表述层或者业务逻辑层负责数据验证。例如对于Customer对象的name属性，事实上在表述层就能检查name属性是否为null，假如表述层、业务逻辑层和Hibernate持久化层都没有检查name属性是否为null，那么数据库层会监测到NAME字段违反了数据完整性约束，从而抛出异常，如图2-2所示，包含非法数据的Customer对象从表述层依次传到数据库层，随后从数据库层抛出的错误信息又依次传到表述层，这种做法显然会降低数据验证的效率。 <br />
<br />
<br />
<br />
<br />
既然如此，把元素的not-null属性设为true，有何意义呢？这主要是便于在软件开发和测试阶段能捕获表述层或者业务逻辑层应该处理而未处理的异常，提醒开发人员在表述层或者业务逻辑层中加入必要的数据验证逻辑。 <br />
<br />
<br />
4．元素的column属性 <br />
<br />
<br />
元素的column属性指定与类的属性映射的表的字段名。以下代码表明和address属性对应的字段为ADDRESS字段： <br />
<br />
<br />
&lt;property name="address" column= "ADDRESS" type="string"/&gt;<br />
<br />
<br />
<br />
如果没有设置&lt; property &gt;元素的column属性，Hibernate将直接以类的属性名作为字段名，也就是说，在默认情况下，与Customer类的address属性对应的字段为address字段。 <br />
<br />
<br />
元素还可以包括子元素，它和元素的column属性一样，都可以设定与类的属性映射的表的字段名。以下两种设置方式是等价的： <br />
<br />
<br />
&lt;property name="address" column= "ADDRESS" type="string"/&gt;<br />
<br />
<br />
或者： <br />
<br />
<br />
&lt;property name="address" type="string"&gt;<br />
&lt;column name="ADDRESS" /&gt;<br />
&lt;/property&gt;<br />
<br />
<br />
<br />
元素的子元素比column属性提供更多的功能，它可以更加详细地描述表的字段。例如，以下子元素指定CUSTOMERS表中的NAME字段的SQL类型为varchar(15)，不允许为null，并且为这个字段建立了索引： <br />
<br />
<br />
<br />
&lt;property name="name" type="string"&gt;<br />
&lt;column name="NAME" sql-type="varchar(15)"<br />
not-null="true" index="idx_name" /&gt;<br />
&lt;/property&gt;<br />
<br />
<br />
<br />
子元素主要和hbm2ddl工具联合使用。当使用hbm2ddl工具来自动生成数据库Schema时，hbm2ddl工具将依据子元素提供的信息来定义表的字段。关于hbm2ddl工具的用法参见本书第3章（hbm2java和hbm2ddl工具）。如果数据库Schema是通过手工方式创建的，就不必通过子元素设定字段的详细信息，通常只需设定它的name属性和not-null属性就可以了，例如： <br />
<br />
<br />
<br />
&lt;property name="name" type="string"&gt;<br />
&lt;column name="NAME" not-null="true" /&gt;<br />
&lt;/property&gt;<br />
<br />
<br />
<br />
或者： <br />
<br />
<br />
<br />
&lt;property name="name" column="NAME" <br />
type="string" not-null="true" /&gt;<br />
<br />
<br />
<br />
除了not-null属性以外，子元素的多数属性（如sql-type或index属性）都不会影响Hibernate的运行时行为。 <br />
<br />
<br />
<br />
Hibernate采用XML文件来配置对象-关系映射，有以下优点： <br />
<br />
<br />
1、Hibernate既不会渗透到上层域模型中，也不会渗透到下层数据模型中。 <br />
<br />
<br />
2、软件开发人员可以独立设计域模型，不必强迫遵守任何规范。 <br />
<br />
<br />
3、数据库设计人员可以独立设计数据模型，不必强迫遵守任何规范。 <br />
<br />
<br />
4、对象-关系映射不依赖于任何程序代码，如果需要修改对象-关系映射，只需修改XML文件，不需要修改任何程序，提高了软件的灵活性，并且使维护更加方便
<img src ="http://www.blogjava.net/wjun530/aggbug/151379.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-10-09 13:59 <a href="http://www.blogjava.net/wjun530/archive/2007/10/09/151379.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java对象持久化技术Hibernate 一</title><link>http://www.blogjava.net/wjun530/archive/2007/10/09/151376.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Tue, 09 Oct 2007 05:58:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/10/09/151376.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/151376.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/10/09/151376.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/151376.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/151376.html</trackback:ping><description><![CDATA[Hibernate是Java应用和关系数据库之间的桥梁，它负责Java对象和关系数据之间的映射。Hibernate内部封装了通过JDBC访问数据库的操作，向上层应用提供了面向对象的数据访问API。在Java应用中使用Hibernate包含以下步骤。<br />
　　<br />
　　（1）创建Hibernate的配置文件。<br />
　　<br />
　　（2）创建持久化类。<br />
　　<br />
　　（3）创建对象-关系映射文件。<br />
　　<br />
　　（4）通过Hibernate API编写访问数据库的代码。<br />
　　<br />
　　本文通过一个简单的例子helloapp应用，演示如何运用Hibernate来访问关系数据库。helloapp应用的功能非常简单：通过Hibernate保存、更新、删除、加载及查询Customer对象。图1显示了Hibernate在helloapp应用中所处的位置。<br />
　　　　　　　<br />
　　helloapp应用既能作为独立的Java程序运行，还能作为Java Web应用运行，该应用的源代码位于配套光盘的sourcecode/chapter2/helloapp目录下。<br />
　　<br />
　　2.1 创建Hibernate的配置文件<br />
　　<br />
　　Hibernate从其配置文件中读取和数据库连接有关的信息，这个配置文件应该位于应用的classpath中。Hibernate的配置文件有两种形式：一种是XML格式的文件；还有一种是Java属性文件，采用"健=值"的形式。<br />
　　<br />
　　下面介绍如何以Java属性文件的格式来创建Hibernate的配置文件。这种配置文件的默认文件名为hibernate.properties，例程2-1为示范代码。<br />
　　<br />
　　例程2-1 hibernate.properties<br />
　　<br />
　　hibernate.dialect=<br />
　　net.sf.hibernate.dialect.mysqlDialect<br />
　　hibernate.connection.driver_class=<br />
　　com.mysql.jdbc.Driver<br />
　　hibernate.connection.url=jdbc:mysql:<br />
　　//localhost:3306/SAMPLEDB<br />
　　hibernate.connection.username=root<br />
　　hibernate.connection.passWord=1234<br />
　　hibernate.show_sql=true<br />
　　<br />
　　以上hibernate.properties文件包含了一系列属性及其属性值，Hibernate将根据这些属性来连接数据库，本例为连接MySQL数据库的配置代码。表2-1对以上hibernate.properties文件中的所有属性做了描述 
<img src ="http://www.blogjava.net/wjun530/aggbug/151376.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-10-09 13:58 <a href="http://www.blogjava.net/wjun530/archive/2007/10/09/151376.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>垃圾清理势在必行——Java垃圾收集算法</title><link>http://www.blogjava.net/wjun530/archive/2007/10/06/150637.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Sat, 06 Oct 2007 00:18:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/10/06/150637.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/150637.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/10/06/150637.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/150637.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/150637.html</trackback:ping><description><![CDATA[1.垃圾收集算法的核心思想 <br />
<br />
Java语言建立了垃圾收集机制，用以跟踪正在使用的对象和发现并回收不再使用(引用)的对象。该机制可以有效防范动态内存分配中可能发生的两个危险：因内存垃圾过多而引发的内存耗尽，以及不恰当的内存释放所造成的内存非法引用。 <br />
<br />
垃圾收集算法的核心思想是：对虚拟机可用内存空间，即堆空间中的对象进行识别，如果对象正在被引用，那么称其为存活对象，反之，如果对象不再被引用，则为垃圾对象，可以回收其占据的空间，用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节直接影响着系统性能，因此需要开发人员做比较深入的了解。 <br />
<br />
2.触发主GC(Garbage Collector)的条件 <br />
<br />
JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它对系统影响很明显。总的来说,有两个条件会触发主GC: <br />
<br />
①当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。 <br />
<br />
②Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报&#8220;out of memory&#8221;的错误,Java应用将停止。 <br />
<br />
由于是否进行主GC由JVM根据系统环境决定,而系统环境在不断的变化当中,所以主GC的运行具有不确定性,无法预计它何时必然出现,但可以确定的是对一个长期运行的应用来说,其主GC是反复进行的。 <br />
<br />
3.减少GC开销的措施 <br />
<br />
根据上述GC的机制,程序的运行会直接影响系统环境的变化,从而影响GC的触发。若不针对GC的特点进行设计和编码,就会出现内存驻留等一系列负面影响。为了避免这些影响,基本的原则就是尽可能地减少垃圾和减少GC过程中的开销。具体措施包括以下几个方面: <br />
<br />
(1)不要显式调用System.gc() <br />
<br />
此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。 <br />
<br />
(2)尽量减少临时对象的使用 <br />
<br />
临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。 <br />
<br />
(3)对象不用时最好显式置为Null <br />
<br />
一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。 <br />
<br />
(4)尽量使用StringBuffer,而不用String来累加字符串(详见blog另一篇文章JAVA中String与StringBuffer) <br />
<br />
由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如Str5=Str1+Str2+Str3+Str4,这条语句执行过程中会产生多个垃圾对象,因为对次作&#8220;+&#8221;操作时都必须创建新的String对象,但这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因StringBuffer是可变长的,它在原有基础上进行扩增,不会产生中间对象。 <br />
<br />
(5)能用基本类型如Int,Long,就不用Integer,Long对象 <br />
<br />
基本类型变量占用的内存资源比相应对象占用的少得多,如果没有必要,最好使用基本变量。 <br />
<br />
(6)尽量少用静态对象变量 <br />
<br />
静态变量属于全局变量,不会被GC回收,它们会一直占用内存。 <br />
<br />
(7)分散对象创建或删除的时间 <br />
<br />
集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片,从而增加主GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC的机会。 <br />
<br />
4.gc与finalize方法 <br />
<br />
⑴gc方法请求垃圾回收 <br />
<br />
使用System.gc()可以不管JVM使用的是哪一种垃圾回收的算法，都可以请求Java的垃圾回收。需要注意的是，调用System.gc()也仅仅是一个请求。JVM接受这个消息后，并不是立即做垃圾回收，而只是对几个垃圾回收算法做了加权，使垃圾回收操作容易发生，或提早发生，或回收较多而已。 <br />
<br />
⑵finalize方法透视垃圾收集器的运行 <br />
<br />
在JVM垃圾收集器收集一个对象之前 ，一般要求程序调用适当的方法释放资源，但在没有明确释放资源的情况下，Java提供了缺省机制来终止化该对象释放资源，这个方法就是finalize()。它的原型为： <br />
<br />
protected void finalize() throws Throwable <br />
<br />
在finalize()方法返回之后，对象消失，垃圾收集开始执行。原型中的throws Throwable表示它可以抛出任何类型的异常。 <br />
<br />
因此，当对象即将被销毁时，有时需要做一些善后工作。可以把这些操作写在finalize()方法里。 <br />
<br />
以下是引用片段： <br />
<br />
<br />
protected void finalize() <br />
　　{ <br />
　　// finalization code here <br />
　　}<br />
<br />
<br />
⑶代码示例 <br />
<br />
以下是引用片段： <br />
<br />
<br />
class Garbage <br />
　　{ <br />
　　int index; <br />
　　static int count; <br />
　　Garbage() <br />
　　{ <br />
　　count++; <br />
　　System.out.println("object "+count+" construct"); <br />
　　setID(count); <br />
　　} <br />
　　void setID(int id) <br />
　　{ <br />
　　index=id; <br />
　　} <br />
　　protected void finalize() //重写finalize方法 <br />
　　{ <br />
　　System.out.println("object "+index+" is reclaimed"); <br />
　　} <br />
　　public static void main(String[] args) <br />
　　{ <br />
　　new Garbage(); <br />
　　new Garbage(); <br />
　　new Garbage(); <br />
　　new Garbage(); <br />
　　System.gc(); //请求运行垃圾收集器 <br />
　　} <br />
　　}<br />
<br />
<br />
5.Java 内存泄漏 <br />
<br />
由于采用了垃圾回收机制，任何不可达对象(对象不再被引用)都可以由垃圾收集线程回收。因此通常说的Java 内存泄漏其实是指无意识的、非故意的对象引用，或者无意识的对象保持。无意识的对象引用是指代码的开发人员本来已经对对象使用完毕，却因为编码的错误而意外地保存了对该对象的引用(这个引用的存在并不是编码人员的主观意愿)，从而使得该对象一直无法被垃圾回收器回收掉，这种本来以为可以释放掉的却最终未能被释放的空间可以认为是被&#8220;泄漏了&#8221;。
<img src ="http://www.blogjava.net/wjun530/aggbug/150637.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-10-06 08:18 <a href="http://www.blogjava.net/wjun530/archive/2007/10/06/150637.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>输入输出类</title><link>http://www.blogjava.net/wjun530/archive/2007/10/06/150636.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Sat, 06 Oct 2007 00:17:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/10/06/150636.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/150636.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/10/06/150636.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/150636.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/150636.html</trackback:ping><description><![CDATA[import java.io.*;<br />
<br />
public class KeyboardInput{<br />
<br />
private final BufferedReader in=new BufferedReader(new InputStreamReader(System.in));<br />
public final synchronized int readInteger(){<br />
String input="";<br />
int value=0;<br />
try{<br />
input=in.readLine();<br />
}<br />
catch (IOException e){}<br />
if(input!=null){<br />
try{<br />
value=Integer.parseInt(input);<br />
}<br />
catch(NumberFormatException e){}<br />
}<br />
return value;<br />
}<br />
<br />
public final synchronized long readLong(){<br />
String input="";<br />
long value=0L;<br />
try{<br />
input=in.readLine();<br />
}<br />
catch (IOException e){}<br />
if(input!=null){<br />
try{<br />
value=Long.parseLong(input);<br />
}<br />
catch(NumberFormatException e){}<br />
}<br />
return value;<br />
}<br />
<br />
public final synchronized double readDouble(){<br />
String input="";<br />
double value=0.0D;<br />
try{<br />
input=in.readLine();<br />
}<br />
catch (IOException e){}<br />
if(input!=null){<br />
try{<br />
value=Double.parseDouble(input);<br />
}<br />
catch(NumberFormatException e){}<br />
}<br />
return value;<br />
}<br />
<br />
public final synchronized float readFloat(){<br />
String input="";<br />
float value=0.0F;<br />
try{<br />
input=in.readLine();<br />
}<br />
catch (IOException e){}<br />
if(input!=null){<br />
try{<br />
value=Float.parseFloat(input);<br />
}<br />
catch(NumberFormatException e){}<br />
}<br />
return value;<br />
}<br />
<br />
public final synchronized char readCharacter(){<br />
char c=;<br />
try{<br />
c=(char)in.read();<br />
}<br />
catch (IOException e){}<br />
return c;<br />
}<br />
<br />
public final synchronized String readString(){<br />
String s="";<br />
try{<br />
s=in.readLine();<br />
}<br />
catch (IOException e){}<br />
if(s==null){<br />
s="";<br />
}<br />
return s;<br />
}<br />
}<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/150636.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-10-06 08:17 <a href="http://www.blogjava.net/wjun530/archive/2007/10/06/150636.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA中的反射机制详解</title><link>http://www.blogjava.net/wjun530/archive/2007/10/02/150132.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Mon, 01 Oct 2007 17:50:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/10/02/150132.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/150132.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/10/02/150132.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/150132.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/150132.html</trackback:ping><description><![CDATA[JAVA反射机制<br />
JAVA反射机制是在运行状态中，对于任意一个类，都能够知道这个类的所有属性和方法；对于任意一个对象，都能够调用它的任意一个方法；这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。<br />
Java反射机制主要提供了以下功能： 在运行时判断任意一个对象所属的类；在运行时构造任意一个类的对象；在运行时判断任意一个类所具有的成员变量和方法；在运行时调用任意一个对象的方法；生成动态代理。<br />
1. 得到某个对象的属性<br />
<br />
1 public Object getProperty(Object owner, String fieldName) throws Exception {<br />
2 Class ownerClass = owner.getClass();<br />
3 <br />
4 Field field = ownerClass.getField(fieldName);<br />
5 <br />
6 Object property = field.get(owner);<br />
7 <br />
8 return property;<br />
9 }<br />
Class ownerClass = owner.getClass()：得到该对象的Class。<br />
<br />
Field field = ownerClass.getField(fieldName)：通过Class得到类声明的属性。<br />
<br />
Object property = field.get(owner)：通过对象得到该属性的实例，如果这个属性是非公有的，这里会报IllegalAccessException。<br />
<br />
2. 得到某个类的静态属性<br />
<br />
1 public Object getStaticProperty(String className, String fieldName)<br />
2 throws Exception {<br />
3 Class ownerClass = Class.forName(className);<br />
4 <br />
5 Field field = ownerClass.getField(fieldName);<br />
6 <br />
7 Object property = field.get(ownerClass);<br />
8 <br />
9 return property;<br />
10 }<br />
<br />
Class ownerClass = Class.forName(className) ：首先得到这个类的Class。<br />
<br />
Field field = ownerClass.getField(fieldName)：和上面一样，通过Class得到类声明的属性。<br />
<br />
Object property = field.get(ownerClass) ：这里和上面有些不同，因为该属性是静态的，所以直接从类的Class里取。<br />
<br />
3. 执行某对象的方法<br />
<br />
1 public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception {<br />
2 <br />
3 Class ownerClass = owner.getClass();<br />
4 <br />
5 Class[] argsClass = new Class[args.length];<br />
6 <br />
7 for (int i = 0, j = args.length; i &lt; j; i++) {<br />
8 argsClass[i] = args[i].getClass();<br />
9 }<br />
10 <br />
11 Method method = ownerClass.getMethod(methodName, argsClass);<br />
12 <br />
13 return method.invoke(owner, args);<br />
14 }<br />
Class owner_class = owner.getClass() ：首先还是必须得到这个对象的Class。<br />
<br />
5～9行：配置参数的Class数组，作为寻找Method的条件。<br />
<br />
Method method = ownerClass.getMethod(methodName, argsClass)：通过Method名和参数的Class数组得到要执行的Method。<br />
<br />
method.invoke(owner, args)：执行该Method，invoke方法的参数是执行这个方法的对象，和参数数组。返回值是Object，也既是该方法的返回值。<br />
<br />
4. 执行某个类的静态方法<br />
<br />
1 public Object invokeStaticMethod(String className, String methodName,<br />
2 Object[] args) throws Exception {<br />
3 Class ownerClass = Class.forName(className);<br />
4 <br />
5 Class[] argsClass = new Class[args.length];<br />
6 <br />
7 for (int i = 0, j = args.length; i &lt; j; i++) {<br />
8 argsClass[i] = args[i].getClass();<br />
9 }<br />
10 <br />
11 Method method = ownerClass.getMethod(methodName, argsClass);<br />
12 <br />
13 return method.invoke(null, args);<br />
14 }<br />
<br />
基本的原理和实例3相同，不同点是最后一行，invoke的一个参数是null，因为这是静态方法，不需要借助实例运行。<br />
<br />
5. 新建实例<br />
1 <br />
2 public Object newInstance(String className, Object[] args) throws Exception {<br />
3 Class newoneClass = Class.forName(className);<br />
4 <br />
5 Class[] argsClass = new Class[args.length];<br />
6 <br />
7 for (int i = 0, j = args.length; i &lt; j; i++) {<br />
8 argsClass[i] = args[i].getClass();<br />
9 }<br />
10 <br />
11 Constructor cons = newoneClass.getConstructor(argsClass);<br />
12 <br />
13 return cons.newInstance(args);<br />
14 <br />
15 }<br />
<br />
这里说的方法是执行带参数的构造函数来新建实例的方法。如果不需要参数，可以直接使用newoneClass.newInstance()来实现。<br />
<br />
Class newoneClass = Class.forName(className)：第一步，得到要构造的实例的Class。<br />
<br />
第5～第9行：得到参数的Class数组。<br />
<br />
Constructor cons = newoneClass.getConstructor(argsClass)：得到构造子。<br />
<br />
cons.newInstance(args)：新建实例。<br />
<br />
6. 判断是否为某个类的实例<br />
<br />
1 public boolean isInstance(Object obj, Class cls) {<br />
2 return cls.isInstance(obj);<br />
3 }<br />
<br />
7. 得到数组中的某个元素<br />
1 public Object getByArray(Object array, int index) {<br />
2 return Array.get(array,index);<br />
3 } 
<img src ="http://www.blogjava.net/wjun530/aggbug/150132.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-10-02 01:50 <a href="http://www.blogjava.net/wjun530/archive/2007/10/02/150132.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何掌握JDK1.5枚举类型</title><link>http://www.blogjava.net/wjun530/archive/2007/10/02/150131.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Mon, 01 Oct 2007 17:48:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/10/02/150131.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/150131.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/10/02/150131.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/150131.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/150131.html</trackback:ping><description><![CDATA[Enum作为Sun全新引进的一个关键字，看起来很象是特殊的class, 它也可以有自己的变量，可以定义自己的方法，可以实现一个或者多个接口。 当我们在声明一个enum类型时，我们应该注意到enum类型有如下的一些特征。 <br />
<br />
　　1．它不能有public的构造函数，这样做可以保证客户代码没有办法新建一个enum的实例。 <br />
<br />
　　2．所有枚举值都是public , static , final的。注意这一点只是针对于枚举值，我们可以和在普通类里面定义 变量一样定义其它任何类型的非枚举变量，这些变量可以用任何你想用的修饰符。 <br />
<br />
　　3．Enum默认实现了java.lang.Comparable接口。 <br />
<br />
　　4．Enum覆载了了toString方法，因此我们如果调用Color.Blue.toString()默认返回字符串&#8221;Blue&#8221;. <br />
<br />
　　5．Enum提供了一个valueOf方法，这个方法和toString方法是相对应的。调用valueOf(&#8220;Blue&#8221;)将返回Color.Blue.因此我们在自己重写toString方法的时候就要注意到这一点，一把来说应该相对应地重写valueOf方法。 <br />
<br />
　　6．Enum还提供了values方法，这个方法使你能够方便的遍历所有的枚举值。 <br />
<br />
　　7．Enum还有一个oridinal的方法，这个方法返回枚举值在枚举类种的顺序，这个顺序根据枚举值声明的顺序而定，这里Color.Red.ordinal()返回0。 <br />
<br />
　　了解了这些基本特性，我们来看看如何使用它们。 <br />
<br />
　　1．遍历所有有枚举值. 知道了有values方法，我们可以轻车熟路地用ForEach循环来遍历了枚举值了。 <br />
<br />
　　for (Color c: Color.values()) <br />
　　System.out.println(&#8220;find value:&#8221; + c); <br />
<br />
　　2．在enum中定义方法和变量，比如我们可以为Color增加一个方法随机返回一个颜色。 <br />
<br />
　　public enum Color { <br />
　　Red, <br />
　　Green, <br />
　　Blue; <br />
<br />
　　/* <br />
　　*定义一个变量表示枚举值的数目。 <br />
　　*(我有点奇怪为什么sun没有给enum直接提供一个size方法). <br />
　　*/ <br />
　　private static int number = Color.values().length ; <br />
<br />
　　/** <br />
　　* 随机返回一个枚举值 <br />
　　@return a random enum value. <br />
　　*/ <br />
　　public static Color getRandomColor(){ <br />
　　long random = System.currentTimeMillis() % number; <br />
　　switch ((int) random){ <br />
　　　case 0: <br />
　　　　return Color.Red; <br />
　　　case 1: <br />
　　　　return Color.Green; <br />
　　　case 2: <br />
　　　　return Color.Blue; <br />
　　　default : return Color.Red; <br />
　　} <br />
　　} <br />
　　} <br />
<br />
　　可以看出这在枚举类型里定义变量和方法和在普通类里面定义方法和变量没有什么区别。唯一要注意的只是变量和方法定义必须放在所有枚举值定义的后面，否则编译器会给出一个错误。 <br />
<br />
　　3．覆载(Override)toString, valueOf方法 <br />
<br />
　　前面我们已经知道enum提供了toString,valueOf等方法，很多时候我们都需要覆载默认的toString方法，那么对于enum我们怎么做呢。其实这和覆载一个普通class的toString方法没有什么区别。 <br />
<br />
　　&#8230;. <br />
　　public String toString(){ <br />
　　switch (this){ <br />
　　case Red: <br />
　　　return "Color.Red"; <br />
　　case Green: <br />
　　　return "Color.Green"; <br />
　　case Blue: <br />
　　　return "Color.Blue"; <br />
　　default: <br />
　　　return "Unknow Color"; <br />
　　} <br />
　　} <br />
　　&#8230;. <br />
　　这时我们可以看到，此时再用前面的遍历代码打印出来的是 <br />
<br />
　　Color.Red <br />
　　Color.Green <br />
　　Color.Blue <br />
<br />
　　而不是 <br />
<br />
　　Red <br />
　　Green <br />
　　Blue. <br />
<br />
　　可以看到toString确实是被覆载了。一般来说在覆载toString的时候我们同时也应该覆载valueOf方法，以保持它们相互的一致性。 <br />
<br />
　　4．使用构造函数 <br />
<br />
　　虽然enum不可以有public的构造函数，但是我们还是可以定义private的构造函数，在enum内部使用。还是用Color这个例子。 <br />
<br />
　　public enum Color { <br />
　　Red("This is Red"), <br />
　　Green("This is Green"), <br />
　　Blue("This is Blue"); <br />
<br />
　　private String desc; <br />
<br />
　　Color(String desc){ <br />
　　this.desc = desc; <br />
　　} <br />
<br />
　　public String getDesc(){ <br />
　　return this.desc; <br />
　　} <br />
<br />
　　} <br />
<br />
　　这里我们为每一个颜色提供了一个说明信息, 然后定义了一个构造函数接受这个说明信息。 <br />
<br />
　　要注意这里构造函数不能为public或者protected, 从而保证构造函数只能在内部使用，客户代码不能new一个枚举值的实例出来。这也是完全符合情理的，因为我们知道枚举值是public static final的常量而已。 <br />
<br />
　　5．实现特定的接口 <br />
<br />
　　我们已经知道enum可以定义变量和方法，它要实现一个接口也和普通class实现一个接口一样，这里就不作示例了。 <br />
<br />
　　6．定义枚举值自己的方法。 <br />
<br />
　　前面我们看到可以为enum定义一些方法，其实我们甚至可以为每一个枚举值定义方法。这样，我们前面覆载 toString的例子可以被改写成这样。 <br />
<br />
　　public enum Color { <br />
　　Red { <br />
　　public String toString(){ <br />
　　　return "Color.Red"; <br />
　　} <br />
　　}, <br />
　　Green { <br />
　　public String toString(){ <br />
　　　return "Color.Green"; <br />
　　} <br />
　　}, <br />
　　Blue{ <br />
　　public String toString(){ <br />
　　　return "Color.Blue"; <br />
　　} <br />
　　}; <br />
　　} <br />
<br />
　　从逻辑上来说这样比原先提供一个&#8220;全局&#8220;的toString方法要清晰一些。 <br />
<br />
　　总的来说，enum作为一个全新定义的类型，是希望能够帮助程序员写出的代码更加简单易懂，个人觉得一般也不需要过多的使用enum的一些高级特性，否则就和简单易懂的初衷想违背了
<img src ="http://www.blogjava.net/wjun530/aggbug/150131.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-10-02 01:48 <a href="http://www.blogjava.net/wjun530/archive/2007/10/02/150131.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>新手入门:写Java程序的三十个基本规则 </title><link>http://www.blogjava.net/wjun530/archive/2007/09/26/148416.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Wed, 26 Sep 2007 11:05:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/26/148416.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/148416.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/26/148416.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/148416.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/148416.html</trackback:ping><description><![CDATA[(1) 类名首字母应该大写。字段、方法以及对象(句柄)的首字母应小写。对于所有标识符，其中包含的所有单词都应紧靠在一起，而且大写中间单词的首字母。例如：<br />
<br />
　　ThisIsAClassName<br />
<br />
　　thisIsMethodOrFieldName<br />
<br />
　　若在定义中出现了常数初始化字符，则大写static final基本类型标识符中的所有字母。这样便可标志出它们属于编译期的常数。<br />
<br />
　　Java包(Package)属于一种特殊情况：它们全都是小写字母，即便中间的单词亦是如此。对于域名扩展名称，如com，org，net或者edu等，全部都应小写(这也是Java 1.1和Java 1.2的区别之一)。<br />
<br />
　　(2) 为了常规用途而创建一个类时，请采取&#8220;经典形式&#8221;，并包含对下述元素的定义：<br />
<br />
　　equals()<br />
<br />
　　hashCode()<br />
<br />
　　toString()<br />
<br />
　　clone()(implement Cloneable)<br />
<br />
　　implement Serializable<br />
<br />
　　(3) 对于自己创建的每一个类，都考虑置入一个main()，其中包含了用于测试那个类的代码。为使用一个项目中的类，我们没必要删除测试代码。若进行了任何形式的改动，可方便地返回测试。这些代码也可作为如何使用类的一个示例使用。<br />
<br />
　　(4) 应将方法设计成简要的、功能性单元，用它描述和实现一个不连续的类接口部分。理想情况下，方法应简明扼要。若长度很大，可考虑通过某种方式将其分割成较短的几个方法。这样做也便于类内代码的重复使用(有些时候，方法必须非常大，但它们仍应只做同样的一件事情)。 (5) 设计一个类时，请设身处地为客户程序员考虑一下(类的使用方法应该是非常明确的)。然后，再设身处地为管理代码的人考虑一下(预计有可能进行哪些形式的修改，想想用什么方法可把它们变得更简单)。<br />
<br />
　　(6) 使类尽可能短小精悍，而且只解决一个特定的问题。下面是对类设计的一些建议：<br />
<br />
　　■一个复杂的开关语句：考虑采用&#8220;多形&#8221;机制<br />
<br />
　　■数量众多的方法涉及到类型差别极大的操作：考虑用几个类来分别实现<br />
<br />
　　■许多成员变量在特征上有很大的差别：考虑使用几个类 。<br />
<br />
　　(7) 让一切东西都尽可能地&#8220;私有&#8221;——private。可使库的某一部分&#8220;公共化&#8221;(一个方法、类或者一个字段等等)，就永远不能把它拿出。若强行拿出，就可能破坏其他人现有的代码，使他们不得不重新编写和设计。若只公布自己必须公布的，就可放心大胆地改变其他任何东西。在多线程环境中，隐私是特别重要的一个因素——只有private字段才能在非同步使用的情况下受到保护。<br />
<br />
　　(8) 谨惕&#8220;巨大对象综合症&#8221;。对一些习惯于顺序编程思维、且初涉OOP领域的新手，往往喜欢先写一个顺序执行的程序，再把它嵌入一个或两个巨大的对象里。根据编程原理，对象表达的应该是应用程序的概念，而非应用程序本身。<br />
<br />
　　(9) 若不得已进行一些不太雅观的编程，至少应该把那些代码置于一个类的内部。<br />
<br />
　　(10) 任何时候只要发现类与类之间结合得非常紧密，就需要考虑是否采用内部类，从而改善编码及维护工作(参见第14章14.1.2小节的&#8220;用内部类改进代码&#8221;)。<br />
<br />
　　(11) 尽可能细致地加上注释，并用javadoc注释文档语法生成自己的程序文档。<br />
<br />
　　(12) 避免使用&#8220;魔术数字&#8221;，这些数字很难与代码很好地配合。如以后需要修改它，无疑会成为一场噩梦，因为根本不知道&#8220;100&#8221;到底是指&#8220;数组大小&#8221;还是&#8220;其他全然不同的东西&#8221;。所以，我们应创建一个常数，并为其使用具有说服力的描述性名称，并在整个程序中都采用常数标识符。这样可使程序更易理解以及更易维护。<br />
<br />
　　(13) 涉及构建器和异常的时候，通常希望重新丢弃在构建器中捕获的任何异常——如果它造成了那个对象的创建失败。这样一来，调用者就不会以为那个对象已正确地创建，从而盲目地继续。<br />
<br />
　　(14) 当客户程序员用完对象以后，若你的类要求进行任何清除工作，可考虑将清除代码置于一个良好定义的方法里，采用类似于cleanup()这样的名字，明确表明自己的用途。除此以外，可在类内放置一个boolean(布尔)标记，指出对象是否已被清除。在类的finalize()方法里，请确定对象已被清除，并已丢弃了从RuntimeException继承的一个类(如果还没有的话)，从而指出一个编程错误。在采取象这样的方案之前，请确定finalize ()能够在自己的系统中工作(可能需要调用System.runFinalizersonExit(true)，从而确保这一行为)。<br />
<br />
　　(15) 在一个特定的作用域内，若一个对象必须清除(非由垃圾收集机制处理)，请采用下述方法：初始化对象;若成功，则立即进入一个含有finally从句的try块，开始清除工作。<br />
<br />
　　(16) 若在初始化过程中需要覆盖(取消)finalize()，请记住调用super.finalize()(若Object属于我们的直接超类，则无此必要)。在对finalize()进行覆盖的过程中，对super.finalize()的调用应属于最后一个行动，而不应是第一个行动，这样可确保在需要基础类组件的时候它们依然有效。<br />
<br />
　　(17) 创建大小固定的对象集合时，请将它们传输至一个数组(若准备从一个方法里返回这个集合，更应如此操作)。这样一来，我们就可享受到数组在编译期进行类型检查的好处。此外，为使用它们，数组的接收者也许并不需要将对象&#8220;造型&#8221;到数组里。<br />
<br />
　　(18) 尽量使用interfaces，不要使用abstract类。若已知某样东西准备成为一个基础类，那么第一个选择应是将其变成一个interface(接口)。只有在不得不使用方法定义或者成员变量的时候，才需要将其变成一个abstract(抽象)类。接口主要描述了客户希望做什么事情，而一个类则致力于(或允许)具体的实施细节。<br />
<br />
　　(19) 在构建器内部，只进行那些将对象设为正确状态所需的工作。尽可能地避免调用其他方法，因为那些方法可能被其他人覆盖或取消，从而在构建过程中产生不可预知的结果(参见第7章的详细说明)。<br />
<br />
　　(20) 对象不应只是简单地容纳一些数据;它们的行为也应得到良好的定义。<br />
<br />
　　(21) 在现成类的基础上创建新类时，请首先选择&#8220;新建&#8221;或&#8220;创作&#8221;。只有自己的设计要求必须继承时，才应考虑这方面的问题。若在本来允许新建的场合使用了继承，则整个设计会变得没有必要地复杂。<br />
<br />
　　(22) 用继承及方法覆盖来表示行为间的差异，而用字段表示状态间的区别。一个非常极端的例子是通过对不同类的继承来表示颜色，这是绝对应该避免的：应直接使用一个&#8220;颜色&#8221;字段。<br />
<br />
　　(23) 为避免编程时遇到麻烦，请保证在自己类路径指到的任何地方，每个名字都仅对应一个类。否则，编译器可能先找到同名的另一个类，并报告出错消息。若怀疑自己碰到了类路径问题，请试试在类路径的每一个起点，搜索一下同名的.class文件。<br />
<br />
　　(24) 在Java 1.1 AWT中使用事件&#8220;适配器&#8221;时，特别容易碰到一个陷阱。若覆盖了某个适配器方法，同时拼写方法没有特别讲究，最后的结果就是新添加一个方法，而不是覆盖现成方法。然而，由于这样做是完全合法的，所以不会从编译器或运行期系统获得任何出错提示——只不过代码的工作就变得不正常了。<br />
<br />
　　(25) 用合理的设计方案消除&#8220;伪功能&#8221;。也就是说，假若只需要创建类的一个对象，就不要提前限制自己使用应用程序，并加上一条&#8220;只生成其中一个&#8221;注释。请考虑将其封装成一个&#8220;独生子&#8221;的形式。若在主程序里有大量散乱的代码，用于创建自己的对象，请考虑采纳一种创造性的方案，将些代码封装起来。<br />
<br />
　　(26) 警惕&#8220;分析瘫痪&#8221;。请记住，无论如何都要提前了解整个项目的状况，再去考察其中的细节。由于把握了全局，可快速认识自己未知的一些因素，防止在考察细节的时候陷入&#8220;死逻辑&#8221;中。<br />
<br />
　　(27) 警惕&#8220;过早优化&#8221;。首先让它运行起来，再考虑变得更快——但只有在自己必须这样做、而且经证实在某部分代码中的确存在一个性能瓶颈的时候，才应进行优化。除非用专门的工具分析瓶颈，否则很有可能是在浪费自己的时间。性能提升的隐含代价是自己的代码变得难于理解，而且难于维护。<br />
<br />
　　(28) 请记住，阅读代码的时间比写代码的时间多得多。思路清晰的设计可获得易于理解的程序，但注释、细致的解释以及一些示例往往具有不可估量的价值。无论对你自己，还是对后来的人，它们都是相当重要的。如对此仍有怀疑，那么请试想自己试图从联机Java文档里找出有用信息时碰到的挫折，这样或许能将你说服。<br />
<br />
　　(29) 如认为自己已进行了良好的分析、设计或者实施，那么请稍微更换一下思维角度。试试邀请一些外来人士——并不一定是专家，但可以是来自本公司其他部门的人。请他们用完全新鲜的眼光考察你的工作，看看是否能找出你一度熟视无睹的问题。采取这种方式，往往能在最适合修改的阶段找出一些关键性的问题，避免产品发行后再解决问题而造成的金钱及精力方面的损失。<br />
<br />
　　(30) 良好的设计能带来最大的回报。简言之，对于一个特定的问题，通常会花较长的时间才能找到一种最恰当的解决方案。但一旦找到了正确的方法，以后的工作就轻松多了，再也不用经历数小时、数天或者数月的痛苦挣扎。我们的努力工作会带来最大的回报(甚至无可估量)。而且由于自己倾注了大量心血，最终获得一个出色的设计方案，成功的快感也是令人心动的。坚持抵制草草完工的诱惑——那样做往往得不偿失。
<img src ="http://www.blogjava.net/wjun530/aggbug/148416.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-26 19:05 <a href="http://www.blogjava.net/wjun530/archive/2007/09/26/148416.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>this与super的应用</title><link>http://www.blogjava.net/wjun530/archive/2007/09/26/148415.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Wed, 26 Sep 2007 11:04:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/26/148415.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/148415.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/26/148415.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/148415.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/148415.html</trackback:ping><description><![CDATA[通过用static来定义方法或成员，为我们编程提供了某种便利，从某种程度上可以说它类似于C语言中的全局函数和全局变量。但是，并不是说有了这种便利，你便可以随处使用，如果那样的话，你便需要认真考虑一下自己是否在用面向对象的思想编程，自己的程序是否是面向对象的。好了，现在开始讨论this&amp;super这两个关键字的意义和用法。<br />
　　在Java中，this通常指当前对象，super则指父类的。当你想要引用当前对象的某种东西，比如当前对象的某个方法，或当前对象的某个成员，你便可以利用this来实现这个目的，当然，this的另一个用途是调用当前对象的另一个构造函数，这些马上就要讨论。如果你想引用父类的某种东西，则非super莫属。由于this与super有如此相似的一些特性和与生俱来的某种关系，所以我们在这一块儿来讨论，希望能帮助你区分和掌握它们两个。<br />
　　在一般方法中<br />
　　最普遍的情况就是，在你的方法中的某个形参名与当前对象的某个成员有相同的名字，这时为了不至于混淆，你便需要明确使用this关键字来指明你要使用某个成员，使用方法是&#8220;this.成员名&#8221;，而不带this的那个便是形参。另外，还可以用&#8220;this.方法名&#8221;来引用当前对象的某个方法，但这时this就不是必须的了，你可以直接用方法名来访问那个方法，编译器会知道你要调用的是那一个。下面的代码演示了上面的用法：<br />
　　public class DemoThis{　<br />
private String name;　<br />
private int age;　<br />
DemoThis(String name,int age){　　<br />
setName(name); <br />
//你可以加上this来调用方法，像这样：this.setName(name);但这并不是必须的　　<br />
setAge(age);　　<br />
this.print(); br&gt; }　　 <br />
public void setName(String name){　　<br />
this.name=name;//此处必须指明你要引用成员变量　<br />
} <br />
public void etAge(int age){　 <br />
this.age=age;　<br />
}　<br />
public void print(){　　<br />
System.out.println("Name="+name+" ge="+age);<br />
//在此行中并不需要用this，因为没有会导致混淆的东西　<br />
}　<br />
public static void main(String[] args){　　<br />
DemoThis dt=new DemoThis("Kevin","22");<br />
　　这段代码很简单，不用解释你也应该能看明白。在构造函数中你看到用this.print(),你完全可以用print()来代替它，两者效果一样。下面我们修改这个程序，来演示super的用法。<br />
　　class Person{　<br />
public int c;　<br />
private String name;　<br />
private int age; <br />
protected void setName(String name){　　<br />
this.name=name;　<br />
}　<br />
protected void setAge(int age){　 <br />
this.age=age;<br />
　} <br />
protected void print(){　　<br />
System.out.println("Name="+name+" Age="+age); <br />
}<br />
}<br />
public class DemoSuper extends Person{　<br />
public void print(){　　<br />
System.out.println("DemoSuper:");　 <br />
super.print(); <br />
}　<br />
public static void main(String[] args){　 <br />
DemoSuper ds=new DemoSuper();　 <br />
ds.setName("kevin");　 <br />
ds.setAge(22);　 <br />
ds.print(); <br />
}<br />
}<br />
　　在DemoSuper中，重新定义的print方法覆写了父类的print方法，它首先做一些自己的事情，然后调用父类的那个被覆写了的方法。输出结果说明了这一点：<br />
　　DemoSuper:<br />
Name=kevin Age=22<br />
<br />
　　这样的使用方法是比较常用的。另外如果父类的成员可以被子类访问，那你可以像使用this一样使用它，用&#8220;super.父类中的成员名&#8221;的方式，但常常你并不是这样来访问父类中的成员名的。<br />
　　在构造函数中构造函数是一种特殊的方法，在对象初始化的时候自动调用。在构造函数中，this和super也有上面说的种种使用方式，并且它还有特殊的地方，请看下面的例子：<br />
<br />
　　<br />
class Person{　<br />
<br />
public static void prt(String s){　　<br />
System.out.println(s);　<br />
}　<br />
Person(){　 <br />
prt("A Person.");　<br />
} <br />
Person(String name){　<br />
　prt("A person name is:"+name);　<br />
<br />
}<br />
}<br />
public class Chinese extends Person{<br />
　Chinese(){　　<br />
super();　//调用父类构造函数（1）　 <br />
prt("A chinese.");//(4) <br />
}　<br />
Chinese(String name){　　<br />
super(name);//调用父类具有相同形参的构造函数（2）　　<br />
prt("his name is:"+name); <br />
} <br />
Chinese(String name,int age){　　<br />
this(name);//调用当前具有相同形参的构造函数（3）　 <br />
prt("his age is:"+age); <br />
} <br />
public static void main(String[] args){　 <br />
Chinese cn=new Chinese();　　<br />
cn=new Chinese("kevin");　 <br />
cn=new Chinese("kevin",22); <br />
}<br />
}<br />
　　在这段程序中，this和super不再是像以前那样用&#8220;.&#8221;连接一个方法或成员，而是直接在其后跟<br />
　　上适当的参数，因此它的意义也就有了变化。super后加参数的是用来调用父类中具有相同形式的<br />
　　构造函数，如1和2处。this后加参数则调用的是当前具有相同参数的构造函数，如3处。当然，在<br />
　　Chinese的各个重载构造函数中，this和super在一般方法中的各种用法也仍可使用，比如4处，你<br />
　　可以将它替换为&#8220;this.prt&#8221;(因为它继承了父类中的那个方法）或者是&#8220;super.prt&#8221;（因为它<br />
　　是父类中的方法且可被子类访问），它照样可以正确运行。但这样似乎就有点画蛇添足的味道<br />
　　了。<br />
　　最后，写了这么多，如果你能对&#8220;this通常指代当前对象，super通常指代父类&#8221;这句话牢记在<br />
　　心，那么本篇便达到了目的，其它的你自会在以后的编程实践当中慢慢体会、掌握。
<img src ="http://www.blogjava.net/wjun530/aggbug/148415.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-26 19:04 <a href="http://www.blogjava.net/wjun530/archive/2007/09/26/148415.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>编程需要注意的陷阱：Java继承是有害的</title><link>http://www.blogjava.net/wjun530/archive/2007/09/26/148414.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Wed, 26 Sep 2007 11:03:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/26/148414.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/148414.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/26/148414.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/148414.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/148414.html</trackback:ping><description><![CDATA[大多数好的设计者象躲避瘟疫一样来避免使用实现继承(extends 关系)。实际上80%的代码应该完全用interfaces写，而不是通过extends。&#8220;JAVA设计模式&#8221;一书详细阐述了怎样用接口继承代替实现继承。这篇文章描述设计者为什么会这么作。 <br />
<br />
Extends是有害的；也许对于Charles Manson这个级别的不是，但是足够糟糕的它应该在任何可能的时候被避开。&#8220;JAVA设计模式&#8221;一书花了很大的部分讨论用interface继承代替实现继承。 <br />
<br />
好的设计者在他的代码中，大部分用interface，而不是具体的基类。本文讨论为什么设计者会这样选择，并且也介绍一些基于interface的编程基础。 <br />
<br />
接口（Interface）和类（Class）? <br />
<br />
Jams Gosling（Java之父）做发起人讲话。有人问他：&#8220;如果你重新构造Java，你想改变什么？&#8221;。&#8220;我想抛弃classes&#8221;他回答。在笑声平息后，它解释说，真正的问题不是由于class本身，而是实现继承(extends 关系)。接口继承（implements关系）是更好的。你应该尽可能的避免实现继承。 <br />
<br />
失去了灵活性 <br />
<br />
为什么你应该避免实现继承呢？第一个问题是明确的使用具体类名将你固定到特定的实现，给底层的改变增加了不必要的困难。 <br />
<br />
在当前的敏捷编程方法中，核心是并行的设计和开发的概念。在你详细设计程序前，你开始编程。这个技术不同于传统方法的形式----传统的方式是设计应该在编码开始前完成----但是许多成功的项目已经证明你能够更快速的开发高质量代码，相对于传统的按部就班的方法。但是在并行开发的核心是主张灵活性。你不得不以某一种方式写你的代码以至于最新发现的需求能够尽可能没有痛苦的合并到已有的代码中。 <br />
<br />
胜于实现你也许需要的特征，你只需实现你明确需要的特征，而且适度的对变化的包容。如果你没有这种灵活，并行的开发，那简直不可能。 <br />
<br />
对于Inteface的编程是灵活结构的核心。为了说明为什么，让我们看一下当使用它们的时候，会发生什么。考虑下面的代码： <br />
<br />
<br />
f()<br />
{ LinkedList list = new LinkedList();<br />
//...<br />
g( list );<br />
}<br />
<br />
g( LinkedList list )<br />
{<br />
list.add( ... );<br />
g2( list )<br />
}<br />
<br />
<br />
现在，假设一个对于快速查询的需求被提出，以至于这个LinkedList不能够解决。你需要用HashSet来代替它。在已有代码中，变化不能够局部化，因为你不仅仅需要修改f()也需要修改g()（它带有LinkedList参数），并且还有g()把列表传递给的任何代码。象下面这样重写代码: <br />
<br />
<br />
f()<br />
{ Collection list = new LinkedList();<br />
//...<br />
g( list );<br />
}<br />
<br />
g( Collection list )<br />
{<br />
list.add( ... );<br />
g2( list )<br />
}<br />
<br />
<br />
这样修改Linked list成hash，可能只是简单的用new HashSet()代替new LinkedList()。就这样。没有其他的需要修改的地方。 <br />
<br />
作为另一个例子，比较下面两段代码： <br />
<br />
<br />
f()<br />
{ Collection c = new HashSet();<br />
//...<br />
g( c );<br />
}<br />
<br />
g( Collection c )<br />
{<br />
for( Iterator i = c.iterator(); i.hasNext() )<br />
do_something_with( i.next() );<br />
}<br />
<br />
<br />
和 <br />
<br />
<br />
f2()<br />
{ Collection c = new HashSet();<br />
//...<br />
g2( c.iterator() );<br />
}<br />
<br />
g2( Iterator i )<br />
{ while( i.hasNext() )<br />
do_something_with( i.next() );<br />
}<br />
<br />
<br />
g2()方法现在能够遍历Collection的派生，就像你能够从Map中得到的键值对。事实上，你能够写iterator，它产生数据，代替遍历一个Collection。你能够写iterator，它从测试的框架或者文件中得到信息。这会有巨大的灵活性。 <br />
<br />
耦合 <br />
<br />
对于实现继承，一个更加关键的问题是耦合---令人烦躁的依赖，就是那种程序的一部分对于另一部分的依赖。全局变量提供经典的例子，证明为什么强耦合会引起麻烦。例如，如果你改变全局变量的类型，那么所有用到这个变量的函数也许都被影响，所以所有这些代码都要被检查，变更和重新测试。而且，所有用到这个变量的函数通过这个变量相互耦合。也就是，如果一个变量值在难以使用的时候被改变，一个函数也许就不正确的影响了另一个函数的行为。这个问题显著的隐藏于多线程的程序。 <br />
<br />
作为一个设计者，你应该努力最小化耦合关系。你不能一并消除耦合，因为从一个类的对象到另一个类的对象的方法调用是一个松耦合的形式。你不可能有一个程序，它没有任何的耦合。然而，你能够通过遵守OO规则，最小化一定的耦合（最重要的是，一个对象的实现应该完全隐藏于使用他的对象）。例如，一个对象的实例变量（不是常量的成员域），应该总是private。我意思是某段时期的，无例外的，不断的。（你能够偶尔有效地使用protected方法，但是protected实例变量是可憎的事）同样的原因你应该不用get/set函数---他们对于是一个域公用只是使人感到过于复杂的方式（尽管返回修饰的对象而不是基本类型值的访问函数是在某些情况下是由原因的，那种情况下，返回的对象类是一个在设计时的关键抽象）。 <br />
<br />
这里，我不是书生气。在我自己的工作中，我发现一个直接的相互关系在我OO方法的严格之间，快速代码开发和容易的代码实现。无论什么时候我违反中心的OO原则，如实现隐藏，我结果重写那个代码（一般因为代码是不可调试的）。我没有时间重写代码，所以我遵循那些规则。我关心的完全实用?我对干净的原因没有兴趣。 <br />
<br />
脆弱的基类问题 <br />
<br />
现在，让我们应用耦合的概念到继承。在一个用extends的继承实现系统中，派生类是非常紧密的和基类耦合，当且这种紧密的连接是不期望的。设计者已经应用了绰号&#8220;脆弱的基类问题&#8221;去描述这个行为。基础类被认为是脆弱的是，因为你在看起来安全的情况下修改基类，但是当从派生类继承时，新的行为也许引起派生类出现功能紊乱。你不能通过简单的在隔离下检查基类的方法来分辨基类的变化是安全的；而是你也必须看（和测试）所有派生类。而且，你必须检查所有的代码，它们也用在基类和派生类对象中，因为这个代码也许被新的行为所打破。一个对于基础类的简单变化可能导致整个程序不可操作。 <br />
<br />
让我们一起检查脆弱的基类和基类耦合的问题。下面的类extends了Java的ArrayList类去使它像一个stack来运转： <br />
<br />
<br />
class Stack extends ArrayList<br />
{ private int stack_pointer = 0;<br />
<br />
public void push( Object article )<br />
{ add( stack_pointer++, article );<br />
}<br />
<br />
public Object pop()<br />
{ return remove( --stack_pointer );<br />
}<br />
<br />
public void push_many( Object[] articles )<br />
{ for( int i = 0; i &lt; articles.length; ++i )<br />
push( articles[i] );<br />
}<br />
}<br />
<br />
<br />
甚至一个象这样简单的类也有问题。思考当一个用户平衡继承和用ArrayList的clear()方法去弹出堆栈时： <br />
<br />
<br />
Stack a_stack = new Stack();<br />
a_stack.push("1");<br />
a_stack.push("2");<br />
a_stack.clear();<br />
<br />
<br />
这个代码成功编译，但是因为基类不知道关于stack指针堆栈的情况,这个stack对象当前在一个未定义的状态。下一个对于push（）调用把新的项放入索引2的位置。（stack_pointer的当前值），所以stack有效地有三个元素-下边两个是垃圾。（Java的stack类正是有这个问题，不要用它）. <br />
<br />
对这个令人讨厌的继承的方法问题的解决办法是为Stack覆盖所有的ArrayList方法，那能够修改数组的状态，所以覆盖正确的操作Stack指针或者抛出一个例外。(removeRange()方法对于抛出一个例外一个好的候选方法)。 <br />
<br />
这个方法有两个缺点。第一，如果你覆盖了所有的东西，这个基类应该真正的是一个interface，而不是一个class。如果你不用任何继承方法，在实现继承中就没有这一点。第二，更重要的是，你不能够让一个stack支持所有的ArrayList方法。例如，令人烦恼的removeRange()没有什么作用。唯一实现无用方法的合理的途径是使它抛出一个例外，因为它应该永远不被调用。这个方法有效的把编译错误成为运行错误。不好的方法是，如果方法只是不被定义，编译器会输出一个方法未找到的错误。如果方法存在，但是抛出一个例外，你只有在程序真正的运行时，你才能够发现调用错误。 <br />
<br />
对于这个基类问题的一个更好的解决办法是封装数据结构代替用继承。这是新的和改进的Stack的版本： <br />
<br />
<br />
class Stack<br />
{<br />
private int stack_pointer = 0;<br />
private ArrayList the_data = new ArrayList();<br />
<br />
public void push( Object article )<br />
{<br />
the_data.add( stack_poniter++, article );<br />
}<br />
<br />
public Object pop()<br />
{<br />
return the_data.remove( --stack_pointer );<br />
}<br />
<br />
public void push_many( Object[] articles )<br />
{<br />
for（ int i = 0; i &lt; o.length; ++i ）<br />
push( articles[i] );<br />
}<br />
}<br />
<br />
<br />
到现在为止，一直都不错，但是考虑脆弱的基类问题，我们说你想要在stack创建一个变量， 用它在一段周期内跟踪最大的堆栈尺寸。一个可能的实现也许象下面这样: <br />
<br />
<br />
class Monitorable_stack extends Stack<br />
{<br />
private int high_water_mark = 0;<br />
private int current_size;<br />
<br />
public void push( Object article )<br />
{<br />
if( ++current_size &gt; high_water_mark )<br />
high_water_mark = current_size;<br />
super.push( article );<br />
}<br />
<br />
publish Object pop()<br />
{<br />
--current_size;<br />
return super.pop();<br />
}<br />
<br />
public int maximum_size_so_far()<br />
{<br />
return high_water_mark;<br />
}<br />
}<br />
<br />
<br />
这个新类运行的很好，至少是一段时间。不幸的是，这个代码发掘了一个事实，push_many()通过调用push()来运行。首先，这个细节看起来不是一个坏的选择。它简化了代码，并且你能够得到push()的派生类版本，甚至当Monitorable_stack通过Stack的参考来访问的时候，以至于high_water_mark能够正确的更新<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/148414.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-26 19:03 <a href="http://www.blogjava.net/wjun530/archive/2007/09/26/148414.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java类库（java.lang）包概述</title><link>http://www.blogjava.net/wjun530/archive/2007/09/24/147663.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Mon, 24 Sep 2007 00:21:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/24/147663.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/147663.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/24/147663.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/147663.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/147663.html</trackback:ping><description><![CDATA[1. 接口java.lang.Appendable可追加（append）接口。实现了该接口的类的对象实例具有可向其追加字符或字符序列的能力。希望能够接收Formatter输出的类必须实现该接口。java.lang.CharSequenceCharSequence是指可阅读的字符序列。该接口为只读访问各种类型的字符序列提供了统一的方法。java.lang.Cloneable可克隆接口。实现了该接口的类具有克隆的能力。可以通过Object.clone()方法将类的实例对象的域（field）逐个复制到同一个类的另外一个实例中。如果使用Object.clone()方法克隆没有实现该接口的类的实例对象，将会触发CloneNotSupportedException异常。java.lang.Comparable可比较接口。实现了该接口的类的两个实例对象之间可以进行比较。比较结果负数（-1）、0、正数（1）分别代表比较对象与被比较对象之间的关系分别是小于、等于、大于。可对实现了该接口的类的多个实例对象进行排序。java.lang.Iterable可迭代接口。实现了该接口的类一般作为容器，且具有提供依次访问被包容对象功能的能力。java.lang.Readable可读取接口。客户对象可以从实现了该接口的类的实例对象中读取字符。java.lang.Runable可运行接口。实现了该接口的类的实例对象将在一个单独的线程（thread）中被运行。没有扩展Thread的类可以通过实现该接口，开发出多线程服务的能力。java.lang.Thread.UncaughtExceptionHandler线程未捕获异常控制器接口。在线程执行过程中引发了异常而没有捕获时， 实现了该接口的类可以作为控制器处理该状况。 <br />
<br />
2. 类java.lang.Boolean将原始数据类型boolean对象化的类。该类的实例对象除了包含一个boolean值之外，还提供boolean String之间转化等处理boolean的方法。java.lang.Byte将原始数据类型byte对象化的类。该类的实例对象除了包含一个byte值之外，还提供byte与其他类型之间转化等处理byte的方法。java.lang.Character将原始数据类型char对象化的类。该类的实例对象除了包含一个char值之外，还提供了处理字符的方法。Java 2平台在字符数组、字符串或StringBuffer中使用UTF-16表达字符。UTF-16使用16至21位二进制位表达，即从/u0000到/u10FFFF。/u0000到/uFFFF表示基本的16位字符，/u10000到/u10FFFF表示辅助字符（supplymentary characters）。 辅助字符由一个高位替代符（high-surrogate ）和一个低位替代符（low-surrogate ）共同组成。高位替代符使用一个/uD800到/uDBFF之间的字符表示；低位替代符使用一个/uDC00到/uDFFF之间的字符表示。假设：A代表辅助字符（SC）的码点值； B代表SC的高位替代符的码点（Unicode code point）值； C代表SC的低位替代符的码点值。那么下面的等式成立：A = (B - 0xD800) &lt;&lt; 10 + (C - 0xDC00) + 0x10000在将Unicode还原为可阅读字符的时候，当且仅当当前字符时高位替代符，下一字符是低位替代符时，此连续的两个字符被解释为辅助字符。java.lang.Character.Subset该类的实例对表达某个Unicode字符集的特定子集。java.lang.Character.UnicodeBlock该类的实例对象表达一族相关的子Unicode字符集。这些子字符集通常服务于相同的目的。java.lang.Class该类的实例对象表达Java应用中正在运行的类或者接口。该类没有公共的构造方法，所以不能由Java虚拟机自动实例化，而是由ClassLoader实例化。java.lang.ClassLoader该类是Java类加载器，负责根据制定的二进制名称加载相应的类。在Java语言规范中规定，类、接口或原始数据类型有三种命名，他们分别是许可名（fully qualified name）、规范名（canonical name）、二进制名（binary name）。不同的类加载器根据类的二进制名从不同的源中读取二进制的类&#8220;*.class&#8221;信息，并生成Class对象。每个类中都有对其加载器的引用。 <br />
<br />
<br />
package org.javaplus.jdkoverview; <br />
public class OuterClass { <br />
public class InnerClass { <br />
public InnerClass() {} <br />
} <br />
public OutterClass() {} <br />
} <br />
public class SubClass extends OuterClass { <br />
public SubClass() {} <br />
}<br />
<br />
<br />
org.javaplus.jdkoverview.SubClass的内部类（InnerClass）的名称许可名: <br />
<br />
org.javaplus.jdkoverview.SubClass.InnerClass规范名: <br />
<br />
org.javaplus.jdkoverview.OuterClass.InnerClass二进制名: <br />
<br />
org.javaplus.jdkoverview.OuterClass$InnerClassjava.lang.Compiler该类提供Java到本机代码的支持，及相关服务。该类只是JIT编译器（Java Just-in-Time Compiler）的一个占位符而已。如果JVM（Java Virtual Machine ）第一次启动时未成功加载JIT编译器，则本类不做任何工作。java.lang.Double将原始数据类型double对象化的类。该类的实例对象除了包含一个double值之外，还提供double与其他类型之间转化等处理double的方法<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/147663.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-24 08:21 <a href="http://www.blogjava.net/wjun530/archive/2007/09/24/147663.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用java实现的经典递归算法</title><link>http://www.blogjava.net/wjun530/archive/2007/09/24/147662.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Mon, 24 Sep 2007 00:19:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/24/147662.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/147662.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/24/147662.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/147662.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/147662.html</trackback:ping><description><![CDATA[一、写作此文的原因：<br />
学过程序设计的朋友都知道，存在自调用的算法称作递归算法。<br />
递归往往能给我们带来非常简洁非常直观的代码形势，从而使我们的编码大大简化，然而递归的思维确实很我们的常规思维相逆的，我们通常都是从上而下的思维问题， 而递归趋势从下往上的进行思维，正由于此，很多人对于递归有着深深的恐惧，我曾经也是如此，如今为把我的经验通过几个经典的例子与初学者共享，故作此文，希望能对需要者有所助益，如若如此，便是幸甚。。。<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
二、递归算法设计的基本思想是：对于一个复杂的问题，把员问题分解为若干个相对简单前类同的子问题，继续下去直到子问题简单到能够直接求解，也就是说到了递推的出口，这样原问题就有递推得解。<br />
关键要抓住的是：（1）递归出口<br />
（2）地推逐步向出口逼近<br />
<br />
<br />
三、具体实例 <br />
1。汉诺塔<br />
这是递归的超经典的例子，几乎每本程序设计书上谈到递归都会介绍。具体情景不再赘述。以我上述的方法观之：（1）递归的出口在于disk数为一的时候<br />
（2）向出口逼近：如果不是一，是n ，则我们先挪动上面n-1块disk，等上面挪完，即递归返回的时候，我们挪动最底下的disk。<br />
<br />
仅仅如此，一个貌似十分复杂的问题就解决了，因为挪动那n-1块disk的时候，会继续向上减少，直到disk的数量为一为止。下面给出ｊａｖａ程序编码（已测试过，运行正常）：<br />
<br />
import javax.swing.JOptionPane;<br />
<br />
public class Hanoi {<br />
private static final String DISK_B = "diskB";<br />
private static final String DISK_C = "diskC";<br />
private static final String DISK_A = "diskA";<br />
static String from=DISK_A;<br />
static String to=DISK_C;<br />
static String mid=DISK_B;<br />
public static void main(String[] args) {<br />
String input=JOptionPane.showInputDialog("please input the number of the disks you want me move.");<br />
int num=Integer.parseInt(input);<br />
move(num,from,mid,to);<br />
}<br />
private static void move(int num, String from2, String mid2, String to2) {<br />
if(num==1){<br />
System.out.println("move disk 1 from "+from2+" to "+to2);<br />
}<br />
else {<br />
move(num-1,from2,to2,mid2);<br />
System.out.println("move disk "+num+" from "+from2+" to "+to2);<br />
move(num-1,mid2,from2,to2);<br />
}<br />
<br />
}<br />
}<br />
<br />
<br />
2。这是一个排列的例子，它所做的工作是将输入的一个字符串中的所有元素进行排序并输出，例如：你给出的参数是"abc" 则程序会输出：<br />
abc<br />
acb<br />
bac<br />
bca<br />
cab<br />
cba<br />
<br />
　　（１）算法的出口在于：ｌｏｗ＝ｈｉｇｈ也就是现在给出的排列元素只有一个时。<br />
　　（２）算法的逼近过程：先确定排列的第一位元素，也就是循环中ｉ所代表的元素，<br />
然后ｌｏｗ＋１开始减少排列元素，如此下去，直到ｌｏｗ＝ｈｉｇｈ<br />
public static void permute(String str) {<br />
char[] strArray = str.toCharArray();<br />
permute(strArray, 0, strArray.length - 1);<br />
}<br />
<br />
public static void permute(char[] list, int low, int high) {<br />
int i;<br />
if (low == high) {<br />
String cout = "";<br />
for (i = 0; i &lt;= high; i++)<br />
cout += list[i];<br />
System.out.println(cout);<br />
} else {<br />
<br />
for (i = low; i &lt;= high; i++) {<br />
char temp = list[low];<br />
list[low] = list[i];<br />
list[i] = temp;<br />
permute(list, low + 1, high);<br />
temp = list[low];<br />
list[low] = list[i];<br />
list[i] = temp;<br />
}<br />
}<br />
}<br />
<br />
３．这是一个组合的例子，与上述的例子相似，只是它所做的工作是，输出所给字符串中制定数目的元素的组合种类<br />
　　　　　（１）程序出口在于ｎ＝１，此时只要输出目标数组的所有元素即可<br />
　　　　　（２）逼近过程，当ｎ＞１　的时候，我们先取第一个元素放入目标数组中，然后ｎ－１，如此下去，最后出来。<br />
　　　　　import javax.swing.JOptionPane;<br />
<br />
public class Combination {<br />
<br />
/**<br />
* @param args<br />
*/<br />
public static void main(String[] args) {<br />
<br />
String input = JOptionPane<br />
.showInputDialog("please input your String: ");<br />
String numString = JOptionPane<br />
.showInputDialog("please input the number of your Combination: ");<br />
int num = Integer.parseInt(numString);<br />
Combine(input, num);<br />
}<br />
<br />
private static void Combine(String input, int num) {<br />
char[] a = input.toCharArray();<br />
String b = "";<br />
Combine(a, num, b, 0, a.length);<br />
<br />
}<br />
<br />
private static void Combine(char[] a, int num, String b, int low, int high) {<br />
if (num == 0) {<br />
System.out.println(b);<br />
} else {<br />
for (int i = low; i &lt; a.length; i++) {<br />
b += a[i];<br />
Combine(a, num - 1, b, i+1, a.length);<br />
b=b.substring(0, b.length()-1);<br />
}<br />
}<br />
<br />
}<br />
}<br />
<br />
<br />
由于递归的表述确实不易加上本人的水平有限，颇有以己之昏昏欲人之昭昭的意味，还望大家多多谅解。
<img src ="http://www.blogjava.net/wjun530/aggbug/147662.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-24 08:19 <a href="http://www.blogjava.net/wjun530/archive/2007/09/24/147662.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于输入输出流的一个问题</title><link>http://www.blogjava.net/wjun530/archive/2007/09/24/147661.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Mon, 24 Sep 2007 00:18:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/24/147661.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/147661.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/24/147661.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/147661.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/147661.html</trackback:ping><description><![CDATA[我是一个java初学者碰到一些小问题，希望前辈们给我解答一下，谢谢！<br />
原题目：<br />
编写应用程序，利用缓冲输入流BufferedInputStream 从键盘输入字符串，当输入的字符串为&#8220;exit&#8221;时结束输入，将输入的所有字符串存放到 my file.txt中。<br />
编写应用程序，读出&#8220;my file.txt&#8221;的内容，并将读出的内容输出到屏幕上，读文件结束后，显示&#8220;Finished reading,programe ended.&#8221;.<br />
代码：<br />
import java.io.*;<br />
public class Ex3 <br />
{<br />
public static void main(String[]args)throws Exception<br />
{<br />
BufferedInputStream bis=new BufferedInputStream(System.in);<br />
FileWriter fw=new FileWriter("my file.txt");<br />
while(true)<br />
{<br />
byte[]b=new byte[200];<br />
int len=bis.read(b);<br />
String str=new String(b,0,len-2);<br />
if(str.equals("exit"))<br />
{<br />
System.exit(0);<br />
}<br />
fw.write(str);<br />
fw.flush();<br />
}<br />
}<br />
}<br />
上面的代码中为什么String str=new String(b,0,len-2);这个方法的参数最后一个要用<br />
len-2？尝试过len或者len-1，结果会将exit也写入文件，len-1还有回车操作符，不明白。<br />
试写的代码：<br />
import java.io.*;<br />
public class Ex4 <br />
{<br />
public static void main(String[]args)throws Exception<br />
{<br />
FileReader fr=new FileReader("my file.txt");<br />
FileOutputStream fos=new FileOutputStream("my file.txt");<br />
int c;<br />
byte b[]=new byte[200];<br />
int len=fr.read();<br />
while((c=fr.read())!=-1)<br />
{<br />
<br />
fos.write(b);<br />
}<br />
System.out.print("Finished reading,programe ended.");<br />
fr.close();<br />
fos.close();<br />
<br />
}<br />
}<br />
思路是否正确，这道题应该怎么写？<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/147661.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-24 08:18 <a href="http://www.blogjava.net/wjun530/archive/2007/09/24/147661.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA 实现各种排序算法和复杂度分析2</title><link>http://www.blogjava.net/wjun530/archive/2007/09/20/146796.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Thu, 20 Sep 2007 07:58:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/20/146796.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/146796.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/20/146796.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/146796.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/146796.html</trackback:ping><description><![CDATA[接上文<br />
<br />
第三种：插入排序<br />
<br />
public static int[] insertionSort(int[] a) {<br />
int n = a.length;<br />
for (int i = 1; i &lt; n; i++) {<br />
int temp = a[i];<br />
int j;<br />
for (j = i - 1; j &gt;= 0 &amp;&amp; temp &lt; a[j]; j--) {<br />
a[j + 1] = a[j];<br />
}<br />
a[j + 1] = temp;<br />
}<br />
return a;<br />
}<br />
}<br />
<br />
算法分析： 插入排序的思想是这样的，第一层for循环表示要循环n次，且每次循环要操作的主体是a[i]，第二层循环是对a[i]的具体操作，是从原数祖第i个位置起，向前比较，若a[i]比前面的数小，前面的数后移占去a[i]的位置，同时也为a[i]空出了插入地点，然后向前继续比较，直到a[i]比前面的数来的大，插入。下一次循环开始，这样就完成一个完整的升序插入排序。<br />
<br />
很明显，这种排序也是不稳定的，<br />
最好的情况是：顺序已经排好那么我们只要进行n-1次比较即可。<br />
最坏的情况是：顺序恰好是逆序，惨了，我们要比较1+2+...+n-1次<br />
<br />
平均的复杂度算起来还是比较困难的，也是很有参考价值的：<br />
<br />
1。首先，我们来看 对于第i个元素 a[i] 的操作<br />
<br />
从等概率角度思考：a[i]只比较 1 次的概率为 1/i;<br />
a[i]只比较 2 次的概率为 1/i;<br />
a[i]只比较 3 次的概率为 1/i;<br />
。<br />
。 <br />
。<br />
<br />
a[i]只比较 i-1 次的概率为 1/i;<br />
a[i]只比较 i 次的概率为 1/i;<br />
于是又编号为i的元素平均比较次数为：(1/i)*(1+2+3+...+i)=(i+1)/2<br />
<br />
2。然后我们来看<br />
<br />
平均比较次数为 T=(2+3+4+...+n)/2<br />
所以插入排序的平均时间复杂度也是O(n^2).<br />
<br />
<br />
<br />
第四种：Rank排序<br />
public static int[] rankSort(int[] a){<br />
int n=a.length;<br />
int[] r1=new int[n];<br />
int[] r2=new int[n];<br />
for (int i = 0; i <n; i++) {<br />
for(int j=i+1;j<n;j++){<br />
if(a[i]<a[j])<br />
r1[j]++;<br />
else <br />
r1[i]++;<br />
}<br />
}<br />
for(int i=0;i<n;i++){<br />
r2[r1[i]]=a[i];<br />
}<br />
return r2;<br />
}<br />
<br />
<br />
<br />
<br />
<br />
算法分析：Rank排序是基于这样的思想，建立一个和待排序数组相同大小的数组，初识化为全0，然后扫描原数组将每个数的大小排名，然后更具排名安排各自元素的位次，思路非常简单<br />
复杂度分析：这个算法是稳定的一共要比较的次数为n*(n-1)/2<br />
时间复杂度是O(n^2);
<img src ="http://www.blogjava.net/wjun530/aggbug/146796.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-20 15:58 <a href="http://www.blogjava.net/wjun530/archive/2007/09/20/146796.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA 实现各种排序算法和复杂度分析1</title><link>http://www.blogjava.net/wjun530/archive/2007/09/20/146795.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Thu, 20 Sep 2007 07:57:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/20/146795.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/146795.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/20/146795.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/146795.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/146795.html</trackback:ping><description><![CDATA[第一种：冒泡排序<br />
<br />
public static int[] bubbleSort(int[] a) {<br />
for (int i = 0; i &lt; a.length; i++) {<br />
for (int j = 0; j &lt; (a.length - i) - 1; j++) {<br />
if (a[j] &gt; a[j + 1]) {<br />
int temp = a[j];<br />
a[j] = a[j + 1];<br />
a[j + 1] = temp;<br />
}<br />
}<br />
}<br />
return a;<br />
}<br />
<br />
复杂度分析：冒泡排序是不稳定的排序算法，一共要比较((n-1)+(n-2)+...+3+2+1)=n*(n-1)/2次，所以时间复杂度是O(n^2)。<br />
<br />
<br />
<br />
<br />
第二种：选择排序<br />
<br />
public static int[] selecitonSort(int[] a) {<br />
for (int i = 0; i &lt; a.length; i++) {<br />
int max = a[0];<br />
int count = 0;<br />
int k = a.length - i - 1;<br />
for (int j = 0; j &lt; a.length - i; j++) {<br />
if (max &lt; a[j]) {<br />
max = a[j];<br />
count = j;<br />
}<br />
}<br />
a[count] = a[k];<br />
a[k] = max;<br />
}<br />
return a;<br />
}<br />
<br />
<br />
复杂度分析：选择排序是不稳定算法，最好的情况是最好情况是已经排好顺序，只要比较<br />
n*(n-1)/2次即可，最坏情况是逆序排好的，那么还要移动 O(n)次,由于是低阶故而不考虑<br />
不难得出选择排序的时间复杂度是 O(n^2)<br />
<br />
<br />
<br />
第三种：插入排序<br />
待续。。。<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/146795.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-20 15:57 <a href="http://www.blogjava.net/wjun530/archive/2007/09/20/146795.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>新手入门：Java数组特点及基本使用技巧</title><link>http://www.blogjava.net/wjun530/archive/2007/09/20/146794.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Thu, 20 Sep 2007 07:56:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/20/146794.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/146794.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/20/146794.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/146794.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/146794.html</trackback:ping><description><![CDATA[1．关于数组的特点 <br />
<br />
1）在Java中，无论使用数组或容器，都有边界检查。如果越界操作就会得到一个RuntimeException异常。 <br />
<br />
2）数组只能保存特定类型。数组可以保存基本类型，容器则不能。容器不以具体的类型来处理对象，它们将所有对象都按Object类型处理。 <br />
<br />
3）容器类只能保存对象的引用。而数组既可以创建为直接保存基本类型，也可以保存对象的引用。在容器中可以使用包装类，如Integer、Double等来实现保存基本数据类型值。 <br />
<br />
4）对象数组和基本类型数组在使用上几乎是相同的；唯一的区别是对象数组保存的是引用，基本类型数组保存基本类型的值。 <br />
<br />
2．关于数组定义 <br />
<br />
1）数组在定义时，不能分配空间。只有定义完后，可以给数组分配空间。 <br />
<br />
int num[]; <br />
<br />
num=new int[3]; <br />
<br />
或 <br />
<br />
int num[]=new int[3]; <br />
<br />
注意 <br />
<br />
int [] num=new int[]{1,2,3}; //ok <br />
<br />
int [] num=new int[3]{1,2,3}; //error; <br />
<br />
2）可以这样定义二维数组。 <br />
<br />
int [][] num; <br />
<br />
//or <br />
<br />
num=new int[3][]; <br />
<br />
num[0]=new int[5]; <br />
<br />
num[1]=new int[3]; <br />
<br />
num[2]=new int[2]; <br />
<br />
3）二维数组赋初值。 <br />
<br />
<br />
int [][] num=new int[][]{1,2,3,4,5,5}; //error<br />
int [][] num=new int[][]{{1,2,3},{4,5,5}}; //ok<br />
int [][] num=new int[2][]{{1,2,3},{4,5,5}}; //error<br />
int [][] num={{1,2,3},{4,5,6}}; //ok<br />
<br />
<br />
3．关于数组初始化 <br />
<br />
对象数组在创建之初会自动初始化成null，由原始数据类型构成的数组会自动初始化成零（针对数值类型），(Char)0 （针对字符类型）或者false （针对布尔类型）。 <br />
<br />
4．数组有关引用的问题 <br />
<br />
int[] a1 = { 1, 2, 3, 4, 5 }; <br />
<br />
int[] a2; <br />
<br />
a2 = a1;//这里只是复制了一个引用 <br />
<br />
看以下代码： <br />
<br />
<br />
public class Arrays {<br />
public static void main(String[] args) {<br />
int[] a1 = { 1, 2, 3, 4, 5 };<br />
for (int i = 0; i &lt; a1.length; i++)<br />
System.out.println("a1[" + i + "] = " + a1[i]);<br />
int[] a2;<br />
a2 = a1;<br />
for (int i = 0; i &lt; a2.length; i++)<br />
a2[i]++;<br />
System.out.println("-----after change a2------");<br />
for (int i = 0; i &lt; a1.length; i++)<br />
System.out.println("a1[" + i + "] = " + a1[i]);<br />
System.out.println("-----after change a2[0]------");<br />
a2[0] = 333;<br />
System.out.println("a2[0]=" + a2[0]);<br />
System.out.println("a1[0]=" + a1[0]);<br />
System.out.println("----- a2------");<br />
for (int i = 0; i &lt; a2.length; i++)<br />
System.out.println("a2[" + i + "] = " + a2[i]);<br />
}<br />
}<br />
<br />
<br />
结果： <br />
<br />
a1[0] = 1 <br />
<br />
a1[1] = 2 <br />
<br />
a1[2] = 3 <br />
<br />
a1[3] = 4 <br />
<br />
a1[4] = 5 <br />
<br />
-----after change a2------ <br />
<br />
a1[0] = 2 <br />
<br />
a1[1] = 3 <br />
<br />
a1[2] = 4 <br />
<br />
a1[3] = 5 <br />
<br />
a1[4] = 6 <br />
<br />
-----after change a2[0]------ <br />
<br />
a2[0]=333 <br />
<br />
a1[0]=333 <br />
<br />
----- a2------ <br />
<br />
a2[0] = 333 <br />
<br />
a2[1] = 3 <br />
<br />
a2[2] = 4 <br />
<br />
a2[3] = 5 <br />
<br />
a2[4] = 6 <br />
<br />
数组a1和a2始终在操作同一个对象。 <br />
<br />
5．关于数组的相关操作 <br />
<br />
1）在Java中，所有的数组都有一个缺省的属性length，用于获取数组中元素的个数。 <br />
<br />
2）数组的复制：System.arraycopy()。 <br />
<br />
3）数组的排序：Arrays.sort()。 <br />
<br />
4）在已排序的数组中查找某个元素：Arrays.binarySearch()。 <br />
<br />
6．关于数组的排序操作 <br />
<br />
1）对象数组排序，必须实现Comparable接口。 <br />
<br />
<br />
import java.util.Arrays;<br />
class Student implements Comparable {<br />
int num;<br />
String name;<br />
<br />
Student(int num, String name) {<br />
this.num = num;<br />
this.name = name;<br />
}<br />
<br />
public String toString()// 重写toString()方法，以便main:System.out.println(ss[i]);<br />
{<br />
return "number=" + num + "," + "name=" + name;<br />
}<br />
<br />
public int compareTo(Object o) {<br />
Student s = (Student) o;<br />
return num &gt; s.num ? 1 : (num == s.num ? 0 : -1);<br />
}<br />
}<br />
<br />
class ArrayTest {<br />
public static void main(String[] args) {<br />
Student[] ss = new Student[] { new Student(1, "zhangsan"),<br />
new Student(2, "lisi"), new Student(3, "wangwu") };<br />
Arrays.sort(ss);<br />
for (int i = 0; i &lt; ss.length; i++) {<br />
System.out.println(ss[i]);<br />
}<br />
}<br />
}<br />
<br />
<br />
结果： <br />
<br />
number=1,name=zhangsan <br />
<br />
number=2,name=lisi <br />
<br />
number=3,name=wangwu <br />
<br />
2）以num为第一关键字，name为第二关键字排序 <br />
<br />
<br />
import java.util.Arrays;<br />
<br />
class Student implements Comparable {<br />
int num;<br />
String name;<br />
<br />
Student(int num, String name) {<br />
this.num = num;<br />
this.name = name;<br />
}<br />
<br />
public String toString() {<br />
return "number=" + num + "," + "name=" + name;<br />
}<br />
<br />
public int compareTo(Object o) {<br />
Student s = (Student) o;<br />
int result = num &gt; s.num ? 1 : (num == s.num ? 0 : -1);<br />
if (0 == result) {<br />
result = name.compareTo(s.name);<br />
}<br />
return result;<br />
}<br />
}<br />
<br />
class ArrayTest {<br />
public static void main(String[] args) {<br />
Student[] ss = new Student[] { new Student(1, "zhangsan"),<br />
new Student(2, "lisi"), new Student(3, "wangwu"),<br />
new Student(3, "mybole") };<br />
Arrays.sort(ss);<br />
for (int i = 0; i &lt; ss.length; i++) {<br />
System.out.println(ss[i]);<br />
}<br />
}<br />
}<br />
<br />
<br />
结果： <br />
<br />
number=1,name=zhangsan <br />
<br />
number=2,name=lisi <br />
<br />
number=3,name=mybole <br />
<br />
number=3,name=wangwu <br />
<br />
7．关于java.util.Arrays <br />
<br />
1）java.util.Class Arrays&#8217;s architecture <br />
<br />
java.lang.Object <br />
<br />
| <br />
<br />
+--java.util.Arrays <br />
<br />
2）说明 <br />
<br />
这个类提供的基本上都是静态方法，用户进行数组操作，binarySearch()：数组中特定元素的寻找，equals()：比较两个数组是否相等（在相同位置上的元素是否相等）,fill()：数组填充，sort()：数组排序<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/146794.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-20 15:56 <a href="http://www.blogjava.net/wjun530/archive/2007/09/20/146794.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java异常处理机制的详细讲解和使用技巧</title><link>http://www.blogjava.net/wjun530/archive/2007/09/20/146793.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Thu, 20 Sep 2007 07:55:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/20/146793.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/146793.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/20/146793.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/146793.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/146793.html</trackback:ping><description><![CDATA[1. 异常机制 <br />
<br />
1.1 <br />
<br />
异常机制是指当程序出现错误后，程序如何处理。具体来说，异常机制提供了程序退出的安全通道。当出现错误后，程序执行的流程发生改变，程序的控制权转移到异常处理器。 <br />
<br />
1.2 <br />
<br />
传统的处理异常的办法是，函数返回一个特殊的结果来表示出现异常（通常这个特殊结果是大家约定俗称的），调用该函数的程序负责检查并分析函数返回的结果。这样做有如下的弊端：例如函数返回-1代表出现异常，但是如果函数确实要返回-1这个正确的值时就会出现混淆；可读性降低，将程序代码与处理异常的代码混爹在一起；由调用函数的程序来分析错误，这就要求客户程序员对库函数有很深的了解。 <br />
<br />
1.3 异常处理的流程 <br />
<br />
1.3.1 遇到错误，方法立即结束，并不返回一个值；同时，抛出一个异常对象 <br />
<br />
1.3.2 调用该方法的程序也不会继续执行下去，而是搜索一个可以处理该异常的异常处理器，并执行其中的代码 <br />
<br />
<br />
2 异常的分类 <br />
<br />
2.1 异常的分类 <br />
<br />
2.1.1 <br />
<br />
异常的继承结构：基类为Throwable，Error和Exception继承Throwable，RuntimeException和IOException等继承Exception，具体的RuntimeException继承RuntimeException。 <br />
<br />
2.1.2 <br />
<br />
Error和RuntimeException及其子类成为未检查异常（unchecked），其它异常成为已检查异常（checked）。 <br />
<br />
2.2 每个类型的异常的特点 <br />
<br />
2.2.1 Error体系 <br />
<br />
Error类体系描述了Java运行系统中的内部错误以及资源耗尽的情形。应用程序不应该抛出这种类型的对象（一般是由虚拟机抛出）。如果出现这种错误，除了尽力使程序安全退出外，在其他方面是无能为力的。所以，在进行程序设计时，应该更关注Exception体系。 <br />
<br />
2.2.2 Exception体系 <br />
<br />
Exception体系包括RuntimeException体系和其他非RuntimeException的体系 <br />
<br />
2.2.2.1 RuntimeException <br />
<br />
RuntimeException体系包括错误的类型转换、数组越界访问和试图访问空指针等等。处理RuntimeException的原则是：如果出现RuntimeException，那么一定是程序员的错误。例如，可以通过检查数组下标和数组边界来避免数组越界访问异常。 <br />
<br />
2.2.2.2 其他（IOException等等） <br />
<br />
这类异常一般是外部错误，例如试图从文件尾后读取数据等，这并不是程序本身的错误，而是在应用环境中出现的外部错误。 <br />
<br />
2.3 与C++异常分类的不同 <br />
<br />
2.3.1 <br />
<br />
其实，Java中RuntimeException这个类名起的并不恰当，因为任何异常都是运行时出现的。（在编译时出现的错误并不是异常，换句话说，异常就是为了解决程序运行时出现的的错误）。 <br />
<br />
2.3.2 <br />
<br />
C++中logic_error与Java中的RuntimeException是等价的，而runtime_error与Java中非RuntimeException类型的异常是等价的。 <br />
<br />
3 异常的使用方法 <br />
<br />
<br />
3.1 声明方法抛出异常 <br />
<br />
3.1.1 语法：throws（略） <br />
<br />
3.1.2 为什么要声明方法抛出异常？ <br />
<br />
方法是否抛出异常与方法返回值的类型一样重要。假设方法抛出异常确没有声明该方法将抛出异常，那么客户程序员可以调用这个方法而且不用编写处理异常的代码。那么，一旦出现异常，那么这个异常就没有合适的异常控制器来解决。 <br />
<br />
3.1.3 为什么抛出的异常一定是已检查异常？ <br />
<br />
RuntimeException与Error可以在任何代码中产生，它们不需要由程序员显示的抛出，一旦出现错误，那么相应的异常会被自动抛出。而已检查异常是由程序员抛出的，这分为两种情况：客户程序员调用会抛出异常的库函数（库函数的异常由库程序员抛出）；客户程序员自己使用throw语句抛出异常。遇到Error，程序员一般是无能为力的；遇到RuntimeException，那么一定是程序存在逻辑错误，要对程序进行修改（相当于调试的一种方法）；只有已检查异常才是程序员所关心的，程序应该且仅应该抛出或处理已检查异常。 <br />
<br />
3.1.4 <br />
<br />
注意：覆盖父类某方法的子类方法不能抛出比父类方法更多的异常，所以，有时设计父类的方法时会声明抛出异常，但实际的实现方法的代码却并不抛出异常，这样做的目的就是为了方便子类方法覆盖父类方法时可以抛出异常。 <br />
<br />
3.2 如何抛出异常 <br />
<br />
3.2.1 语法：throw（略） <br />
<br />
3.2.2 抛出什么异常？ <br />
<br />
对于一个异常对象，真正有用的信息时异常的对象类型，而异常对象本身毫无意义。比如一个异常对象的类型是ClassCastException，那么这个类名就是唯一有用的信息。所以，在选择抛出什么异常时，最关键的就是选择异常的类名能够明确说明异常情况的类。 <br />
<br />
3.2.3 <br />
<br />
异常对象通常有两种构造函数：一种是无参数的构造函数；另一种是带一个字符串的构造函数，这个字符串将作为这个异常对象除了类型名以外的额外说明。 <br />
<br />
3.2.4 <br />
<br />
创建自己的异常：当Java内置的异常都不能明确的说明异常情况的时候，需要创建自己的异常。需要注意的是，唯一有用的就是类型名这个信息，所以不要在异常类的设计上花费精力。 <br />
<br />
3.3 捕获异常 <br />
<br />
如果一个异常没有被处理，那么，对于一个非图形界面的程序而言，该程序会被中止并输出异常信息；对于一个图形界面程序，也会输出异常的信息，但是程序并不中止，而是返回用Ы缑娲硌分小?BR&gt; 3.3.1 语法：try、catch和finally（略） <br />
<br />
控制器模块必须紧接在try块后面。若掷出一个异常，异常控制机制会搜寻参数与异常类型相符的第一个控制器随后它会进入那个catch <br />
<br />
从句，并认为异常已得到控制。一旦catch 从句结束对控制器的搜索也会停止。 <br />
<br />
3.3.1.1 捕获多个异常（注意语法与捕获的顺序）（略） <br />
<br />
3.3.1.2 finally的用法与异常处理流程（略） <br />
<br />
3.3.2 异常处理做什么？ <br />
<br />
对于Java来说，由于有了垃圾收集，所以异常处理并不需要回收内存。但是依然有一些资源需要程序员来收集，比如文件、网络连接和图片等资源。 <br />
<br />
3.3.3 应该声明方法抛出异常还是在方法中捕获异常？ <br />
<br />
原则：捕捉并处理哪些知道如何处理的异常，而传递哪些不知道如何处理的异常 <br />
<br />
3.3.4 再次抛出异常 <br />
<br />
3.3.4.1 为什么要再次抛出异常？ <br />
<br />
在本级中，只能处理一部分内容，有些处理需要在更高一级的环境中完成，所以应该再次抛出异常。这样可以使每级的异常处理器处理它能够处理的异常。 <br />
<br />
3.3.4.2 异常处理流程 <br />
<br />
对应与同一try块的catch块将被忽略，抛出的异常将进入更高的一级。 <br />
<br />
4 关于异常的其他问题 <br />
<br />
4.1 过度使用异常 <br />
<br />
首先，使用异常很方便，所以程序员一般不再愿意编写处理错误的代码，而仅仅是简简单单的抛出一个异常。这样做是不对的，对于完全已知的错误，应该编写处理这种错误的代码，增加程序的鲁棒性。另外，异常机制的效率很差。 <br />
<br />
4.2 将异常与普通错误区分开 <br />
<br />
对于普通的完全一致的错误，应该编写处理这种错误的代码，增加程序的鲁棒性。只有外部的不能确定和预知的运行时错误才需要使用异常。 <br />
<br />
4.3 异常对象中包含的信息 <br />
<br />
一般情况下，异常对象唯一有用的信息就是类型信息。但使用异常带字符串的构造函数时，这个字符串还可以作为额外的信息。调用异常对象的getMessage()、toString()或者printStackTrace()方法可以分别得到异常对象的额外信息、类名和调用堆栈的信息。并且后一种包含的信息是前一种的超集<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/146793.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-20 15:55 <a href="http://www.blogjava.net/wjun530/archive/2007/09/20/146793.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用JAVA语言来提取网站内部URL的算法</title><link>http://www.blogjava.net/wjun530/archive/2007/09/20/146792.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Thu, 20 Sep 2007 07:54:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/20/146792.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/146792.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/20/146792.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/146792.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/146792.html</trackback:ping><description><![CDATA[一、引言 <br />
<br />
20世纪末是万维网开始膨胀的时期，它的发展速度是十分惊人的。据统计，万维网已经连通了世界上几乎所有的国家，并且万维网正在急速发展。目前，万维网已经成为世界上最大的信息源，成为全球范围内科研、教育、商业和社会、新闻、学校和专业机构介绍、图书及娱乐等信息的集大成者。近年来对万维网的研究的两大热点是网络搜索引擎的研究和网络拓扑结构的研究。对于网络信息挖掘的首先要面对的问题就是如何提取出网站内部的URL，只有获得了网站的所有URL才可以网站的全部内容。基于此本文给出了一种实用有效的提取网站内部URL的方法。 <br />
<br />
二、JAVA基础 <br />
<br />
Java语言方便灵活，是现在开发网络程序的主要语言。本文采用了Java语言来开发。在Java的提供的java.util.*和 java.net.* 工具包中提供了大量简单易用的API。此外，在网络上还有大量的开放源码。 <br />
<br />
HTMLParser v 1.3是一套优秀的开放源代码的提供HTML文件分析的工具包。它可以提供快速的、实事的文本分析的功能。HTMLParser最大的成功之处是它设计简单、速度快、应用方便。可以从http://htmlparser.sourceforge.net下载此工具包和了解其用法。 <br />
<br />
对于HTMLParser 工具包我们需要修改其中的htmlparser.java文件使其适用中文的html文件分析。htmlparser.java文件中具体实现细节就不再介绍。这里主要是把protected static final String DEFAULT_CHARSET = "ISO-8859-1"；修改成protected static final String DEFAULT_CHARSET = "gb2312"；因为采用默认的字符集"ISO-8859-1"对含有中文的html文件进行分析的时候就会出现乱码。必须进行new String(str.getBytes("ISO-8859-1")，"GB2312")的转换工作。 <br />
<br />
对于修改好的htmlparser工具包，需要重新压缩成.jar文件，放到jdk的工作环境中。 <br />
<br />
三、具体设计 <br />
<br />
首先新建一个静态全局Vector变量L，在算法的递归调用中往L中不断添加新发现的URL。公式(1)是一个遍历网站内部URL的数学表达式。 <br />
<br />
S=T(ui) （1） <br />
<br />
s.t. ui O(ui) <br />
<br />
ui Ui-1 i=1，2&#8230;m <br />
<br />
其中T(u)是遍历一个站点的所有URL的函数，为了保证该遍历方法收敛这里我给出了两个限制条件，ui为一网站中的URL，O(ui)是判断ui是否为该网站内部的URL。 <br />
<br />
算法是： <br />
<br />
1） 定义全局静态变量public static Vector svecLink； <br />
<br />
2） 开始搜索网站W的主URL； <br />
<br />
3） 解析出第一页面的所有URL； <br />
<br />
4） 剔除无效的URL和非本站点的URL同时记录所获的URL的个数为a； <br />
<br />
5） 把URL添加到svecLink中； <br />
<br />
6） 从svecLink中取出最后a个URL分别递归调用此函数 <br />
<br />
为了能保证遍历的收敛，程序必须对URL进行严格限制。下面是一些具体实现： <br />
<br />
1） 定义的全局变量 <br />
<br />
public final int DEEP=3; //遍历的深度 <br />
<br />
public static Vector svecLink, svecOutlink; //存放内部URL和外部URL <br />
<br />
public static String hostName; //主机名称 <br />
<br />
public static boolean bl; //判断标志 <br />
<br />
private String location; <br />
<br />
private Parser parser; //对超文本进行分析 <br />
<br />
2） 获取主机名称GetHostName（）函数 <br />
<br />
通过该函数来判断所得URL是否是本网站的URL，如果不是就不需要添加svecLink中如果是并且以前没有提取过就添加到svecLink中。 <br />
<br />
<br />
public String GetHostName(String hostname)<br />
{<br />
URL aurl;<br />
String ss=" ";<br />
try<br />
{<br />
aurl=new URL(hostname);<br />
ss=aurl.getHost();<br />
}<br />
catch(MalformedURLException e)<br />
{<br />
e.printStackTrace();<br />
}<br />
return ss;<br />
}<br />
<br />
<br />
3） 递归遍历方法 <br />
<br />
由于网站中URL之间的连接构成了图，所以对图的遍历这里采用深度优先的方法。 <br />
<br />
<br />
public void extractLinks(String loc) throws ParserException {<br />
System.out.println("Parsing "+loc+" for links...");<br />
<br />
Vector vecTemp=new Vector();<br />
try {<br />
this.parser = new Parser(loc); //原理见HTMLParser<br />
parser.registerScanners(); <br />
bl=true;<br />
}<br />
catch (ParserException e) {<br />
bl=false; <br />
e.printStackTrace();<br />
}<br />
<br />
String ss,str1;<br />
URL wwwurl;<br />
boolean byes;<br />
<br />
int a=0;<br />
b++;<br />
Node [] links = parser.extractAllNodesThatAre(LinkTag.class); <br />
//获取一个页面中//所有的URL<br />
for (int i = 0;i &lt; links.length;i++) {<br />
if(bl)<br />
{<br />
byes=true;<br />
System.out.println("Total url is "+links.length+"This page has url "+i);<br />
LinkTag linkTag = (LinkTag)links[i];<br />
str1=linkTag.getLink(); <br />
if(str1.equals("")) continue;<br />
if(str1.charAt(str1.length()-1)==/<br />
||str1.charAt(str1.length()-1)==\) <br />
str1=str1.substring(0,str1.length()-1); <br />
if(!svecLink.contains(str1)) <br />
{ <br />
try<br />
{<br />
wwwurl=new URL(str1);<br />
wwwurl.getContent();<br />
}<br />
catch(MalformedURLException e)<br />
{<br />
byes=false;<br />
}<br />
catch(IOException e)<br />
{<br />
byes=false;<br />
}<br />
if(GetHostName(str1).equals(hostName) &amp;&amp; byes)<br />
{<br />
a++;<br />
tID++;<br />
svecLink.add(str1);<br />
vecTemp.add(str1);<br />
System.out.println("the url is "+str1);<br />
}<br />
else<br />
{<br />
<br />
svecOutlink.add(str1);<br />
}<br />
} <br />
} <br />
}<br />
<br />
String strNew;<br />
if(a&gt;0&amp;&amp;b&lt;=DEEP)<br />
{ <br />
<br />
for(int i=0;i<vectemp.size();i++)<br />
{<br />
strNew=(String)vecTemp.get(i);<br />
System.out.println("this is "+strNew);<br />
extractLinks(strNew); //递归调用<br />
}<br />
}<br />
<br />
}<br />
<br />
<br />
四、结论 <br />
<br />
本文介绍给出了一种提取网站内部URL的实现方法，本文的方法对网络机器人的研究和WEB文本挖掘提供了方便的工具。在程序的实现中还有许多实际的困难，需要在实验中不断完善和改进<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/146792.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-20 15:54 <a href="http://www.blogjava.net/wjun530/archive/2007/09/20/146792.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java初学者容易混淆的几个问题详细解析</title><link>http://www.blogjava.net/wjun530/archive/2007/09/18/146162.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Tue, 18 Sep 2007 06:52:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/18/146162.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/146162.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/18/146162.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/146162.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/146162.html</trackback:ping><description><![CDATA[１.String类和StringBuffer类 <br />
<br />
<br />
它们都是处理字符串的类,但是它们有一个最大的区别,那就是,String对象是存储你不能改动的文本字符串,相反,如果你希望改动,则应使用StringBuffer类作为替换. <br />
<br />
<br />
eg1: <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code <br />
<br />
　　 String s1="You are hired!"; <br />
<br />
　　 System.out.println(s1.replace(h,f));//用f把字串中的h替换了 <br />
<br />
　　 System.out.println(s1); <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code <br />
<br />
　　 运行结果: <br />
<br />
　　 You are fired! <br />
<br />
　　 You are hired! <br />
　　 结果分析: <br />
<br />
　　 从结果,明显可知,s1的值并没有被改变,而第一行结果只是屏幕内容的替换. <br />
<br />
　　 eg2: <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code <br />
<br />
　　 StringBuffer s2=new StringBuffer("Hello from Java!"); <br />
<br />
　　 s2.replace(6,10,"to"); <br />
<br />
　　 System.out.println(s2); <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code<br />
<br />
<br />
　　 <br />
<br />
运行结果: <br />
<br />
Hello to Java! <br />
<br />
结果分析: <br />
<br />
显然,s2的值已改变. <br />
<br />
<br />
２.位逻辑与条件逻辑 <br />
<br />
<br />
首先声明, 为了与位逻辑更好区分开来,我把通常所说的逻辑取了个别名叫做条件逻辑. <br />
<br />
<br />
它们都有各自的操作符,位逻辑操作符有:&amp;(与运算),^(异或运算),|(或运算);条件逻辑操作符有:&amp;&amp;(并且),||(或者). <br />
<br />
位逻辑运算通常是针对两个数而言,实行位操作;而条件逻辑运算是针对两个条件表达式而言,实行条件操作.其实,位逻辑操作符一样可以实现条件操作,但是此时有一个重要的区别:用位操作符时,不管操作符两边的 <br />
<br />
条件表达式成不成立,它都要通通进行运算判断,而条件逻辑操作符不一样了,如果通过左侧的操作数就可以进行它们需要的判断,那么它就不会再计算右侧的操作数了,这种情况叫短路.废话少说!且看下例. <br />
<br />
<br />
eg1: <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code <br />
<br />
　　 double value=0; <br />
<br />
　　 if(value!=0 &amp;&amp; 1/value&lt;1000){ <br />
<br />
　　 System.out.println("The value is not too small."); <br />
<br />
　　 } <br />
<br />
　　 else{ <br />
<br />
　　 System.out.println("The value is too small."); <br />
<br />
　　 } <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code<br />
<br />
<br />
　　 <br />
<br />
运行结果: <br />
<br />
The value is too small. <br />
<br />
结果分析: <br />
<br />
照理说应会出现除数为0的错误,但是我刚才说了,由于条件逻辑操作符是短路操作符,显然,value!=0不成立,立即就可作出判断应执行else后的语句,所以它就不再会运算判断1/value&lt;1000了.如果不懂请再看一例: <br />
<br />
<br />
eg2: <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code <br />
<br />
　　 double int1=0,int2=1,int3=1; <br />
<br />
　　 if(int1!=0 &amp; (int2=2)==1){} <br />
<br />
　　 System.out.println("int2="+int2); <br />
<br />
　　 if(int1!=0 &amp;&amp; (int3=2)==1){} <br />
<br />
　　 System.out.println("int3="+int3); <br />
<br />
　　 ...... <br />
<br />
　　 //omit some code<br />
<br />
<br />
运行结果: <br />
<br />
int2=2.0 <br />
<br />
int3=1.0 <br />
<br />
结果分析: <br />
<br />
我想不用我分析了,你应该懂了吧.<br />
<br />
<br />
<br />
<br />
３.实例变量与类变量 <br />
<br />
可以通过两种方法在类中存储数据───作为实例变量和类变量.实例变量是特定于对象的,如果你有两个对象(即一个类的两个实例),每一个对象中的实例变量独立于另一个对象中的实例变量的;另一方面,两个对象的类变量均指向相同的数据,并因此面保存相同的值,换句话说,类变量被类中的所有对象共享.差点忘了,它们在形式上的区别,类变量在声明时比实例变量多一个static. <br />
<br />
<br />
eg: <br />
<br />
　　 class data <br />
<br />
　　 public int intdata=0;//显然,intdata在这儿是实例变量 <br />
<br />
　　 } <br />
<br />
　　 public class exam <br />
<br />
　　 { <br />
<br />
　　 public static void main(String[] args) <br />
<br />
　　 { <br />
<br />
　　 data a,b; <br />
<br />
　　 a=new data(); <br />
<br />
　　 b=new data(); <br />
<br />
　　 a.intdata=1; <br />
<br />
　　 System.out.println("b.indata="+b.intdata); <br />
<br />
　　 } <br />
<br />
　　 }<br />
<br />
<br />
运行结果: <br />
<br />
b.intdata=0 <br />
<br />
结果分析: <br />
<br />
可以看出,a.intdata的值虽然变了,但并没有影响b.intdata.但是如果在data类中声明intdata时,在其前面加上static就变成类变量了(即:public static int intdata=0;),则此时运行结果会变为: <br />
<br />
b.intdata=1 <br />
<br />
这次a.intdata值的改变可把b.intdata影响了,事实上,对象a和b的类变量均指向相同的数据,所有值一样,这就是类变量的作用. <br />
<br />
<br />
４.实例方法,类方法,构造器方法 <br />
<br />
<br />
我们通常所说的方法系指实例方法,就像c语言中的函数一样,其具体方法我就不用说了,在这里我主要是用它来区分类方法和构造器方法.类方法与实例方法最大的区别是:在形式上类方法多一个static,在用法上,不必创建对象就可直接调用类方法(而实例方法却一定要先创建对象,再通过对象调用). <br />
<br />
<br />
eg: <br />
<br />
　　 class add <br />
<br />
　　 { <br />
<br />
　　 static int addem(int op1,int op2) <br />
<br />
　　 { <br />
　　 return op1+op2; <br />
<br />
　　 } <br />
<br />
　　 } <br />
<br />
　　 public class xxf <br />
<br />
　　 { <br />
<br />
　　 public static void main(String[] args) <br />
<br />
　　 { <br />
<br />
　　 System.out.println("addem(2,2)="+add.addem(2,2)); <br />
<br />
　　 } //直接用类名作为对象调用类方法 <br />
<br />
　　 }<br />
<br />
<br />
　　 <br />
<br />
注: 也可按通常的方法,即先创建对象,再调用方法,不过,这时static就无任何意义了. <br />
<br />
<br />
再说说构造器方法,它是用来初始化对象中的数据的一种方法,创建很容易,只需在类中加上一个与这个类同名的方法,不需要在前面加任何访问说明符或者返回类型,另外,构造器也一样可以向方法一样传递参数. <br />
<br />
<br />
eg: <br />
<br />
　 class data <br />
<br />
　　 { <br />
<br />
　　 private String data1;//事先声明 <br />
　　 data(String s) <br />
<br />
　　 { <br />
<br />
　　 data1=s; /*通过接收数据来初始化变量.(注:不能在构造器内 <br />
<br />
　　 声明变量,事先在外就要声明.)*/ <br />
<br />
　　 } <br />
<br />
　　 public String getdata() <br />
<br />
　　 { <br />
<br />
　　 return data1; <br />
<br />
　　 } <br />
<br />
　　 } <br />
<br />
　　 public class xxf <br />
<br />
　　 { <br />
<br />
　　 public static void main(String[] args) <br />
<br />
　　 { <br />
<br />
　　 System.out.println((new data("I love you")).getdata());<br />
/*通过传递参数调用构造器新建一个对象,再通过对象调用方法得到数据*/ <br />
<br />
　　 } <br />
<br />
　　 }<br />
<br />
<br />
５.接口与类 <br />
<br />
类是对一类特定对象的规格说明,我们可以类定义创建对象,通过创建对象来组合所有属于该类的组件,而接口不能这样做.而接口实质上就是一个常量和抽象方法的集合,要使用一个接口,就需要在类中实现这个接口,然后作为类定义的一部分,编写接口中声明的每一个方法,接口中的方法永远是public,abstract,接口中的常量永远是public static和final,因此不需要为它们说明属性.因为在Java中不支持多重继承,但是,可以用接口来实现类似的功能,这是接口的重要作用之一. <br />
<br />
<br />
eg: <br />
<br />
　　 interface anyone //定义一个接口 <br />
<br />
　　 { <br />
<br />
　　 final double PI=3.1416; <br />
<br />
　　 void setNumber(int number); <br />
<br />
　　 int getNumber(); <br />
<br />
　　 } <br />
<br />
　　 interface anyother //定义另一个接口 <br />
<br />
　　 { <br />
<br />
　　 void setString(String str); <br />
<br />
　　 String getString(); <br />
<br />
　　 } <br />
<br />
　　 class xxf implement anyone,anyother //定义一个类,并使用两个接口 <br />
<br />
　　 { <br />
<br />
　　 int number; <br />
<br />
　　 String str; <br />
<br />
　　 public xxf(){} <br />
<br />
　　 void setNumber(int number) <br />
<br />
　　 { <br />
<br />
　　 this.number=number; <br />
<br />
　　 } <br />
<br />
　　 void setString(String str) <br />
<br />
　　 { <br />
<br />
　　 this.str=str; <br />
<br />
　　 } <br />
<br />
　　 void int getNumber(){}//可以为一个空实现. <br />
<br />
　　 void String getString(){} <br />
<br />
　　 }<br />
<br />
<br />
//在类中必须实现接口中声明的所有方法.(当然也可不必,但是要用到适配器类或用抽象类)<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/146162.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-18 14:52 <a href="http://www.blogjava.net/wjun530/archive/2007/09/18/146162.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中使用接口实现多继承和多态的方法</title><link>http://www.blogjava.net/wjun530/archive/2007/09/18/146161.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Tue, 18 Sep 2007 06:50:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/18/146161.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/146161.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/18/146161.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/146161.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/146161.html</trackback:ping><description><![CDATA[1.JAVA里没有多继承，一个类之能有一个父类。 <br />
<br />
<br />
而继承的表现就是多态。一个父类可以有多个子类，而在子类里可以重写父类的方法（例如方法print()），这样每个子类里重写的代码不一样，自然表现形式就不一样。这样用父类的变量去引用不同的子类，在调用这个相同的方法print()的时候得到的结果和表现形式就不一样了，这就是多态，相同的消息（也就是调用相同的方法）会有不同的结果。举例说明： <br />
<br />
<br />
<br />
//父类<br />
public class Father{ <br />
//父类有一个打孩子方法 <br />
public void hitChild(){}<br />
}<br />
//子类1<br />
public class Son1 extends Father{ <br />
//重写父类打孩子方法 <br />
public void hitChild(){ <br />
System.out.println("为什么打我？我做错什么了！"); }<br />
}<br />
//子类2<br />
public class Son2 extends Father{ <br />
//重写父类打孩子方法 public void hitChild(){ <br />
System.out.println("我知道错了，别打了！"); }<br />
}<br />
//子类3<br />
public class Son3 extends Father{ <br />
//重写父类打孩子方法 public void hitChild(){ <br />
System.out.println("我跑，你打不着！"); }<br />
}<br />
//测试类<br />
public class Test{<br />
public static void main(String args[]){<br />
Father father;<br />
father = new Son1(); <br />
father.hitChild(); <br />
father = new Son2(); <br />
father.hitChild();<br />
father = new Son3(); <br />
father.hitChild();<br />
}<br />
}<br />
<br />
<br />
<br />
都调用了相同的方法，出现了不同的结果！这就是多态的表现！ <br />
<br />
<br />
2.JAVA中没有多继承，而用接口实现了多继承！一个类或是可以同时实现多个接口！（就相当于C++里一个类同时继承了多个类！）例如： <br />
<br />
<br />
<br />
public class Son implements Father1,Father2,Father3{ <br />
<br />
}<br />
<br />
<br />
<br />
接口有利于代码功能的扩展（增加新的功能）！而继承则有利于代码功能的修改（把旧的功能改成新的功能）！这里旧功能的修改和新功能的增加前提是不修改旧的功能，旧的功能仍然可以使用！旧类的代码也不做修改！ <br />
<br />
<br />
接着上面的继承举例：上面例子中的子类我们统称为一代子类 <br />
<br />
<br />
1.用继承修改旧的功能。 <br />
<br />
<br />
现在如果一代Son1的功能打孩子，也就是方法hitChild()里边的功能实现有问题，我们要把具体实现修改掉，那么我们可以重新写一个新的二代子类Son11继承自一代子类Son1，并重写其hitChild()方法。 <br />
<br />
<br />
<br />
//二代子类<br />
public class Son11 extends Son1{ <br />
//重写父类打孩子方法 <br />
public void hitChild(){ System.out.println("我是二代子类对此方法的新实现");<br />
}<br />
}<br />
<br />
<br />
<br />
2.用接口实现增加新的功能。 <br />
<br />
<br />
现在类Son1已经有了&#8216;打孩子&#8217;功能（也就是方法），而我们有了新的需求，我们需要它还要有一个新的功能&#8216;宠孩子&#8217;（新方法pet();）。而旧的代码不能动！那么我们可以定义一个新的接口（PetInterFace），接口里定义这个方法pet()的声明。再重新写一个二代子类Son11，让它先继承类Son1，同时实现接口 PetInterFace！并实现接口里的 pet()方法！ <br />
<br />
<br />
<br />
//新接口<br />
public interface PetInterFace{ <br />
//新功能方法 public void pet();<br />
}<br />
//二代子类<br />
public class Son11 extends Son1 implements PetInterFace{<br />
//实现接口方法 <br />
public void pet(){ <br />
System.out.println("父亲很爱我！"); <br />
}<br />
}<br />
<br />
<br />
<br />
这样这个二代子类即有了老的方法，又有新的功能！
<img src ="http://www.blogjava.net/wjun530/aggbug/146161.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-18 14:50 <a href="http://www.blogjava.net/wjun530/archive/2007/09/18/146161.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>彻底学习Java语言中的覆盖和重载的使用</title><link>http://www.blogjava.net/wjun530/archive/2007/09/18/146160.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Tue, 18 Sep 2007 06:49:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/18/146160.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/146160.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/18/146160.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/146160.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/146160.html</trackback:ping><description><![CDATA[override可以翻译为覆盖，从字面就可以知道，它是覆盖了一个方法并且对其重写，以求达到不同的作用。对我们来说最熟悉的覆盖就是对接口方法的实现，在接口中一般只是对方法进行了声明，而我们在实现时，就需要实现接口声明的所有方法。除了这个典型的用法以外，我们在继承中也可能会在子类覆盖父类中的方法。在覆盖要注意以下的几点： <br />
<br />
1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配，才能达到覆盖的效果； <br />
<br />
2、覆盖的方法的返回值必须和被覆盖的方法的返回一致； <br />
<br />
3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致，或者是其子类； <br />
<br />
4、被覆盖的方法不能为private，否则在其子类中只是新定义了一个方法，并没有对其进行覆盖。 <br />
<br />
overload对我们来说可能比较熟悉，可以翻译为重载，它是指我们可以定义一些名称相同的方法，通过定义不同的输入参数来区分这些方法，然后再调用时，VM就会根据不同的参数样式，来选择合适的方法执行。在使用重载要注意以下的几点： <br />
<br />
1、在使用重载时只能通过不同的参数样式。例如，不同的参数类型，不同的参数个数，不同的参数顺序（当然，同一方法内的几个参数类型必须不一样，例如可以是fun(int, float)， 但是不能为fun(int, int)）； <br />
<br />
2、不能通过访问权限、返回类型、抛出的异常进行重载； <br />
<br />
3、方法的异常类型和数目不会对重载造成影响； <br />
<br />
4、对于继承来说，如果某一方法在父类中是访问权限是priavte，那么就不能在子类对其进行重载，如果定义的话，也只是定义了一个新方法，而不会达到重载的效果。 <br />
<br />
下面是对override和overload的测试程序，其中注释中的内容都是会产生编译错误的代码，我们将注释去掉，看看在编译时会产生什么效果。 <br />
<br />
<br />
<br />
// 对overload测试的文件：OverloadTest.java<br />
public class OverloadTest {<br />
// 下面几个方法用来验证可以通过定义不同的参数类型和参数的数目进行方法重载。<br />
public void fun(){<br />
System.out.println("method fun in OverloadTest, no parameter");<br />
}<br />
<br />
public void fun(float f) {<br />
System.out.println("method fun in OverloadTest, parameter type: float");<br />
}<br />
<br />
public void fun(int i){<br />
System.out.println("method fun in OverloadTest, parameter type: int");<br />
}<br />
<br />
public void fun(int i1, int i2) {<br />
System.out.println("method fun in OverloadTest, parameter type: int, int");<br />
}<br />
<br />
// 下面的两个方法用来验证可以通过定义不同的参数顺序进行方法重载。<br />
// 需要注意：这里的参数肯定不是相同的类型，否则的顺序的先后就毫无意义。<br />
public void fun1(int i, float f) {<br />
System.out.println("method fun1 in OverloadTest,<br />
sequence of parameters is: int, float");<br />
}<br />
<br />
public void fun1(float f, int i) {<br />
System.out.println("method fun1 in OverloadTest, <br />
sequence of parameters is: float, int");<br />
}<br />
<br />
// 下面的两个方法用来验证方法抛出的异常对于重载的影响.<br />
// 无论是异常的类型还是异常的个数都不会对重载造成任何的影响。<br />
public void fun2() throws TestException {<br />
System.out.println("fun2 in OverloadTest, exception: TestException");<br />
}<br />
<br />
public void fun2(int i) throws TestException, TestException1 {<br />
System.out.println("fun2 in OverloadTest, <br />
exception: TestException, TestException1");<br />
}<br />
<br />
public void fun2(float f) throws Exception {<br />
System.out.println("fun2 in OverloadTest, exception: Exception");<br />
}<br />
<br />
// 不能通过抛出的异常类型来重载fun方法。<br />
//public void fun(int i) throws Exception {<br />
// System.out.println("method fun in OverloadTest, <br />
parameter type: int, exception: Exception");<br />
//}<br />
<br />
// 不能通过返回值重载fun方法。<br />
//public boolean fun(int i) throws Exception {<br />
// System.out.println("method fun in OverloadTest, <br />
parameter type: int, exception: Exception, return: boolean");<br />
// return true;<br />
//}<br />
<br />
private void fun3() { }<br />
<br />
// 不能通过不同的访问权限进行重载<br />
public void fun3() { }<br />
<br />
public static void main(String[] args) {<br />
// 这里只是定义了OverloadTest的实例，所以test不会调用<br />
// OverloadTest1中的方法。<br />
OverloadTest test = new OverloadTest1();<br />
// 这里定义了OverloadTest1的实例，因为OverloadTest1是OverloadTest<br />
// 的子类，所以test1会调用OverloadTest中的方法。<br />
OverloadTest1 test1 = new OverloadTest1();<br />
<br />
try {<br />
int i = 1, j = 2, m = 3;<br />
<br />
// 这里不会调用OverloadTest1的fun方法<br />
// test.fun(i, m, j);<br />
test1.fun(i, j, m);<br />
test1.fun();<br />
// 这个调用不会执行，因为fun3()在OverloadTest中访问权限是priavte <br />
//test1.fun3();<br />
test1.fun3(i);<br />
} catch(Exception e) { }<br />
}<br />
}<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/146160.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-18 14:49 <a href="http://www.blogjava.net/wjun530/archive/2007/09/18/146160.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Web三层架构的配置详解</title><link>http://www.blogjava.net/wjun530/archive/2007/09/18/146159.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Tue, 18 Sep 2007 06:48:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/18/146159.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/146159.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/18/146159.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/146159.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/146159.html</trackback:ping><description><![CDATA[一、软件下载:：<br />
<br />
　　1、java <br />
<br />
　　这里使用的是jdk1.4.2。 <br />
<br />
　　下载地址:http://dlc.sun.com/jdk/j2sdk-1_4_2_07-windows-i586-p.exe; <br />
<br />
　　2、tomcat <br />
<br />
　　这里的tomcat的版本是5.0的，安装版或是解压版都是可以的。 <br />
<br />
　　下载地址:http://apache.linuxforum.net/dis ... a-tomcat-5.0.28.exe <br />
<br />
　　3、数据库 <br />
<br />
　　推荐使用mysql，but暂时找不到下载地址，由于此次偶们班有课程设计要用sqlserver的，所以下面实践会使用sqlserver(找张安装盘安装) <br />
<br />
　　注:连接sqlserver需要三个jar文件(文件名ms开头)，可从这里下载: <br />
<br />
　　http://www.softhouse.com.cn/html ... 11250300001646.html <br />
<br />
　　上面包括完整的配置方法 <br />
<br />
　　二、软件安装 ：<br />
<br />
　　1.双击安装jdk。装完后在我的电脑-&gt;属性-&gt;高级-&gt;环境变量-&gt;系统变量中添加以下环境变量: <br />
<br />
　　(假设你的JDK安装在c:/jdk) <br />
<br />
　　JDK_HOME=C:jdk <br />
<br />
　　classpath=.;%JDK_HOME%libdt.jar;%JDK_HOME%libtools.jar <br />
<br />
　　在PATH(原来就已存在)中添加:%JDK_HOME%bin <br />
<br />
　　这样jdk环境配置成功。 <br />
<br />
　　2.双击安装你的Tomcat。(建议安装在D:/tomcat) <br />
<br />
　　注:tomcat5.0.x版本不需配置环境变量就可以使用，但如果需要编译Servlet则必须将tomcat的两个jar文件也放到环境变量中，具体方法如下: <br />
<br />
　　在我的电脑-&gt;属性-&gt;高级-&gt;环境变量-&gt;系统变量中添加: <br />
<br />
　　TOMCAT_HOME=D:tomcat <br />
<br />
　　修改classpath变成:.;%JDK_HOME%libdt.jar;%JDK_HOME%libtools.jar;%TOMCAT_HOME%commonlibservlet-api.jar;%TOMCAT_HOME%commonlibjsp-api.jar; <br />
<br />
　　这样已经完整安装完Tomcat了。 <br />
<br />
　　建议:在PATH中再添加:%JDK_HOME%bin;%TOMCAT_HOME%bin <br />
<br />
　　这样做的目的是在虚拟dos中可以使用tomcat的工具 <br />
<br />
　　最后顺便把第一步下载回来的三个jar文件放到tomcat目录下的common/lib目录中 <br />
<br />
　　3.测试: <br />
<br />
　　打开开始菜单中的tomcat(小猫图案)的monitor工具，点击start server，显示绿色三角形即为启动，打开浏览器在地址栏输入:http://localhost:8080/可以看到小猫图案说明已经配置成功。 <br />
<br />
　　4.安装数据库(sqlserver) <br />
<br />
　　windows xp下必须安装个人版或开发版(个人推荐个人版)。 <br />
<br />
　　一直next就可以(微软的东西就是方便)，这里注意到域帐户那里选择本机帐户，到验证那里选择混合验证(这点很重要，若选择系统认证以后在程序中就连接不上数据库)，为你的sa用户输入一个密码(如果练习用就不用考虑安全性啦，选择&#8220;空密码&#8221;就可以) <br />
<br />
　　三、正式编程：<br />
<br />
　　1.建立数据库 <br />
<br />
　　打开开始菜单找到sqlserver的快捷方式组里面的企业管理器，点开左边的树一般有个localhost连接，如果有个绿色三角形符号证明数据库连接成功。点击邮件新建数据库，在你新建的数据库中再新建个数据表(例如叫test)，数据表建立若干字段。 <br />
<br />
　　补充:提供另一种建表方法是使用sql脚本建表。首先创建如下文件: <br />
<br />
　　test.sql <br />
<br />
　　create database test <br />
<br />
　　USE test <br />
<br />
　　create table test <br />
<br />
　　( <br />
<br />
　　id int not null identity(1,1), <br />
<br />
　　mark varchar(255), <br />
<br />
　　name varchar(255), <br />
<br />
　　sex varchar(255) <br />
<br />
　　) <br />
<br />
　　然后打开开始菜单中sqlserver快捷方式组中的查询分析器，点击&#8220;文件&#8221;-》&#8220;打开&#8221;，选择test.sql，点击运行即可。 <br />
<br />
　　2.建立tomcat虚拟目录 <br />
<br />
　　在任意你喜欢的地方建立一个文件夹(例如e:/wool)，在文件夹中建立WEB-INF文件夹(注意必须大写)，WEB-INF文件夹中再建立classes和lib两个文件夹，将第一步下载的三个jar文件(连接数据库用)放入lib文件夹中。(有人可能问:这里为何没有web.xml这个文件，不急，后面会说到) <br />
<br />
　　3.配置文件与连接池 <br />
<br />
　　这次班里做课程设计这步难倒不少同学，我这里有最简单方法(俺老师建议去http://localhost:8080/admin中去配置，我个人觉得那样麻烦而且容易出错)，好，下面介绍步骤：<br />
<br />
　　首先，按下面内容建立好两个文件： <br />
<br />
<br />
<br />
<br />
wool.xml<br />
<br />
　　&lt;?xml version=1.0 encoding=utf-8?&gt;<br />
<br />
　　&lt;Context docBase="E:wool" path="/wool" reloadable="true" workDir="E:woolWEB-INFclasses"&gt;<br />
<br />
　　&lt;Resource name="jdbc/sqlserver" type="javax.sql.DataSource"/&gt;<br />
<br />
　　&lt;ResourceLink name="jdbc/sqlserver" global="jdbc/sqlserver" type="javax.sql.DataSource"/&gt;<br />
<br />
　　&lt;ResourceParams name="jdbc/sqlserver"&gt;<br />
<br />
　　&lt;parameter&gt;<br />
<br />
　　&lt;name&gt;driverClassName&lt;/name&gt;<br />
<br />
　　&lt;value&gt;com.microsoft.jdbc.sqlserver.SQLServerDriver&lt;/value&gt;<br />
<br />
　　&lt;/parameter&gt;<br />
<br />
　　&lt;parameter&gt;<br />
<br />
　　&lt;name&gt;url&lt;/name&gt;<br />
<br />
　　&lt;value&gt;jdbc:microsoft:sqlserver://localhost;DatabaseName=test&lt;/value&gt;<br />
&lt;!--这里test是第三点中第一小点建立的数据库名--&gt;<br />
<br />
　　&lt;/parameter&gt;<br />
<br />
　　&lt;parameter&gt;<br />
<br />
　　&lt;name&gt;username&lt;/name&gt;<br />
<br />
　　&lt;value&gt;sa&lt;/value&gt;<br />
<br />
　　&lt;/parameter&gt;<br />
<br />
　　&lt;parameter&gt;<br />
<br />
　　&lt;name&gt;password&lt;/name&gt;<br />
<br />
　　&lt;value&gt;&lt;/value&gt;&gt;&lt;!--安装sqlserver时用空密码的话这里什么都不用填（注意连空格都没有）--&gt;<br />
<br />
　　&lt;/parameter&gt;<br />
<br />
　　&lt;parameter&gt;<br />
<br />
　　&lt;name&gt;maxActive&lt;/name&gt;<br />
<br />
　　&lt;value&gt;4&lt;/value&gt;<br />
<br />
　　&lt;/parameter&gt;<br />
<br />
　　&lt;parameter&gt;<br />
<br />
　　&lt;name&gt;maxWait&lt;/name&gt;<br />
<br />
　　&lt;value&gt;5000&lt;/value&gt;<br />
<br />
　　&lt;/parameter&gt;<br />
<br />
　　&lt;parameter&gt;<br />
<br />
　　&lt;name&gt;maxIdle&lt;/name&gt;<br />
<br />
　　&lt;value&gt;2&lt;/value&gt;<br />
<br />
　　&lt;/parameter&gt;<br />
<br />
　　&lt;/ResourceParams&gt;<br />
<br />
　　&lt;/Context&gt;<br />
<br />
<br />
<br />
注意:在帮同学配置的过程发现一个问题，就是上面的配置文件必须把注释部分删除才可以成功运行。暂时不了解什么原因，理论上xml文件中这样的注释是允许的，郁闷。 <br />
<br />
　　web.xml <br />
<br />
　　connectDB <br />
<br />
　　jdbc/sqlserver <br />
<br />
　　javax.sql.DataSource <br />
<br />
　　Container <br />
<br />
　　建立完两个文件后，把wool.xml放到tomcat目录下的confCatalinalocalhost中;web.xml文件放到E:woolWEB-INF目录中。 <br />
<br />
　　4.写段程序测试连接 <br />
<br />
　　test.jsp <br />
<br />
　　名字　　性别　　分数 <br />
<br />
　　将test.jsp文件放到E:wool即可。 <br />
<br />
　　5.启动sqlserver和tomcat，在浏览器输入: <br />
<br />
　　http://localhost:8080/wool/test.jsp <br />
<br />
　　四、写在最后 <br />
<br />
　　以上方法是偶最近帮同学配置工程训练(web三层架构)的时候使用的方法，是偶以前参考网络上大虾们的经验后觉得最好的方法。通过以上步骤，对于未做过java或者其他基于web编程的同学来说可以迅速入门(其实最主要可以完成本次烦人的工程训练)。
<img src ="http://www.blogjava.net/wjun530/aggbug/146159.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-18 14:48 <a href="http://www.blogjava.net/wjun530/archive/2007/09/18/146159.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>教你如何成为一名合格的Java初级程序员</title><link>http://www.blogjava.net/wjun530/archive/2007/09/17/145681.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Mon, 17 Sep 2007 00:21:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/17/145681.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/145681.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/17/145681.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/145681.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/145681.html</trackback:ping><description><![CDATA[目前，JAVA是开发人员的热宠，很多论坛都有不少热爱JAVA的开发人员，也有不少想成为JAVA程序员，但苦于不知道该如何学习，也不清楚该学些什么知识才能成为一个JAVA程序员。本人在这里抛砖引玉，和大家讨论成为一个JAVA初级程序员应该具有的知识，与大家共享。 <br />
<br />
<br />
个人认为想成为一个合格的JAVA初级程序员应该具备如下知识： <br />
<br />
<br />
一、面向对象的知识：JAVA是一个面向对象的开发语言，因此熟悉面向对象对学习JAVA很有必要，您要了解：什么是对象，什么是类;什么是封装，什么是多态，什么是继承;什么是抽象类，什么是接口。了解了概念后，您还需要这些概念是如何体现的，如类和对象有什么区别?类是如何封装的 <br />
<br />
<br />
二、JAVA语法：如果您已经有了开发经验，恭喜您，您学习JAVA语法来将比较容易。如果您有C++等面向对象语言的开发经验，您只需简单的翻看一下介绍JAVA的相关书籍就可以了。如果您是新手，没有关系，您下些工夫，好好研究一本JAVA初级教程之类的书就可以了。 <br />
<br />
<br />
学习了JAVA语法，加上面向对象的知识，只有您用心，您就可以写出来比较好的JAVA代码了。如果您再抽出时间熟悉一下JAVA编程规范，您代码的水平就应该不俗了。 <br />
<br />
<br />
三、JSP和HTML：在我国的绝大多数公司，做JAVA程序员都少不了和JSP以及HTML打交道。因此，想成为JAVA程序员就不可避免的要熟悉JSP和HTML，您最好能知道JSP的几个内置对象，如Session，Request，Reponse，，以及常用的JSP标签，如 include,userBean等。尽管一些工具会帮您生成HTML代码，但您还是要熟悉比如title，，，，等。如果您再熟悉一下JS和CSS就更好了，那会使您制作的页面更友好。 <br />
<br />
<br />
四、WebServer:熟悉了以上三种，可以肯定的说您已经可以制作出来JSP页面了，您也可以在您的页面里使用自己开发的JAVA类 (JAVABEAN)了，但您的页面总要跑起来才能看到您要的效果，这就要求您必须熟悉一种WebServer，比如：TOMCAT，RESIN等。您要熟悉如何发布您的应用，如何利用WebServer的数据库资源等。 <br />
<br />
<br />
五、开发工具：大家都知道，开发工具可以帮助您更好更快地开发，因此熟悉几种开发工具很有必要。目前JAVA的开发工具比较流行的有JBuilder,IDEA,Eclipse，HTML的开发工具有Dreamweaver等。 <br />
<br />
<br />
六、熟悉一种框架：熟悉一种框架其实是成为JAVA程序员的一种可选知识，但目前开发B/S结构的应用的开发小组，都差不多会采用一种框架来构建自己的应用系统。框架都会有许多可重用的代码，良好的层次关系和业务控制逻辑，基于框架的开发使你可以省出很多的开发成本。目前比较流行的框架有 Struts和WAF等<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/145681.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-17 08:21 <a href="http://www.blogjava.net/wjun530/archive/2007/09/17/145681.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何才算真正的掌握Java语言（J2SE篇）</title><link>http://www.blogjava.net/wjun530/archive/2007/09/17/145678.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Mon, 17 Sep 2007 00:08:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/17/145678.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/145678.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/17/145678.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/145678.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/145678.html</trackback:ping><description><![CDATA[时常看到一些人说掌握了Java，但是让他们用Java做一个实际的项目可能又困难重重，在这里，笔者根据自己的一点理解斗胆提出自己的一些对掌握Java这个说法的标准，当然对于新手，也可以提供一个需要学习哪些内容的参考。另外这个标准仅限于J2SE部分，J2EE部分的内容有时间再另说。 <br />
<br />
1、语法：必须比较熟悉，在写代码的时候IDE的编辑器对某一行报错应该能够根据报错信息知道是什么样的语法错误并且知道任何修正。 <br />
<br />
2、命令：必须熟悉JDK带的一些常用命令及其常用选项，命令至少需要熟悉：appletviewer、HtmlConverter、jar、java、javac、javadoc、javap、javaw、native2ascii、serialver，如果这些命令你没有全部使用过，那么你对java实际上还很不了解。 <br />
<br />
3、工具：必须至少熟练使用一种IDE的开发工具，例如Eclipse、Netbeans、JBuilder、Jdeveloper、IDEA、JCreator或者Workshop，包括进行工程管理、常用选项的设置、插件的安装配置以及进行调试。 <br />
<br />
4、API：Java的核心API是非常庞大的，但是有一些内容笔者认为是必须熟悉的，否则不可能熟练的运用Java，包括： <br />
<br />
1）、java.lang包下的80％以上的类的功能的灵活运用。 <br />
<br />
2）、java.util包下的80％以上的类的灵活运用，特别是集合类体系、规则表达式、zip、以及时间、随机数、属性、资源和Timer。 <br />
<br />
3）、java.io包下的60％以上的类的使用，理解IO体系的基于管道模型的设计思路以及常用IO类的特性和使用场合。 <br />
<br />
4）、java.math包下的100％的内容。 <br />
<br />
5）、java.net包下的60％以上的内容，对各个类的功能比较熟悉。 <br />
<br />
6）、java.text包下的60％以上的内容，特别是各种格式化类。 <br />
<br />
7）、熟练运用JDBC。 <br />
<br />
8）、java.security包下40％以上的内容，如果对于安全没有接触的话根本就不可能掌握java。 <br />
<br />
9）、AWT的基本内容，包括各种组件事件、监听器、布局管理器、常用组件、打印。 <br />
<br />
10）、Swing的基本内容，和AWT的要求类似。 <br />
<br />
11）、XML处理，熟悉SAX、DOM以及JDOM的优缺点并且能够使用其中的一种完成XML的解析及内容处理。 <br />
<br />
5、测试：必须熟悉使用junit编写测试用例完成代码的自动测试。 <br />
<br />
6、管理：必须熟悉使用ant完成工程管理的常用任务，例如工程编译、生成javadoc、生成jar、版本控制、自动测试。 <br />
<br />
7、排错：应该可以根据异常信息比较快速的定位问题的原因和大致位置。 <br />
<br />
8、思想：必须掌握OOP的主要要求，这样使用Java开发的系统才能是真正的Java系统。 <br />
<br />
9、规范：编写的代码必须符合流行的编码规范，例如类名首字母大写，成员和方法名首字母小写，方法名的第一个单词一般是动词，包名全部小写等，这样程序的可读性才比较好<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/145678.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-17 08:08 <a href="http://www.blogjava.net/wjun530/archive/2007/09/17/145678.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中入门需要掌握的30个基本概念</title><link>http://www.blogjava.net/wjun530/archive/2007/09/17/145676.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Mon, 17 Sep 2007 00:01:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/17/145676.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/145676.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/17/145676.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/145676.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/145676.html</trackback:ping><description><![CDATA[1.OOP中唯一关系的是对象的接口是什么，就像计算机的销售商她不管电源内部结构是怎样的，他只关系能否给你提供电就行了，也就是只要知道can or not而不是how and why.所有的程序是由一定的属性和行为对象组成的，不同的对象的访问通过函数调用来完成，对象间所有的交流都是通过方法调用，通过对封装对象数据，很大限度上提高复用率。 <br />
<br />
<br />
2.OOP中最重要的思想是类，类是模板是蓝图，从类中构造一个对象，即创建了这个类的一个实例(instance)。 <br />
<br />
<br />
3.封装:就是把数据和行为结合起在一个包中)并对对象使用者隐藏数据的实现过程，一个对象中的数据叫他的实例字段(instance field)。 <br />
<br />
<br />
4.通过扩展一个类来获得一个新类叫继承(inheritance)，而所有的类都是由Object根超类扩展而得，根超类下文会做介绍。 <br />
<br />
<br />
5.对象的3个主要特性 <br />
<br />
<br />
behavior---说明这个对象能做什么. <br />
<br />
state---当对象施加方法时对象的反映. <br />
<br />
identity---与其他相似行为对象的区分标志. <br />
<br />
每个对象有唯一的indentity 而这3者之间相互影响. <br />
<br />
<br />
6.类之间的关系: <br />
<br />
<br />
use-a :依赖关系 <br />
<br />
has-a :聚合关系 <br />
<br />
is-a :继承关系--例:A类继承了B类，此时A类不仅有了B类的方法，还有其自己的方法.(个性存在于共性中) <br />
<br />
<br />
7.构造对象使用构造器:构造器的提出，构造器是一种特殊的方法，构造对象并对其初始化。 <br />
<br />
<br />
例:Data类的构造器叫Data <br />
<br />
<br />
new Data()---构造一个新对象，且初始化当前时间. <br />
<br />
Data happyday=new Data()---把一个对象赋值给一个变量happyday，从而使该对象能够多次使用，此处要声明的使变量与对象变量二者是不同的.new返回的值是一个引用。 <br />
<br />
<br />
构造器特点:构造器可以有0个，一个或多个参数 <br />
<br />
构造器和类有相同的名字 <br />
<br />
一个类可以有多个构造器 <br />
<br />
构造器没有返回值 <br />
<br />
构造器总是和new运算符一起使用. <br />
<br />
<br />
8.重载:当多个方法具有相同的名字而含有不同的参数时，便发生重载.编译器必须挑选出调用哪个方法。 <br />
<br />
<br />
9.包(package)Java允许把一个或多个类收集在一起成为一组，称作包，以便于组织任务，标准Java库分为许多包.java.lang java.util java，net等，包是分层次的所有的java包都在java和javax包层次内。 <br />
<br />
<br />
10.继承思想:允许在已经存在的类的基础上构建新的类，当你继承一个已经存在的类时，那么你就复用了这个类的方法和字段，同时你可以在新类中添加新的方法和字段。 <br />
<br />
<br />
11.扩展类:扩展类充分体现了is-a的继承关系. 形式为:class (子类) extends (基类)。 <br />
<br />
<br />
12.多态:在java中，对象变量是多态的.而java中不支持多重继承。 <br />
<br />
<br />
13.动态绑定:调用对象方法的机制。 <br />
<br />
<br />
(1)编译器检查对象声明的类型和方法名。 <br />
<br />
<br />
(2)编译器检查方法调用的参数类型。 <br />
<br />
<br />
(3)静态绑定:若方法类型为priavte static final 编译器会准确知道该调用哪个方法。 <br />
<br />
<br />
(4)当程序运行并且使用动态绑定来调用一个方法时，那么虚拟机必须调用x所指向的对象的实际类型相匹配的方法版本。 <br />
<br />
<br />
(5)动态绑定:是很重要的特性，它能使程序变得可扩展而不需要重编译已存代码。 <br />
<br />
<br />
14.final类:为防止他人从你的类上派生新类，此类是不可扩展的。 <br />
<br />
<br />
15.动态调用比静态调用花费的时间要长。 <br />
<br />
<br />
16.抽象类:规定一个或多个抽象方法的类本身必须定义为abstract。 <br />
<br />
<br />
例: public abstract string getDescripition <br />
<br />
<br />
17.Java中的每一个类都是从Object类扩展而来的。 <br />
<br />
<br />
18.object类中的equal和toString方法。 <br />
<br />
<br />
equal用于测试一个对象是否同另一个对象相等。 <br />
<br />
<br />
toString返回一个代表该对象的字符串，几乎每一个类都会重载该方法，以便返回当前状态的正确表示. <br />
<br />
(toString 方法是一个很重要的方法) <br />
<br />
<br />
19.通用编程:任何类类型的所有值都可以同object类性的变量来代替。 <br />
<br />
<br />
20.数组列表:ArrayList动态数组列表，是一个类库，定义在java.uitl包中，可自动调节数组的大小。 <br />
<br />
<br />
21.class类 object类中的getclass方法返回ckass类型的一个实例，程序启动时包含在main方法的类会被加载，虚拟机要加载他需要的所有类，每一个加载的类都要加载它需要的类。 <br />
<br />
<br />
22.class类为编写可动态操纵java代码的程序提供了强大的功能反射，这项功能为JavaBeans特别有用，使用反射Java能支持VB程序员习惯使用的工具。 <br />
<br />
<br />
能够分析类能力的程序叫反射器，Java中提供此功能的包叫Java.lang.reflect反射机制十分强大. <br />
<br />
<br />
1.在运行时分析类的能力。 <br />
<br />
2.在运行时探察类的对象。 <br />
<br />
3.实现通用数组操纵代码。 <br />
<br />
4.提供方法对象。 <br />
<br />
<br />
而此机制主要针对是工具者而不是应用及程序。 <br />
<br />
<br />
反射机制中的最重要的部分是允许你检查类的结构.用到的API有: <br />
<br />
<br />
java.lang.reflect.Field 返回字段. <br />
<br />
java.reflect.Method 返回方法. <br />
<br />
java.lang.reflect.Constructor 返回参数. <br />
<br />
<br />
方法指针:java没有方法指针，把一个方法的地址传给另一个方法，可以在后面调用它，而接口是更好的解决方案。 <br />
<br />
<br />
23.接口(Interface)说明类该做什么而不指定如何去做，一个类可以实现一个或多个interface。 <br />
<br />
<br />
24.接口不是一个类，而是对符合接口要求的类的一套规范。 <br />
<br />
<br />
若实现一个接口需要2个步骤:　 <br />
<br />
<br />
1.声明类需要实现的指定接口。 <br />
<br />
2.提供接口中的所有方法的定义。 <br />
<br />
<br />
声明一个类实现一个接口需要使用implements 关键字 <br />
<br />
<br />
class actionB implements Comparable 其actionb需要提供CompareTo方法，接口不是类，不能用new实例化一个接口. <br />
<br />
<br />
25.一个类只有一个超类，但一个类能实现多个接口。Java中的一个重要接口：Cloneable <br />
<br />
<br />
26.接口和回调.编程一个常用的模式是回调模式，在这种模式中你可以指定当一个特定时间发生时回调对象上的方法。 <br />
<br />
<br />
例:ActionListener 接口监听. <br />
<br />
类似的API有:java.swing.JOptionPane <br />
<br />
<br />
java.swing.Timer <br />
<br />
java.awt.Tookit <br />
<br />
<br />
27.对象clone:clone方法是object一个保护方法，这意味着你的代码不能简单的调用它。 <br />
<br />
<br />
28.内部类:一个内部类的定义是定义在另一个内部的类。 <br />
<br />
<br />
原因是: <br />
<br />
<br />
1.一个内部类的对象能够访问创建它的对象的实现，包括私有数据。 <br />
<br />
<br />
2.对于同一个包中的其他类来说，内部类能够隐藏起来。 <br />
<br />
<br />
3.匿名内部类可以很方便的定义回调。 <br />
<br />
<br />
4.使用内部类可以非常方便的编写事件驱动程序。 <br />
<br />
<br />
29.代理类(proxy): <br />
<br />
<br />
1.指定接口要求所有代码 <br />
<br />
<br />
2.object类定义的所有的方法(toString equals) <br />
<br />
<br />
30.数据类型:Java是强调类型的语言，每个变量都必须先申明它都类型，java中总共有8个基本类型.4种是整型，2种是浮点型，一种是字符型，被用于Unicode编码中的字符，布尔型<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/145676.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-17 08:01 <a href="http://www.blogjava.net/wjun530/archive/2007/09/17/145676.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中抽象类和接口的区别</title><link>http://www.blogjava.net/wjun530/archive/2007/09/17/145675.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Mon, 17 Sep 2007 00:00:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/17/145675.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/145675.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/17/145675.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/145675.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/145675.html</trackback:ping><description><![CDATA[Abstractclass和interface是Java语言中对于抽象类定义进行支持的两种机制，正是由于这两种机制的存在，才赋予了Java强大的面向对象能力。abstractclass和interface之间在对于抽象类定义的支持方面具有很大的相似性，甚至可以相互替换，因此很多开发者在进行抽象类定义时对于abstractclass和interface的选择显得比较随意。其实，两者之间还是有很大的区别的，对于它们的选择甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。本文将对它们之间的区别进行一番剖析，试图给开发者提供一个在二者之间进行选择的依据。 <br />
<br />
<br />
理解抽象类 <br />
<br />
<br />
abstractclass和interface在Java语言中都是用来进行抽象类（本文中的抽象类并非从abstractclass翻译而来，它表示的是一个抽象体，而abstractclass为Java语言中用于定义抽象类的一种方法，请读者注意区分）定义的，那么什么是抽象类，使用抽象类能为我们带来什么好处呢？ <br />
<br />
<br />
在面向对象的概念中，我们知道所有的对象都是通过类来描绘的，但是反过来却不是这样。并不是所有的类都是用来描绘对象的，如果一个类中没有包含足够的信息来描绘一个具体的对象，这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、设计中得出的抽象概念，是对一系列看上去不同，但是本质上相同的具体概念的抽象。比如：如果我们进行一个图形编辑软件的开发，就会发现问题领域存在着圆、三角形这样一些具体概念，它们是不同的，但是它们又都属于形状这样一个概念，形状这个概念在问题领域是不存在的，它就是一个抽象概念。正是因为抽象的概念在问题领域没有对应的具体概念，所以用以表征抽象概念的抽象类是不能够实例化的。 <br />
<br />
<br />
在面向对象领域，抽象类主要用来进行类型隐藏。我们可以构造出一个固定的一组行为的抽象描述，但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类，而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体，因此它可以是不允许修改的；同时，通过从这个抽象体派生，也可扩展此模块的行为功能。熟悉OCP的读者一定知道，为了能够实现面向对象设计的一个最核心的原则OCP(Open-ClosedPrinciple)，抽象类是其中的关键所在。 <br />
<br />
<br />
从语法定义层面看abstractclass和interface <br />
<br />
<br />
在语法层面，Java语言对于abstractclass和interface给出了不同的定义方式，下面以定义一个名为Demo的抽象类为例来说明这种不同。 <br />
<br />
<br />
使用abstractclass的方式定义Demo抽象类的方式如下： <br />
<br />
<br />
abstractclassDemo｛abstractvoidmethod1();abstractvoidmethod2();&#8230;｝ <br />
<br />
<br />
使用interface的方式定义Demo抽象类的方式如下： <br />
<br />
<br />
interfaceDemo{voidmethod1();voidmethod2();&#8230;} <br />
<br />
<br />
在abstractclass方式中，Demo可以有自己的数据成员，也可以有非abstarct的成员方法，而在interface方式的实现中，Demo只能够有静态的不能被修改的数据成员（也就是必须是staticfinal的，不过在interface中一般不定义数据成员），所有的成员方法都是abstract的。从某种意义上说，interface是一种特殊形式的abstractclass。 <br />
<br />
<br />
从编程的角度来看，abstractclass和interface都可以用来实现"designbycontract"的思想。但是在具体的使用上面还是有一些区别的。 <br />
<br />
<br />
首先，abstractclass在Java语言中表示的是一种继承关系，一个类只能使用一次继承关系。但是，一个类却可以实现多个interface。也许，这是Java语言的设计者在考虑Java对于多重继承的支持方面的一种折中考虑吧。 <br />
<br />
<br />
其次，在abstractclass的定义中，我们可以赋予方法的默认行为。但是在interface的定义中，方法却不能拥有默认行为，为了绕过这个限制，必须使用委托，但是这会增加一些复杂性，有时会造成很大的麻烦。 <br />
<br />
<br />
在抽象类中不能定义默认行为还存在另一个比较严重的问题，那就是可能会造成维护上的麻烦。因为如果后来想修改类的界面（一般通过abstractclass或者interface来表示）以适应新的情况（比如，添加新的方法或者给已用的方法中添加新的参数）时，就会非常的麻烦，可能要花费很多的时间（对于派生类很多的情况，尤为如此）。但是如果界面是通过abstractclass来实现的，那么可能就只需要修改定义在abstractclass中的默认行为就可以了。 <br />
<br />
<br />
同样，如果不能在抽象类中定义默认行为，就会导致同样的方法实现出现在该抽象类的每一个派生类中，违反了"onerule，oneplace"原则，造成代码重复，同样不利于以后的维护。因此，在abstractclass和interface间进行选择时要非常的小心。 <br />
<br />
<br />
从设计理念层面看abstractclass和interface <br />
<br />
<br />
上面主要从语法定义和编程的角度论述了abstractclass和interface的区别，这些层面的区别是比较低层次的、非本质的。本小节将从另一个层面：abstractclass和interface所反映出的设计理念，来分析一下二者的区别。作者认为，从这个层面进行分析才能理解二者概念的本质所在。 <br />
<br />
<br />
前面已经提到过，abstarctclass在Java语言中体现了一种继承关系，要想使得继承关系合理，父类和派生类之间必须存在"isa"关系，即父类和派生类在概念本质上应该是相同的（参考文献〔3〕中有关于"isa"关系的大篇幅深入的论述，有兴趣的读者可以参考）。对于interface来说则不然，并不要求interface的实现者和interface定义在概念本质上是一致的，仅仅是实现了interface定义的契约而已。为了使论述便于理解，下面将通过一个简单的实例进行说明。 <br />
<br />
<br />
考虑这样一个例子，假设在我们的问题领域中有一个关于Door的抽象概念，该Door具有执行两个动作open和close，此时我们可以通过abstractclass或者interface来定义一个表示该抽象概念的类型，定义方式分别如下所示： <br />
<br />
<br />
使用abstractclass方式定义Door： <br />
<br />
<br />
abstractclassDoor{abstractvoidopen();abstractvoidclose()；} <br />
<br />
<br />
使用interface方式定义Door： <br />
<br />
<br />
interfaceDoor{voidopen();voidclose();} <br />
<br />
<br />
其他具体的Door类型可以extends使用abstractclass方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstractclass和interface没有大的区别。 <br />
<br />
<br />
如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢（在本例中，主要是为了展示abstractclass和interface反映在设计理念上的区别，其他方面无关的问题都做了简化或者忽略）？下面将罗列出可能的解决方案，并从设计理念层面对这些不同的方案进行分析。 <br />
<br />
<br />
解决方案一： <br />
<br />
<br />
简单的在Door的定义中增加一个alarm方法，如下： <br />
<br />
<br />
abstractclassDoor{abstractvoidopen();abstractvoidclose()；abstractvoidalarm();} <br />
<br />
<br />
或者 <br />
<br />
<br />
interfaceDoor{voidopen();voidclose();voidalarm();} <br />
<br />
<br />
那么具有报警功能的AlarmDoor的定义方式如下： <br />
<br />
<br />
classAlarmDoorextendsDoor{voidopen(){&#8230;}voidclose(){&#8230;}voidalarm(){&#8230;}} <br />
<br />
<br />
或者 <br />
<br />
<br />
classAlarmDoorimplementsDoor｛voidopen(){&#8230;}voidclose(){&#8230;}voidalarm(){&#8230;}｝ <br />
<br />
<br />
这种方法违反了面向对象设计中的一个核心原则ISP（InterfaceSegregationPriciple），在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变（比如：修改alarm方法的参数）而改变，反之依然。 <br />
<br />
<br />
解决方案二： <br />
<br />
<br />
既然open、close和alarm属于两个不同的概念，根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有：这两个概念都使用abstractclass方式定义；两个概念都使用interface方式定义；一个概念使用abstractclass方式定义，另一个概念使用interface方式定义。 <br />
<br />
<br />
显然，由于Java语言不支持多重继承，所以两个概念都使用abstractclass方式定义是不可行的。后面两种方式都是可行的，但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。 <br />
<br />
<br />
如果两个概念都使用interface方式来定义，那么就反映出两个问题：1、我们可能没有理解清楚问题领域，AlarmDoor在概念本质上到底是Door还是报警器？2、如果我们对于问题领域的理解没有问题，比如：我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的，那么我们在实现时就没有能够正确的揭示我们的设计意图，因为在这两个概念的定义上（均使用interface方式定义）反映不出上述含义。 <br />
<br />
<br />
如果我们对于问题领域的理解是：AlarmDoor在概念本质上是Door，同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢？前面已经说过，abstractclass在Java语言中表示一种继承关系，而继承关系在本质上是"isa"关系。所以对于Door这个概念，我们应该使用abstarctclass方式来定义。另外，AlarmDoor又具有报警功能，说明它又能够完成报警概念中定义的行为，所以报警概念可以通过interface方式定义。如下所示： <br />
<br />
<br />
abstractclassDoor{abstractvoidopen();abstractvoidclose()；}interfaceAlarm{voidalarm();}classAlarmDoorextendsDoorimplementsAlarm{voidopen(){&#8230;}voidclose(){&#8230;}voidalarm(){&#8230;}} <br />
<br />
<br />
这种实现方式基本上能够明确的反映出我们对于问题领域的理解，正确的揭示我们的设计意图。其实abstractclass表示的是"isa"关系，interface表示的是"likea"关系，大家在选择时可以作为一个依据，当然这是建立在对问题领域的理解上的，比如：如果我们认为AlarmDoor在概念本质上是报警器，同时又具有Door的功能，那么上述的定义方式就要反过来了。
<img src ="http://www.blogjava.net/wjun530/aggbug/145675.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-17 08:00 <a href="http://www.blogjava.net/wjun530/archive/2007/09/17/145675.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正则表达式的简单应用实例</title><link>http://www.blogjava.net/wjun530/archive/2007/09/17/145674.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Sun, 16 Sep 2007 23:59:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/17/145674.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/145674.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/17/145674.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/145674.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/145674.html</trackback:ping><description><![CDATA[import java.io.*;<br />
import java.util.regex.*;<br />
<br />
public class Printer {<br />
public static void main(String[] args) {<br />
System.out.println("<br />
Please enter the input string:<br />
");<br />
BufferedReader reader = <br />
new BufferedReader(new InputStreamReader(System.in));<br />
String inputString;<br />
boolean isOK = false;<br />
try {<br />
while(!isOK) {<br />
if((inputString = reader.readLine()) != null) {<br />
if(inputString.length() &gt; 200) {<br />
System.out.println("The string <br />
exceeds 200 characters.<br />
Please enter again!<br />
");<br />
}<br />
else {<br />
Pattern regex = Pattern.compile("[^@#$%&amp;*/^]+");<br />
Matcher matcher = regex.matcher(inputString);<br />
boolean isMatched = matcher.matches();<br />
if(!isMatched) {<br />
System.out.println("The String cant <br />
contain @,#,$,%,*,&amp; and ^.<br />
Please enter again!<br />
");<br />
}<br />
else {<br />
isOK = true;<br />
System.out.println("<br />
Your input <br />
string is: <br />
" + inputString);<br />
} <br />
}<br />
}<br />
}<br />
}<br />
catch(IOException e) {<br />
e.printStackTrace();<br />
}<br />
}<br />
} 
<img src ="http://www.blogjava.net/wjun530/aggbug/145674.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-17 07:59 <a href="http://www.blogjava.net/wjun530/archive/2007/09/17/145674.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java IO学习基础之读写文本文件</title><link>http://www.blogjava.net/wjun530/archive/2007/09/15/145352.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Sat, 15 Sep 2007 03:39:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/15/145352.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/145352.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/15/145352.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/145352.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/145352.html</trackback:ping><description><![CDATA[Java的IO操作都是基于流进行操作的，为了提高读写效率一般需要进行缓冲。 <br />
<br />
简单的示例程序如下：<br />
<br />
/**<br />
* 读出1.txt中的内容，写入2.txt中<br />
*<br />
*/<br />
<br />
import java.io.*;<br />
<br />
public class ReadWriteFile{<br />
public static void main(String[] args){<br />
try{<br />
<br />
File read = new File("c:\1.txt");<br />
File write = new File("c:\2.txt");<br />
<br />
BufferedReader br = new BufferedReader(<br />
new FileReader(read));<br />
BufferedWriter bw = new BufferedWriter(<br />
new FileWriter(write));<br />
String temp = null;<br />
temp = br.readLine();<br />
while(temp != null){<br />
//写文件<br />
bw.write(temp + "<br />
"); //只适用Windows系统<br />
//继续读文件<br />
temp = br.readLine();<br />
}<br />
<br />
bw.close();<br />
br.close();<br />
<br />
}catch(FileNotFoundException e){ //文件未找到<br />
System.out.println (e);<br />
}catch(IOException e){<br />
System.out.println (e);<br />
} <br />
} <br />
} 
<img src ="http://www.blogjava.net/wjun530/aggbug/145352.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-15 11:39 <a href="http://www.blogjava.net/wjun530/archive/2007/09/15/145352.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中static、this、super、final用法</title><link>http://www.blogjava.net/wjun530/archive/2007/09/15/145350.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Sat, 15 Sep 2007 03:31:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/15/145350.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/145350.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/15/145350.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/145350.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/145350.html</trackback:ping><description><![CDATA[一、static <br />
<br />
　　请先看下面这段程序：<br />
<br />
<br />
public class Hello{<br />
public static void main(String[] args){ //(1)<br />
System.out.println("Hello,world!"); //(2)<br />
}<br />
}<br />
<br />
<br />
　　看过这段程序，对于大多数学过Java 的从来说，都不陌生。即使没有学过Java，而学过其它的高级语言，例如C，那你也应该能看懂这段代码的意思。它只是简单的输出&#8220;Hello,world&#8221;，一点别的用处都没有，然而，它却展示了static关键字的主要用法。 <br />
<br />
<br />
　　在1处，我们定义了一个静态的方法名为main，这就意味着告诉Java编译器，我这个方法不需要创建一个此类的对象即可使用。你还得你是怎么运行这个程序吗？一般，我们都是在命令行下，打入如下的命令(加下划线为手动输入)：<br />
<br />
javac Hello.java<br />
java Hello<br />
Hello,world!<br />
<br />
　　这就是你运行的过程，第一行用来编译Hello.java这个文件，执行完后，如果你查看当前，会发现多了一个Hello.class文件，那就是第一行产生的Java二进制字节码。第二行就是执行一个Java程序的最普遍做法。执行结果如你所料。在2中，你可能会想，为什么要这样才能输出。好，我们来分解一下这条语句。（如果没有安装Java文档，请到Sun的官方网站浏览J2SE API）首先，System是位于java.lang包中的一个核心类，如果你查看它的定义，你会发现有这样一行：public static final PrintStream out;接着在进一步，点击PrintStream这个超链接，在METHOD页面，你会看到大量定义的方法，查找println，会有这样一行：<br />
<br />
public void println(String x)。<br />
<br />
　　好了，现在你应该明白为什么我们要那样调用了，out是System的一个静态变量，所以可以直接使用，而out所属的类有一个println方法。<br />
<br />
静态方法<br />
<br />
　　通常，在一个类中定义一个方法为static，那就是说，无需本类的对象即可调用此方法。如下所示：<br />
<br />
<br />
class Simple{<br />
static void go(){<br />
System.out.println("Go...");<br />
}<br />
}<br />
public class Cal{<br />
public static void main(String[] args){<br />
Simple.go();<br />
}<br />
}<br />
<br />
<br />
　　调用一个静态方法就是&#8220;类名.方法名&#8221;,静态方法的使用很简单如上所示。一般来说，静态方法常常为应用程序中的其它类提供一些实用工具所用，在Java的类库中大量的静态方法正是出于此目的而定义的。 <br />
<br />
<br />
静态变量<br />
<br />
　　静态变量与静态方法类似。所有此类实例共享此静态变量，也就是说在类装载时，只分配一块存储空间，所有此类的对象都可以操控此块存储空间，当然对于final则另当别论了。看下面这段代码：<br />
<br />
<br />
class Value{<br />
static int c=0;<br />
static void inc(){<br />
c++;<br />
}<br />
}<br />
class Count{<br />
public static void prt(String s){<br />
System.out.println(s);<br />
}<br />
public static void main(String[] args){<br />
Value v1,v2;<br />
v1=new Value();<br />
v2=new Value();<br />
prt("v1.c="+v1.c+" v2.c="+v2.c);<br />
v1.inc();<br />
prt("v1.c="+v1.c+" v2.c="+v2.c); <br />
}<br />
}<br />
<br />
<br />
　　结果如下： <br />
<br />
<br />
v1.c=0 v2.c=0<br />
v1.c=1 v2.c=1<br />
<br />
　　由此可以证明它们共享一块存储区。static变量有点类似于C中的全局变量的概念。值得探讨的是静态变量的初始化问题。我们修改上面的程序：<br />
<br />
<br />
class Value{<br />
static int c=0;<br />
Value(){<br />
c=15;<br />
}<br />
Value(int i){<br />
c=i;<br />
}<br />
static void inc(){<br />
c++;<br />
}<br />
}<br />
class Count{<br />
public static void prt(String s){<br />
System.out.println(s);<br />
}<br />
Value v=new Value(10);<br />
static Value v1,v2;<br />
static{<br />
prt("v1.c="+v1.c+" v2.c="+v2.c);<br />
v1=new Value(27);<br />
prt("v1.c="+v1.c+" v2.c="+v2.c);<br />
v2=new Value(15);<br />
prt("v1.c="+v1.c+" v2.c="+v2.c);<br />
}<br />
public static void main(String[] args){<br />
Count ct=new Count();<br />
prt("ct.c="+ct.v.c);<br />
prt("v1.c="+v1.c+" v2.c="+v2.c);<br />
v1.inc();<br />
prt("v1.c="+v1.c+" v2.c="+v2.c);<br />
prt("ct.c="+ct.v.c);<br />
}<br />
}<br />
<br />
<br />
运行结果如下： <br />
<br />
<br />
v1.c=0 v2.c=0<br />
v1.c=27 v2.c=27<br />
v1.c=15 v2.c=15<br />
ct.c=10<br />
v1.c=10 v2.c=10<br />
v1.c=11 v2.c=11<br />
ct.c=11<br />
<br />
　　这个程序展示了静态初始化的各种特性。如果你初次接触Java，结果可能令你吃惊。可能会对static后加大括号感到困惑。首先要告诉你的是，static定义的变量会优先于任何其它非static变量，不论其出现的顺序如何。正如在程序中所表现的，虽然v出现在v1和v2的前面，但是结果却是v1和v2的初始化在v的前面。在static{后面跟着一段代码，这是用来进行显式的静态变量初始化，这段代码只会初始化一次，且在类被第一次装载时。如果你能读懂并理解这段代码，会帮助你对static关键字的认识。在涉及到继承的时候，会先初始化父类的static变量，然后是子类的，依次类推。非静态变量不是本文的主题，在此不做详细讨论，请参考Think in Java中的讲解。<br />
<br />
静态类<br />
<br />
　　通常一个普通类不允许声明为静态的，只有一个内部类才可以。这时这个声明为静态的内部类可以直接作为一个普通类来使用，而不需实例一个外部类。如下代码所示：<br />
<br />
<br />
public class StaticCls{<br />
public static void main(String[] args){<br />
OuterCls.InnerCls oi=new OuterCls.InnerCls();<br />
}<br />
}<br />
class OuterCls{<br />
public static class InnerCls{<br />
InnerCls(){<br />
System.out.println("InnerCls");<br />
}<br />
}<br />
}<br />
<br />
<br />
　　输出结果会如你所料： <br />
<br />
<br />
InnerCls<br />
<br />
　　和普通类一样。内部类的其它用法请参阅Think in Java中的相关章节，此处不作详解。<br />
<br />
二、this &amp; super<br />
<br />
　　在上一篇拙作中，我们讨论了static的种种用法，通过用static来定义方法或成员，为我们编程提供了某种便利，从某种程度上可以说它类似于C语言中的全局函数和全局变量。但是，并不是说有了这种便利，你便可以随处使用，如果那样的话，你便需要认真考虑一下自己是否在用面向对象的思想编程，自己的程序是否是面向对象的。好了，现在开始讨论this&amp;super这两个关键字的意义和用法。<br />
<br />
　　在Java中，this通常指当前对象，super则指父类的。当你想要引用当前对象的某种东西，比如当前对象的某个方法，或当前对象的某个成员，你便可以利用this来实现这个目的，当然，this的另一个用途是调用当前对象的另一个构造函数，这些马上就要讨论。如果你想引用父类的某种东西，则非super莫属。由于this与super有如此相似的一些特性和与生俱来的某种关系，所以我们在这一块儿来讨论，希望能帮助你区分和掌握它们两个。<br />
<br />
在一般方法中<br />
<br />
　　最普遍的情况就是，在你的方法中的某个形参名与当前对象的某个成员有相同的名字，这时为了不至于混淆，你便需要明确使用this关键字来指明你要使用某个成员，使用方法是&#8220;this.成员名&#8221;，而不带this的那个便是形参。另外，还可以用&#8220;this.方法名&#8221;来引用当前对象的某个方法，但这时this就不是必须的了，你可以直接用方法名来访问那个方法，编译器会知道你要调用的是那一个。下面的代码演示了上面的用法：<br />
<br />
<br />
public class DemoThis{<br />
private String name;<br />
private int age;<br />
DemoThis(String name,int age){<br />
setName(name); //你可以加上this来调用方法，像这样：this.setName(name);但这并不是必须的<br />
setAge(age);<br />
this.print();<br />
} <br />
public void setName(String name){<br />
this.name=name;//此处必须指明你要引用成员变量<br />
}<br />
public void setAge(int age){<br />
this.age=age;<br />
}<br />
public void print(){<br />
System.out.println("Name="+name+" Age="+age);//在此行中并不需要用this，因为没有会导致混淆的东西<br />
}<br />
public static void main(String[] args){<br />
DemoThis dt=new DemoThis("Kevin","22");<br />
}<br />
}<br />
<br />
<br />
　　这段代码很简单，不用解释你也应该能看明白。在构造函数中你看到用this.print(),你完全可以用print()来代替它，两者效果一样。下面我们修改这个程序，来演示super的用法。<br />
<br />
class Person{<br />
public int c;<br />
private String name;<br />
private int age;<br />
protected void setName(String name){<br />
this.name=name;<br />
}<br />
protected void setAge(int age){<br />
this.age=age;<br />
}<br />
protected void print(){<br />
System.out.println("Name="+name+" Age="+age);<br />
}<br />
}<br />
public class DemoSuper extends Person{<br />
public void print(){<br />
System.out.println("DemoSuper:");<br />
super.print();<br />
}<br />
public static void main(String[] args){<br />
DemoSuper ds=new DemoSuper();<br />
ds.setName("kevin");<br />
ds.setAge(22);<br />
ds.print();<br />
}<br />
}<br />
<br />
<br />
　　在DemoSuper中，重新定义的print方法覆写了父类的print方法，它首先做一些自己的事情，然后调用父类的那个被覆写了的方法。输出结果说明了这一点： <br />
<br />
<br />
DemoSuper:<br />
Name=kevin Age=22<br />
<br />
　　这样的使用方法是比较常用的。另外如果父类的成员可以被子类访问，那你可以像使用this一样使用它，用&#8220;super.父类中的成员名&#8221;的方式，但常常你并不是这样来访问父类中的成员名的。<br />
<br />
在构造函数中<br />
<br />
　　构造函数是一种特殊的方法，在对象初始化的时候自动调用。在构造函数中，this和super也有上面说的种种使用方式，并且它还有特殊的地方，请看下面的例子：<br />
<br />
<br />
class Person{<br />
public static void prt(String s){<br />
System.out.println(s);<br />
}<br />
Person(){<br />
prt("A Person.");<br />
}<br />
Person(String name){<br />
prt("A person name is:"+name);<br />
}<br />
}<br />
public class Chinese extends Person{<br />
Chinese(){<br />
super(); //调用父类构造函数（1）<br />
prt("A chinese.");//(4)<br />
}<br />
Chinese(String name){<br />
super(name);//调用父类具有相同形参的构造函数（2）<br />
prt("his name is:"+name);<br />
}<br />
Chinese(String name,int age){<br />
this(name);//调用当前具有相同形参的构造函数（3）<br />
prt("his age is:"+age);<br />
}<br />
public static void main(String[] args){<br />
Chinese cn=new Chinese();<br />
cn=new Chinese("kevin");<br />
cn=new Chinese("kevin",22);<br />
}<br />
}<br />
<br />
<br />
　　在这段程序中，this和super不再是像以前那样用&#8220;.&#8221;连接一个方法或成员，而是直接在其后跟上适当的参数，因此它的意义也就有了变化。super后加参数的是用来调用父类中具有相同形式的构造函数，如1和2处。this后加参数则调用的是当前具有相同参数的构造函数，如3处。当然，在Chinese的各个重载构造函数中，this和super在一般方法中的各种用法也仍可使用，比如4处，你可以将它替换为&#8220;this.prt&#8221;(因为它继承了父类中的那个方法）或者是&#8220;super.prt&#8221;（因为它是父类中的方法且可被子类访问），它照样可以正确运行。但这样似乎就有点画蛇添足的味道了。 <br />
<br />
<br />
　　最后，写了这么多，如果你能对&#8220;this通常指代当前对象，super通常指代父类&#8221;这句话牢记在心，那么本篇便达到了目的，其它的你自会在以后的编程实践当中慢慢体会、掌握。另外关于本篇中提到的继承，请参阅相关Java教程。<br />
<br />
三、final<br />
<br />
　　final在Java中并不常用，然而它却为我们提供了诸如在C语言中定义常量的功能，不仅如此，final还可以让你控制你的成员、方法或者是一个类是否可被覆写或继承等功能，这些特点使final在Java中拥有了一个不可或缺的地位，也是学习Java时必须要知道和掌握的关键字之一。<br />
<br />
final成员<br />
<br />
　　当你在类中定义变量时，在其前面加上final关键字，那便是说，这个变量一旦被初始化便不可改变，这里不可改变的意思对基本类型来说是其值不可变，而对于对象变量来说其引用不可再变。其初始化可以在两个地方，一是其定义处，也就是说在final变量定义时直接给其赋值，二是在构造函数中。这两个地方只能选其一，要么在定义时给值，要么在构造函数中给值，不能同时既在定义时给了值，又在构造函数中给另外的值。下面这段代码演示了这一点：<br />
<br />
<br />
import java.util.List;<br />
import java.util.ArrayList;<br />
import java.util.LinkedList;<br />
public class Bat{<br />
final PI=3.14; //在定义时便给址值<br />
final int i; //因为要在构造函数中进行初始化，所以此处便不可再给值<br />
final List list; //此变量也与上面的一样<br />
Bat(){<br />
i=100;<br />
list=new LinkedList();<br />
}<br />
Bat(int ii,List l){<br />
i=ii;<br />
list=l;<br />
}<br />
public static void main(String[] args){<br />
Bat b=new Bat();<br />
b.list.add(new Bat());<br />
//b.i=25;<br />
//b.list=new ArrayList();<br />
System.out.println("I="+b.i+" List Type:"+b.list.getClass());<br />
b=new Bat(23,new ArrayList());<br />
b.list.add(new Bat());<br />
System.out.println("I="+b.i+" List Type:"+b.list.getClass());<br />
}<br />
}<br />
<br />
<br />
　　此程序很简单的演示了final的常规用法。在这里使用在构造函数中进行初始化的方法，这使你有了一点灵活性。如Bat的两个重载构造函数所示，第一个缺省构造函数会为你提供默认的值，重载的那个构造函数会根据你所提供的值或类型为final变量初始化。然而有时你并不需要这种灵活性，你只需要在定义时便给定其值并永不变化，这时就不要再用这种方法。在main方法中有两行语句注释掉了，如果你去掉注释，程序便无法通过编译，这便是说，不论是i的值或是list的类型，一旦初始化，确实无法再更改。然而b可以通过重新初始化来指定i的值或list的类型，输出结果中显示了这一点： <br />
<br />
<br />
I=100 List Type:class java.util.LinkedList<br />
I=23 List Type:class java.util.ArrayList<br />
<br />
　　还有一种用法是定义方法中的参数为final，对于基本类型的变量，这样做并没有什么实际意义，因为基本类型的变量在调用方法时是传值的，也就是说你可以在方法中更改这个参数变量而不会影响到调用语句，然而对于对象变量，却显得很实用，因为对象变量在传递时是传递其引用，这样你在方法中对对象变量的修改也会影响到调用语句中的对象变量，当你在方法中不需要改变作为参数的对象变量时，明确使用final进行声明，会防止你无意的修改而影响到调用方法。<br />
另外方法中的内部类在用到方法中的参变量时，此参变也必须声明为final才可使用，如下代码所示：<br />
<br />
<br />
public class INClass{<br />
void innerClass(final String str){<br />
class IClass{<br />
IClass(){<br />
System.out.println(str);<br />
}<br />
}<br />
IClass ic=new IClass();<br />
}<br />
public static void main(String[] args){<br />
INClass inc=new INClass();<br />
inc.innerClass("Hello");<br />
}<br />
}<br />
<br />
<br />
final方法 <br />
<br />
<br />
　　将方法声明为final，那就说明你已经知道这个方法提供的功能已经满足你要求，不需要进行扩展，并且也不允许任何从此类继承的类来覆写这个方法，但是继承仍然可以继承这个方法，也就是说可以直接使用。另外有一种被称为inline的机制，它会使你在调用final方法时，直接将方法主体插入到调用处，而不是进行例行的方法调用，例如保存断点，压栈等，这样可能会使你的程序效率有所提高，然而当你的方法主体非常庞大时，或你在多处调用此方法，那么你的调用主体代码便会迅速膨胀，可能反而会影响效率，所以你要慎用final进行方法定义。<br />
<br />
final类<br />
<br />
　　当你将final用于类身上时，你就需要仔细考虑，因为一个final类是无法被任何人继承的，那也就意味着此类在一个继承树中是一个叶子类，并且此类的设计已被认为很完美而不需要进行修改或扩展。对于final类中的成员，你可以定义其为final，也可以不是final。而对于方法，由于所属类为final的关系，自然也就成了final型的。你也可以明确的给final类中的方法加上一个final，但这显然没有意义。<br />
<br />
　　下面的程序演示了final方法和final类的用法：<br />
<br />
<br />
final class final{<br />
final String str="final Data";<br />
public String str1="non final data";<br />
final public void print(){<br />
System.out.println("final method.");<br />
}<br />
public void what(){<br />
System.out.println(str+"<br />
"+str1);<br />
}<br />
}<br />
public class FinalDemo { //extends final 无法继承 <br />
public static void main(String[] args){<br />
final f=new final();<br />
f.what();<br />
f.print();<br />
}<br />
}<br />
<br />
<br />
　　从程序中可以看出，final类与普通类的使用几乎没有差别，只是它失去了被继承的特性。final方法与非final方法的区别也很难从程序行看出，只是记住慎用。 <br />
<br />
<br />
final在设计模式中的应用<br />
<br />
　　在设计模式中有一种模式叫做不变模式，在Java中通过final关键字可以很容易的实现这个模式，在讲解final成员时用到的程序Bat.java就是一个不变模式的例子。如果你对此感兴趣，可以参考阎宏博士编写的《Java与模式》一书中的讲解。<br />
<br />
　　到此为止，this,static,supert和final的使用已经说完了，如果你对这四个关键字已经能够大致说出它们的区别与用法，那便说明你基本已经掌握。然而，世界上的任何东西都不是完美无缺的，Java提供这四个关键字，给程序员的编程带来了很大的便利，但并不是说要让你到处使用，一旦达到滥用的程序，便适得其反，所以在使用时请一定要认真考虑。<br />
<br />
<br />
　　请先看下面这段程序：<br />
<br />
<br />
public class Hello{<br />
public static void main(String[] args){ //(1)<br />
System.out.println("Hello,world!"); //(2)<br />
}<br />
}<br />
<br />
<br />
　　看过这段程序，对于大多数学过Java 的从来说，都不陌生。即使没有学过Java，而学过其它的高级语言，例如C，那你也应该能看懂这段代码的意思。它只是简单的输出&#8220;Hello,world&#8221;，一点别的用处都没有，然而，它却展示了static关键字的主要用法。 <br />
<br />
<br />
　　在1处，我们定义了一个静态的方法名为main，这就意味着告诉Java编译器，我这个方法不需要创建一个此类的对象即可使用。你还得你是怎么运行这个程序吗？一般，我们都是在命令行下，打入如下的命令(加下划线为手动输入)：<br />
<br />
javac Hello.java<br />
java Hello<br />
Hello,world!<br />
<br />
　　这就是你运行的过程，第一行用来编译Hello.java这个文件，执行完后，如果你查看当前，会发现多了一个Hello.class文件，那就是第一行产生的Java二进制字节码。第二行就是执行一个Java程序的最普遍做法。执行结果如你所料。在2中，你可能会想，为什么要这样才能输出。好，我们来分解一下这条语句。（如果没有安装Java文档，请到Sun的官方网站浏览J2SE API）首先，System是位于java.lang包中的一个核心类，如果你查看它的定义，你会发现有这样一行：public static final PrintStream out;接着在进一步，点击PrintStream这个超链接，在METHOD页面，你会看到大量定义的方法，查找println，会有这样一行：<br />
<br />
public void println(String x)。<br />
<br />
　　好了，现在你应该明白为什么我们要那样调用了，out是System的一个静态变量，所以可以直接使用，而out所属的类有一个println方法。<br />
<br />
静态方法<br />
<br />
　　通常，在一个类中定义一个方法为static，那就是说，无需本类的对象即可调用此方法。如下所示：<br />
<br />
<br />
class Simple{<br />
static void go(){<br />
System.out.println("Go...");<br />
}<br />
}<br />
public class Cal{<br />
public static void main(String[] args){<br />
Simple.go();<br />
}<br />
}<br />
<br />
<br />
　　调用一个静态方法就是&#8220;类名.方法名&#8221;,静态方法的使用很简单如上所示。一般来说，静态方法常常为应用程序中的其它类提供一些实用工具所用，在Java的类库中大量的静态方法正是出于此目的而定义的。 <br />
<br />
<br />
静态变量<br />
<br />
　　静态变量与静态方法类似。所有此类实例共享此静态变量，也就是说在类装载时，只分配一块存储空间，所有此类的对象都可以操控此块存储空间，当然对于final则另当别论了。看下面这段代码：<br />
<br />
<br />
class Value{<br />
static int c=0;<br />
static void inc(){<br />
c++;<br />
}<br />
}<br />
class Count{<br />
public static void prt(String s){<br />
System.out.println(s);<br />
}<br />
public static void main(String[] args){<br />
Value v1,v2;<br />
v1=new Value();<br />
v2=new Value();<br />
prt("v1.c="+v1.c+" v2.c="+v2.c);<br />
v1.inc();<br />
prt("v1.c="+v1.c+" v2.c="+v2.c); <br />
}<br />
}<br />
<br />
<br />
　　结果如下： <br />
<br />
<br />
v1.c=0 v2.c=0<br />
v1.c=1 v2.c=1<br />
<br />
　　由此可以证明它们共享一块存储区。static变量有点类似于C中的全局变量的概念。值得探讨的是静态变量的初始化问题。我们修改上面的程序：<br />
<br />
<br />
class Value{<br />
static int c=0;<br />
Value(){<br />
c=15;<br />
}<br />
Value(int i){<br />
c=i;<br />
}<br />
static void inc(){<br />
c++;<br />
}<br />
}<br />
class Count{<br />
public static void prt(String s){<br />
System.out.println(s);<br />
}<br />
Value v=new Value(10);<br />
static Value v1,v2;<br />
static{<br />
prt("v1.c="+v1.c+" v2.c="+v2.c);<br />
v1=new Value(27);<br />
prt("v1.c="+v1.c+" v2.c="+v2.c);<br />
v2=new Value(15);<br />
prt("v1.c="+v1.c+" v2.c="+v2.c);<br />
}<br />
public static void main(String[] args){<br />
Count ct=new Count();<br />
prt("ct.c="+ct.v.c);<br />
prt("v1.c="+v1.c+" v2.c="+v2.c);<br />
v1.inc();<br />
prt("v1.c="+v1.c+" v2.c="+v2.c);<br />
prt("ct.c="+ct.v.c);<br />
}<br />
}<br />
<br />
<br />
运行结果如下： <br />
<br />
<br />
v1.c=0 v2.c=0<br />
v1.c=27 v2.c=27<br />
v1.c=15 v2.c=15<br />
ct.c=10<br />
v1.c=10 v2.c=10<br />
v1.c=11 v2.c=11<br />
ct.c=11<br />
<br />
　　这个程序展示了静态初始化的各种特性。如果你初次接触Java，结果可能令你吃惊。可能会对static后加大括号感到困惑。首先要告诉你的是，static定义的变量会优先于任何其它非static变量，不论其出现的顺序如何。正如在程序中所表现的，虽然v出现在v1和v2的前面，但是结果却是v1和v2的初始化在v的前面。在static{后面跟着一段代码，这是用来进行显式的静态变量初始化，这段代码只会初始化一次，且在类被第一次装载时。如果你能读懂并理解这段代码，会帮助你对static关键字的认识。在涉及到继承的时候，会先初始化父类的static变量，然后是子类的，依次类推。非静态变量不是本文的主题，在此不做详细讨论，请参考Think in Java中的讲解。<br />
<br />
静态类<br />
<br />
　　通常一个普通类不允许声明为静态的，只有一个内部类才可以。这时这个声明为静态的内部类可以直接作为一个普通类来使用，而不需实例一个外部类。如下代码所示：<br />
<br />
<br />
public class StaticCls{<br />
public static void main(String[] args){<br />
OuterCls.InnerCls oi=new OuterCls.InnerCls();<br />
}<br />
}<br />
class OuterCls{<br />
public static class InnerCls{<br />
InnerCls(){<br />
System.out.println("InnerCls");<br />
}<br />
}<br />
}<br />
<br />
<br />
　　输出结果会如你所料： <br />
<br />
<br />
InnerCls<br />
<br />
　　和普通类一样。内部类的其它用法请参阅Think in Java中的相关章节，此处不作详解。<br />
<br />
二、this &amp; super<br />
<br />
　　在上一篇拙作中，我们讨论了static的种种用法，通过用static来定义方法或成员，为我们编程提供了某种便利，从某种程度上可以说它类似于C语言中的全局函数和全局变量。但是，并不是说有了这种便利，你便可以随处使用，如果那样的话，你便需要认真考虑一下自己是否在用面向对象的思想编程，自己的程序是否是面向对象的。好了，现在开始讨论this&amp;super这两个关键字的意义和用法。<br />
<br />
　　在Java中，this通常指当前对象，super则指父类的。当你想要引用当前对象的某种东西，比如当前对象的某个方法，或当前对象的某个成员，你便可以利用this来实现这个目的，当然，this的另一个用途是调用当前对象的另一个构造函数，这些马上就要讨论。如果你想引用父类的某种东西，则非super莫属。由于this与super有如此相似的一些特性和与生俱来的某种关系，所以我们在这一块儿来讨论，希望能帮助你区分和掌握它们两个。<br />
<br />
在一般方法中<br />
<br />
　　最普遍的情况就是，在你的方法中的某个形参名与当前对象的某个成员有相同的名字，这时为了不至于混淆，你便需要明确使用this关键字来指明你要使用某个成员，使用方法是&#8220;this.成员名&#8221;，而不带this的那个便是形参。另外，还可以用&#8220;this.方法名&#8221;来引用当前对象的某个方法，但这时this就不是必须的了，你可以直接用方法名来访问那个方法，编译器会知道你要调用的是那一个。下面的代码演示了上面的用法：<br />
<br />
<br />
public class DemoThis{<br />
private String name;<br />
private int age;<br />
DemoThis(String name,int age){<br />
setName(name); //你可以加上this来调用方法，像这样：this.setName(name);但这并不是必须的<br />
setAge(age);<br />
this.print();<br />
} <br />
public void setName(String name){<br />
this.name=name;//此处必须指明你要引用成员变量<br />
}<br />
public void setAge(int age){<br />
this.age=age;<br />
}<br />
public void print(){<br />
System.out.println("Name="+name+" Age="+age);//在此行中并不需要用this，因为没有会导致混淆的东西<br />
}<br />
public static void main(String[] args){<br />
DemoThis dt=new DemoThis("Kevin","22");<br />
}<br />
}<br />
<br />
<br />
　　这段代码很简单，不用解释你也应该能看明白。在构造函数中你看到用this.print(),你完全可以用print()来代替它，两者效果一样。下面我们修改这个程序，来演示super的用法。<br />
<br />
class Person{<br />
public int c;<br />
private String name;<br />
private int age;<br />
protected void setName(String name){<br />
this.name=name;<br />
}<br />
protected void setAge(int age){<br />
this.age=age;<br />
}<br />
protected void print(){<br />
System.out.println("Name="+name+" Age="+age);<br />
}<br />
}<br />
public class DemoSuper extends Person{<br />
public void print(){<br />
System.out.println("DemoSuper:");<br />
super.print();<br />
}<br />
public static void main(String[] args){<br />
DemoSuper ds=new DemoSuper();<br />
ds.setName("kevin");<br />
ds.setAge(22);<br />
ds.print();<br />
}<br />
}<br />
<br />
<br />
　　在DemoSuper中，重新定义的print方法覆写了父类的print方法，它首先做一些自己的事情，然后调用父类的那个被覆写了的方法。输出结果说明了这一点： <br />
<br />
<br />
DemoSuper:<br />
Name=kevin Age=22<br />
<br />
　　这样的使用方法是比较常用的。另外如果父类的成员可以被子类访问，那你可以像使用this一样使用它，用&#8220;super.父类中的成员名&#8221;的方式，但常常你并不是这样来访问父类中的成员名的。<br />
<br />
在构造函数中<br />
<br />
　　构造函数是一种特殊的方法，在对象初始化的时候自动调用。在构造函数中，this和super也有上面说的种种使用方式，并且它还有特殊的地方，请看下面的例子：<br />
<br />
<br />
class Person{<br />
public static void prt(String s){<br />
System.out.println(s);<br />
}<br />
Person(){<br />
prt("A Person.");<br />
}<br />
Person(String name){<br />
prt("A person name is:"+name);<br />
}<br />
}<br />
public class Chinese extends Person{<br />
Chinese(){<br />
super(); //调用父类构造函数（1）<br />
prt("A chinese.");//(4)<br />
}<br />
Chinese(String name){<br />
super(name);//调用父类具有相同形参的构造函数（2）<br />
prt("his name is:"+name);<br />
}<br />
Chinese(String name,int age){<br />
this(name);//调用当前具有相同形参的构造函数（3）<br />
prt("his age is:"+age);<br />
}<br />
public static void main(String[] args){<br />
Chinese cn=new Chinese();<br />
cn=new Chinese("kevin");<br />
cn=new Chinese("kevin",22);<br />
}<br />
}<br />
<br />
<br />
　　在这段程序中，this和super不再是像以前那样用&#8220;.&#8221;连接一个方法或成员，而是直接在其后跟上适当的参数，因此它的意义也就有了变化。super后加参数的是用来调用父类中具有相同形式的构造函数，如1和2处。this后加参数则调用的是当前具有相同参数的构造函数，如3处。当然，在Chinese的各个重载构造函数中，this和super在一般方法中的各种用法也仍可使用，比如4处，你可以将它替换为&#8220;this.prt&#8221;(因为它继承了父类中的那个方法）或者是&#8220;super.prt&#8221;（因为它是父类中的方法且可被子类访问），它照样可以正确运行。但这样似乎就有点画蛇添足的味道了。 <br />
<br />
<br />
　　最后，写了这么多，如果你能对&#8220;this通常指代当前对象，super通常指代父类&#8221;这句话牢记在心，那么本篇便达到了目的，其它的你自会在以后的编程实践当中慢慢体会、掌握。另外关于本篇中提到的继承，请参阅相关Java教程。<br />
<br />
三、final<br />
<br />
　　final在Java中并不常用，然而它却为我们提供了诸如在C语言中定义常量的功能，不仅如此，final还可以让你控制你的成员、方法或者是一个类是否可被覆写或继承等功能，这些特点使final在Java中拥有了一个不可或缺的地位，也是学习Java时必须要知道和掌握的关键字之一。<br />
<br />
final成员<br />
<br />
　　当你在类中定义变量时，在其前面加上final关键字，那便是说，这个变量一旦被初始化便不可改变，这里不可改变的意思对基本类型来说是其值不可变，而对于对象变量来说其引用不可再变。其初始化可以在两个地方，一是其定义处，也就是说在final变量定义时直接给其赋值，二是在构造函数中。这两个地方只能选其一，要么在定义时给值，要么在构造函数中给值，不能同时既在定义时给了值，又在构造函数中给另外的值。下面这段代码演示了这一点：<br />
<br />
<br />
import java.util.List;<br />
import java.util.ArrayList;<br />
import java.util.LinkedList;<br />
public class Bat{<br />
final PI=3.14; //在定义时便给址值<br />
final int i; //因为要在构造函数中进行初始化，所以此处便不可再给值<br />
final List list; //此变量也与上面的一样<br />
Bat(){<br />
i=100;<br />
list=new LinkedList();<br />
}<br />
Bat(int ii,List l){<br />
i=ii;<br />
list=l;<br />
}<br />
public static void main(String[] args){<br />
Bat b=new Bat();<br />
b.list.add(new Bat());<br />
//b.i=25;<br />
//b.list=new ArrayList();<br />
System.out.println("I="+b.i+" List Type:"+b.list.getClass());<br />
b=new Bat(23,new ArrayList());<br />
b.list.add(new Bat());<br />
System.out.println("I="+b.i+" List Type:"+b.list.getClass());<br />
}<br />
}<br />
<br />
<br />
　　此程序很简单的演示了final的常规用法。在这里使用在构造函数中进行初始化的方法，这使你有了一点灵活性。如Bat的两个重载构造函数所示，第一个缺省构造函数会为你提供默认的值，重载的那个构造函数会根据你所提供的值或类型为final变量初始化。然而有时你并不需要这种灵活性，你只需要在定义时便给定其值并永不变化，这时就不要再用这种方法。在main方法中有两行语句注释掉了，如果你去掉注释，程序便无法通过编译，这便是说，不论是i的值或是list的类型，一旦初始化，确实无法再更改。然而b可以通过重新初始化来指定i的值或list的类型，输出结果中显示了这一点： <br />
<br />
<br />
I=100 List Type:class java.util.LinkedList<br />
I=23 List Type:class java.util.ArrayList<br />
<br />
　　还有一种用法是定义方法中的参数为final，对于基本类型的变量，这样做并没有什么实际意义，因为基本类型的变量在调用方法时是传值的，也就是说你可以在方法中更改这个参数变量而不会影响到调用语句，然而对于对象变量，却显得很实用，因为对象变量在传递时是传递其引用，这样你在方法中对对象变量的修改也会影响到调用语句中的对象变量，当你在方法中不需要改变作为参数的对象变量时，明确使用final进行声明，会防止你无意的修改而影响到调用方法。<br />
另外方法中的内部类在用到方法中的参变量时，此参变也必须声明为final才可使用，如下代码所示：<br />
<br />
<br />
public class INClass{<br />
void innerClass(final String str){<br />
class IClass{<br />
IClass(){<br />
System.out.println(str);<br />
}<br />
}<br />
IClass ic=new IClass();<br />
}<br />
public static void main(String[] args){<br />
INClass inc=new INClass();<br />
inc.innerClass("Hello");<br />
}<br />
}<br />
<br />
<br />
final方法 <br />
<br />
<br />
　　将方法声明为final，那就说明你已经知道这个方法提供的功能已经满足你要求，不需要进行扩展，并且也不允许任何从此类继承的类来覆写这个方法，但是继承仍然可以继承这个方法，也就是说可以直接使用。另外有一种被称为inline的机制，它会使你在调用final方法时，直接将方法主体插入到调用处，而不是进行例行的方法调用，例如保存断点，压栈等，这样可能会使你的程序效率有所提高，然而当你的方法主体非常庞大时，或你在多处调用此方法，那么你的调用主体代码便会迅速膨胀，可能反而会影响效率，所以你要慎用final进行方法定义。<br />
<br />
final类<br />
<br />
　　当你将final用于类身上时，你就需要仔细考虑，因为一个final类是无法被任何人继承的，那也就意味着此类在一个继承树中是一个叶子类，并且此类的设计已被认为很完美而不需要进行修改或扩展。对于final类中的成员，你可以定义其为final，也可以不是final。而对于方法，由于所属类为final的关系，自然也就成了final型的。你也可以明确的给final类中的方法加上一个final，但这显然没有意义。<br />
<br />
　　下面的程序演示了final方法和final类的用法：<br />
<br />
<br />
final class final{<br />
final String str="final Data";<br />
public String str1="non final data";<br />
final public void print(){<br />
System.out.println("final method.");<br />
}<br />
public void what(){<br />
System.out.println(str+"<br />
"+str1);<br />
}<br />
}<br />
public class FinalDemo { //extends final 无法继承 <br />
public static void main(String[] args){<br />
final f=new final();<br />
f.what();<br />
f.print();<br />
}<br />
}<br />
<br />
<br />
　　从程序中可以看出，final类与普通类的使用几乎没有差别，只是它失去了被继承的特性。final方法与非final方法的区别也很难从程序行看出，只是记住慎用。 <br />
<br />
<br />
final在设计模式中的应用<br />
<br />
　　在设计模式中有一种模式叫做不变模式，在Java中通过final关键字可以很容易的实现这个模式，在讲解final成员时用到的程序Bat.java就是一个不变模式的例子。如果你对此感兴趣，可以参考阎宏博士编写的《Java与模式》一书中的讲解。<br />
<br />
　　到此为止，this,static,supert和final的使用已经说完了，如果你对这四个关键字已经能够大致说出它们的区别与用法，那便说明你基本已经掌握。然而，世界上的任何东西都不是完美无缺的，Java提供这四个关键字，给程序员的编程带来了很大的便利，但并不是说要让你到处使用，一旦达到滥用的程序，便适得其反，所以在使用时请一定要认真考虑。
<img src ="http://www.blogjava.net/wjun530/aggbug/145350.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-15 11:31 <a href="http://www.blogjava.net/wjun530/archive/2007/09/15/145350.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>this与super的应用</title><link>http://www.blogjava.net/wjun530/archive/2007/09/15/145349.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Sat, 15 Sep 2007 03:29:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/15/145349.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/145349.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/15/145349.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/145349.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/145349.html</trackback:ping><description><![CDATA[通过用static来定义方法或成员，为我们编程提供了某种便利，从某种程度上可以说它类似于C语言中的全局函数和全局变量。但是，并不是说有了这种便利，你便可以随处使用，如果那样的话，你便需要认真考虑一下自己是否在用面向对象的思想编程，自己的程序是否是面向对象的。好了，现在开始讨论this&amp;super这两个关键字的意义和用法。<br />
　　在Java中，this通常指当前对象，super则指父类的。当你想要引用当前对象的某种东西，比如当前对象的某个方法，或当前对象的某个成员，你便可以利用this来实现这个目的，当然，this的另一个用途是调用当前对象的另一个构造函数，这些马上就要讨论。如果你想引用父类的某种东西，则非super莫属。由于this与super有如此相似的一些特性和与生俱来的某种关系，所以我们在这一块儿来讨论，希望能帮助你区分和掌握它们两个。<br />
　　在一般方法中<br />
　　最普遍的情况就是，在你的方法中的某个形参名与当前对象的某个成员有相同的名字，这时为了不至于混淆，你便需要明确使用this关键字来指明你要使用某个成员，使用方法是&#8220;this.成员名&#8221;，而不带this的那个便是形参。另外，还可以用&#8220;this.方法名&#8221;来引用当前对象的某个方法，但这时this就不是必须的了，你可以直接用方法名来访问那个方法，编译器会知道你要调用的是那一个。下面的代码演示了上面的用法：<br />
　　public class DemoThis{　<br />
private String name;　<br />
private int age;　<br />
DemoThis(String name,int age){　　<br />
setName(name); <br />
//你可以加上this来调用方法，像这样：this.setName(name);但这并不是必须的　　<br />
setAge(age);　　<br />
this.print(); br&gt; }　　 <br />
public void setName(String name){　　<br />
this.name=name;//此处必须指明你要引用成员变量　<br />
} <br />
public void etAge(int age){　 <br />
this.age=age;　<br />
}　<br />
public void print(){　　<br />
System.out.println("Name="+name+" ge="+age);<br />
//在此行中并不需要用this，因为没有会导致混淆的东西　<br />
}　<br />
public static void main(String[] args){　　<br />
DemoThis dt=new DemoThis("Kevin","22");<br />
　　这段代码很简单，不用解释你也应该能看明白。在构造函数中你看到用this.print(),你完全可以用print()来代替它，两者效果一样。下面我们修改这个程序，来演示super的用法。<br />
　　class Person{　<br />
public int c;　<br />
private String name;　<br />
private int age; <br />
protected void setName(String name){　　<br />
this.name=name;　<br />
}　<br />
protected void setAge(int age){　 <br />
this.age=age;<br />
　} <br />
protected void print(){　　<br />
System.out.println("Name="+name+" Age="+age); <br />
}<br />
}<br />
public class DemoSuper extends Person{　<br />
public void print(){　　<br />
System.out.println("DemoSuper:");　 <br />
super.print(); <br />
}　<br />
public static void main(String[] args){　 <br />
DemoSuper ds=new DemoSuper();　 <br />
ds.setName("kevin");　 <br />
ds.setAge(22);　 <br />
ds.print(); <br />
}<br />
}<br />
　　在DemoSuper中，重新定义的print方法覆写了父类的print方法，它首先做一些自己的事情，然后调用父类的那个被覆写了的方法。输出结果说明了这一点：<br />
　　DemoSuper:<br />
Name=kevin Age=22<br />
<br />
　　这样的使用方法是比较常用的。另外如果父类的成员可以被子类访问，那你可以像使用this一样使用它，用&#8220;super.父类中的成员名&#8221;的方式，但常常你并不是这样来访问父类中的成员名的。<br />
　　在构造函数中构造函数是一种特殊的方法，在对象初始化的时候自动调用。在构造函数中，this和super也有上面说的种种使用方式，并且它还有特殊的地方，请看下面的例子：<br />
<br />
　　<br />
class Person{　<br />
<br />
public static void prt(String s){　　<br />
System.out.println(s);　<br />
}　<br />
Person(){　 <br />
prt("A Person.");　<br />
} <br />
Person(String name){　<br />
　prt("A person name is:"+name);　<br />
<br />
}<br />
}<br />
public class Chinese extends Person{<br />
　Chinese(){　　<br />
super();　//调用父类构造函数（1）　 <br />
prt("A chinese.");//(4) <br />
}　<br />
Chinese(String name){　　<br />
super(name);//调用父类具有相同形参的构造函数（2）　　<br />
prt("his name is:"+name); <br />
} <br />
Chinese(String name,int age){　　<br />
this(name);//调用当前具有相同形参的构造函数（3）　 <br />
prt("his age is:"+age); <br />
} <br />
public static void main(String[] args){　 <br />
Chinese cn=new Chinese();　　<br />
cn=new Chinese("kevin");　 <br />
cn=new Chinese("kevin",22); <br />
}<br />
}<br />
　　在这段程序中，this和super不再是像以前那样用&#8220;.&#8221;连接一个方法或成员，而是直接在其后跟<br />
　　上适当的参数，因此它的意义也就有了变化。super后加参数的是用来调用父类中具有相同形式的<br />
　　构造函数，如1和2处。this后加参数则调用的是当前具有相同参数的构造函数，如3处。当然，在<br />
　　Chinese的各个重载构造函数中，this和super在一般方法中的各种用法也仍可使用，比如4处，你<br />
　　可以将它替换为&#8220;this.prt&#8221;(因为它继承了父类中的那个方法）或者是&#8220;super.prt&#8221;（因为它<br />
　　是父类中的方法且可被子类访问），它照样可以正确运行。但这样似乎就有点画蛇添足的味道<br />
　　了。<br />
　　最后，写了这么多，如果你能对&#8220;this通常指代当前对象，super通常指代父类&#8221;这句话牢记在<br />
　　心，那么本篇便达到了目的，其它的你自会在以后的编程实践当中慢慢体会、掌握。
<img src ="http://www.blogjava.net/wjun530/aggbug/145349.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-15 11:29 <a href="http://www.blogjava.net/wjun530/archive/2007/09/15/145349.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据库连接</title><link>http://www.blogjava.net/wjun530/archive/2007/09/15/145347.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Sat, 15 Sep 2007 03:26:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/15/145347.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/145347.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/15/145347.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/145347.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/145347.html</trackback:ping><description><![CDATA[现在有好多初学jsp的网友经常会问数据库怎么连接啊，怎么老出错啊？所以我集中的在这写篇文章供大家参考，其实这种把数据库逻辑全部放在jsp里未必是好的做法，但是有利于初学者学习，所以我就这样做了，当大家学到一定程度的时候，可以考虑用MVC的模式开发。在练习这些代码的时候，你一定将jdbc的驱动程序放到服务器的类路径里，然后要在数据库里建一个表test,有两个字段比如为test1，test2，可以用下面SQL建 <br />
create table test(test1 varchar(20),test2 varchar(20) <br />
然后向这个表写入一条测试纪录 <br />
那么现在开始我们的jsp和数据库之旅吧。 <br />
一、jsp连接Oracle8/8i/9i数据库（用thin模式） <br />
testoracle.jsp如下： <br />
&lt;%@ page contentType="text/html;charset=gb2312"%&gt; <br />
&lt;%@ page import="java.sql.*"%&gt; <br />
&lt;html&gt; <br />
&lt;body&gt; <br />
&lt;%Class.forName("oracle.jdbc.driver.OracleDriver").newInstance(); <br />
String url="jdbc:oracle:thin:@localhost:1521:orcl"; <br />
//orcl为你的数据库的SID <br />
String user="scott"; <br />
String password="tiger"; <br />
Connection conn= DriverManager.getConnection(url,user,password); <br />
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE); <br />
String sql="select * from test"; <br />
ResultSet rs=stmt.executeQuery(sql); <br />
while(rs.next()) {%&gt; <br />
您的第一个字段内容为：&lt;%=rs.getString(1)%&gt; <br />
您的第二个字段内容为：&lt;%=rs.getString(2)%&gt; <br />
&lt;%}%&gt; <br />
&lt;%out.print("数据库操作成功，恭喜你");%&gt; <br />
&lt;%rs.close(); <br />
stmt.close(); <br />
conn.close(); <br />
%&gt; <br />
&lt;/body&gt; <br />
&lt;/html&gt; <br />
二、jsp连接Sql Server7.0/2000数据库 <br />
testsqlserver.jsp如下： <br />
&lt;%@ page contentType="text/html;charset=gb2312"%&gt; <br />
&lt;%@ page import="java.sql.*"%&gt; <br />
&lt;html&gt; <br />
&lt;body&gt; <br />
&lt;%Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance(); <br />
String url="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=pubs"; <br />
//pubs为你的数据库的 <br />
String user="sa"; <br />
String password=""; <br />
Connection conn= DriverManager.getConnection(url,user,password); <br />
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE); <br />
String sql="select * from test"; <br />
ResultSet rs=stmt.executeQuery(sql); <br />
while(rs.next()) {%&gt; <br />
您的第一个字段内容为：&lt;%=rs.getString(1)%&gt; <br />
您的第二个字段内容为：&lt;%=rs.getString(2)%&gt; <br />
&lt;%}%&gt; <br />
&lt;%out.print("数据库操作成功，恭喜你");%&gt; <br />
&lt;%rs.close(); <br />
stmt.close(); <br />
conn.close(); <br />
%&gt; <br />
&lt;/body&gt; <br />
&lt;/html&gt; <br />
三、jsp连接DB2数据库 <br />
testdb2.jsp如下： <br />
&lt;%@ page contentType="text/html;charset=gb2312"%&gt; <br />
&lt;%@ page import="java.sql.*"%&gt; <br />
&lt;html&gt; <br />
&lt;body&gt; <br />
&lt;%Class.forName("com.ibm.db2.jdbc.app.DB2Driver ").newInstance(); <br />
String url="jdbc:db2://localhost:5000/sample"; <br />
//sample为你的数据库名 <br />
String user="admin"; <br />
String password=""; <br />
Connection conn= DriverManager.getConnection(url,user,password); <br />
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE); <br />
String sql="select * from test"; <br />
ResultSet rs=stmt.executeQuery(sql); <br />
while(rs.next()) {%&gt; <br />
您的第一个字段内容为：&lt;%=rs.getString(1)%&gt; <br />
您的第二个字段内容为：&lt;%=rs.getString(2)%&gt; <br />
&lt;%}%&gt; <br />
&lt;%out.print("数据库操作成功，恭喜你");%&gt; <br />
&lt;%rs.close(); <br />
stmt.close(); <br />
conn.close(); <br />
%&gt; <br />
&lt;/body&gt; <br />
&lt;/html&gt; <br />
四、jsp连接Informix数据库 <br />
testinformix.jsp如下： <br />
&lt;%@ page contentType="text/html;charset=gb2312"%&gt; <br />
&lt;%@ page import="java.sql.*"%&gt; <br />
&lt;html&gt; <br />
&lt;body&gt; <br />
&lt;%Class.forName("com.informix.jdbc.IfxDriver").newInstance(); <br />
String url = <br />
"jdbc:informix-sqli://123.45.67.89:1533/testDB:INFORMIXSERVER=myserver; <br />
user=testuser;password=testpassword"; <br />
//testDB为你的数据库名 <br />
Connection conn= DriverManager.getConnection(url); <br />
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE); <br />
String sql="select * from test"; <br />
ResultSet rs=stmt.executeQuery(sql); <br />
while(rs.next()) {%&gt; <br />
您的第一个字段内容为：&lt;%=rs.getString(1)%&gt; <br />
您的第二个字段内容为：&lt;%=rs.getString(2)%&gt; <br />
&lt;%}%&gt; <br />
&lt;%out.print("数据库操作成功，恭喜你");%&gt; <br />
&lt;%rs.close(); <br />
stmt.close(); <br />
conn.close(); <br />
%&gt; <br />
&lt;/body&gt; <br />
&lt;/html&gt; <br />
五、jsp连接Sybase数据库 <br />
testmysql.jsp如下： <br />
&lt;%@ page contentType="text/html;charset=gb2312"%&gt; <br />
&lt;%@ page import="java.sql.*"%&gt; <br />
&lt;html&gt; <br />
&lt;body&gt; <br />
&lt;%Class.forName("com.sybase.jdbc.SybDriver").newInstance(); <br />
String url =" jdbc:sybase:Tds:localhost:5007/tsdata"; <br />
//tsdata为你的数据库名 <br />
Properties sysProps = System.getProperties(); <br />
SysProps.put("user","userid"); <br />
SysProps.put("password","user_password"); <br />
Connection conn= DriverManager.getConnection(url, SysProps); <br />
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE); <br />
String sql="select * from test"; <br />
ResultSet rs=stmt.executeQuery(sql); <br />
while(rs.next()) {%&gt; <br />
您的第一个字段内容为：&lt;%=rs.getString(1)%&gt; <br />
您的第二个字段内容为：&lt;%=rs.getString(2)%&gt; <br />
&lt;%}%&gt; <br />
&lt;%out.print("数据库操作成功，恭喜你");%&gt; <br />
&lt;%rs.close(); <br />
stmt.close(); <br />
conn.close(); <br />
%&gt; <br />
&lt;/body&gt; <br />
&lt;/html&gt; <br />
六、jsp连接MySQL数据库 <br />
testmysql.jsp如下： <br />
&lt;%@ page contentType="text/html;charset=gb2312"%&gt; <br />
&lt;%@ page import="java.sql.*"%&gt; <br />
&lt;html&gt; <br />
&lt;body&gt; <br />
&lt;%Class.forName("org.gjt.mm.mysql.Driver").newInstance(); <br />
String url ="jdbc:mysql://localhost/softforum?user=soft&amp;password=soft1234&amp;useUnicode=true&amp;characterEncoding=8859_1" <br />
//testDB为你的数据库名 <br />
Connection conn= DriverManager.getConnection(url); <br />
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE); <br />
String sql="select * from test"; <br />
ResultSet rs=stmt.executeQuery(sql); <br />
while(rs.next()) {%&gt; <br />
您的第一个字段内容为：&lt;%=rs.getString(1)%&gt; <br />
您的第二个字段内容为：&lt;%=rs.getString(2)%&gt; <br />
&lt;%}%&gt; <br />
&lt;%out.print("数据库操作成功，恭喜你");%&gt; <br />
&lt;%rs.close(); <br />
stmt.close(); <br />
conn.close(); <br />
%&gt; <br />
&lt;/body&gt; <br />
&lt;/html&gt; <br />
七、jsp连接PostgreSQL数据库 <br />
testmysql.jsp如下： <br />
&lt;%@ page contentType="text/html;charset=gb2312"%&gt; <br />
&lt;%@ page import="java.sql.*"%&gt; <br />
&lt;html&gt; <br />
&lt;body&gt; <br />
&lt;%Class.forName("org.postgresql.Driver").newInstance(); <br />
String url ="jdbc:postgresql://localhost/soft" <br />
//soft为你的数据库名 <br />
String user="myuser"; <br />
String password="mypassword"; <br />
Connection conn= DriverManager.getConnection(url,user,password); <br />
Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE); <br />
String sql="select * from test"; <br />
ResultSet rs=stmt.executeQuery(sql); <br />
while(rs.next()) {%&gt; <br />
您的第一个字段内容为：&lt;%=rs.getString(1)%&gt; <br />
您的第二个字段内容为：&lt;%=rs.getString(2)%&gt; <br />
&lt;%}%&gt; <br />
&lt;%out.print("数据库操作成功，恭喜你");%&gt; <br />
&lt;%rs.close(); <br />
stmt.close(); <br />
conn.close(); <br />
%&gt; <br />
&lt;/body&gt; <br />
&lt;/html&gt; <br />
例：<br />
import java.sql.*;<br />
public class dbaccess{<br />
public static void main(String args[]) throws Exception{<br />
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");<br />
String dburl ="jdbc:odbc:driver={Microsoft Access Driver (*.mdb)};DBQ=Mobile.mdb";//此为NO-DSN方式<br />
//String dburl ="jdbc:odbc:odbcName";//此为ODBC连接方式<br />
Connection conn=DriverManager.getConnection(dburl);<br />
Statement stmt=conn.createStatement();<br />
ResultSet rs=stmt.executeQuery("select Top 20 * from MobileSection");<br />
System.out.println("号段 地区 类型 省份 区号");<br />
while(rs.next()){<br />
System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3)+" "+rs.getString(4)+" "+rs.getString(5));<br />
}<br />
rs.close();<br />
stmt.close();<br />
conn.close();<br />
}<br />
}<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/145347.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-15 11:26 <a href="http://www.blogjava.net/wjun530/archive/2007/09/15/145347.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA中浅复制与深复制概念详细解析</title><link>http://www.blogjava.net/wjun530/archive/2007/09/10/143925.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Mon, 10 Sep 2007 03:15:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/10/143925.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/143925.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/10/143925.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/143925.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/143925.html</trackback:ping><description><![CDATA[1．浅复制与深复制概念 <br />
<br />
⑴浅复制（浅克隆） <br />
<br />
被复制对象的所有变量都含有与原来的对象相同的值，而所有的对其他对象的引用仍然指向原来的对象。换言之，浅复制仅仅复制所考虑的对象，而不复制它所引用的对象。 <br />
<br />
<br />
⑵深复制（深克隆） <br />
<br />
被复制对象的所有变量都含有与原来的对象相同的值，除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象，而不再是原有的那些被引用的对象。换言之，深复制把要复制的对象所引用的对象都复制了一遍。 <br />
<br />
<br />
2．Java的clone（）方法 <br />
<br />
⑴clone方法将对象复制了一份并返回给调用者。一般而言，clone（）方法满足： <br />
<br />
①对任何的对象x，都有x.clone() !=x//克隆对象与原对象不是同一个对象 <br />
<br />
②对任何的对象x，都有x.clone().getClass()= =x.getClass()//克隆对象与原对象的类型一样 <br />
<br />
③如果对象x的equals()方法定义恰当，那么x.clone().equals(x)应该成立。 <br />
<br />
<br />
⑵Java中对象的克隆 <br />
<br />
①为了获取对象的一份拷贝，我们可以利用Object类的clone()方法。 <br />
<br />
②在派生类中覆盖基类的clone()方法，并声明为public。 <br />
<br />
③在派生类的clone()方法中，调用super.clone()。 <br />
<br />
④在派生类中实现Cloneable接口。 <br />
<br />
<br />
请看如下代码： <br />
<br />
<br />
class Student implements Cloneable <br />
<br />
{ <br />
<br />
String name; <br />
<br />
int age; <br />
<br />
Student(String name,int age) <br />
<br />
{ <br />
<br />
this.name=name; <br />
<br />
this.age=age; <br />
<br />
} <br />
<br />
public Object clone() <br />
<br />
{ <br />
<br />
Object o=null; <br />
<br />
try <br />
<br />
{ <br />
<br />
o=(Student)super.clone();//Object中的clone()识别出你要复制的是哪一 <br />
<br />
// 个对象。 <br />
<br />
} <br />
<br />
catch(CloneNotSupportedException e) <br />
<br />
{ <br />
<br />
System.out.println(e.toString()); <br />
<br />
} <br />
<br />
return o; <br />
<br />
} <br />
<br />
} <br />
<br />
<br />
public static void main(String[] args) <br />
<br />
{ <br />
<br />
Student s1=new Student("zhangsan",18); <br />
<br />
Student s2=(Student)s1.clone(); <br />
<br />
s2.name="lisi"; <br />
<br />
s2.age=20; <br />
<br />
System.out.println("name="+s1.name+","+"age="+s1.age);//修改学生2后，不影响 <br />
<br />
//学生1的值。 <br />
<br />
} <br />
<br />
<br />
说明： <br />
<br />
①为什么我们在派生类中覆盖Object的clone()方法时，一定要调用super.clone()呢？在运行时刻，Object中的clone()识别出你要复制的是哪一个对象，然后为此对象分配空间，并进行对象的复制，将原始对象的内容一一复制到新对象的存储空间中。 <br />
<br />
②继承自java.lang.Object类的clone()方法是浅复制。以下代码可以证明之。 <br />
<br />
<br />
class Professor <br />
<br />
{ <br />
<br />
String name; <br />
<br />
int age; <br />
<br />
Professor(String name,int age) <br />
<br />
{ <br />
<br />
this.name=name; <br />
<br />
this.age=age; <br />
<br />
} <br />
<br />
} <br />
<br />
class Student implements Cloneable <br />
<br />
{ <br />
<br />
String name;//常量对象。 <br />
<br />
int age; <br />
<br />
Professor p;//学生1和学生2的引用值都是一样的。 <br />
<br />
Student(String name,int age,Professor p) <br />
<br />
{ <br />
<br />
this.name=name; <br />
<br />
this.age=age; <br />
<br />
this.p=p; <br />
<br />
} <br />
<br />
public Object clone() <br />
<br />
{ <br />
<br />
Student o=null; <br />
<br />
try <br />
<br />
{ <br />
<br />
o=(Student)super.clone(); <br />
<br />
} <br />
<br />
catch(CloneNotSupportedException e) <br />
<br />
{ <br />
<br />
System.out.println(e.toString()); <br />
<br />
} <br />
<br />
o.p=(Professor)p.clone(); <br />
<br />
return o; <br />
<br />
} <br />
<br />
} <br />
<br />
public static void main(String[] args) <br />
<br />
{ <br />
<br />
Professor p=new Professor("wangwu",50); <br />
<br />
Student s1=new Student("zhangsan",18,p); <br />
<br />
Student s2=(Student)s1.clone(); <br />
<br />
s2.p.name="lisi"; <br />
<br />
s2.p.age=30; <br />
<br />
System.out.println("name="+s1.p.name+","+"age="+s1.p.age);//学生1的教授 <br />
<br />
//成为lisi,age为30。 <br />
<br />
} <br />
<br />
<br />
那应该如何实现深层次的克隆，即修改s2的教授不会影响s1的教授？代码改进如下。 <br />
<br />
<br />
改进使学生1的Professor不改变（深层次的克隆） <br />
<br />
class Professor implements Cloneable <br />
<br />
{ <br />
<br />
String name; <br />
<br />
int age; <br />
<br />
Professor(String name,int age) <br />
<br />
{ <br />
<br />
this.name=name; <br />
<br />
this.age=age; <br />
<br />
} <br />
<br />
public Object clone() <br />
<br />
{ <br />
<br />
Object o=null; <br />
<br />
try <br />
<br />
{ <br />
<br />
o=super.clone(); <br />
<br />
} <br />
<br />
catch(CloneNotSupportedException e) <br />
<br />
{ <br />
<br />
System.out.println(e.toString()); <br />
<br />
} <br />
<br />
return o; <br />
<br />
} <br />
<br />
} <br />
<br />
class Student implements Cloneable <br />
<br />
{ <br />
<br />
String name; <br />
<br />
int age; <br />
<br />
Professor p; <br />
<br />
Student(String name,int age,Professor p) <br />
<br />
{ <br />
<br />
this.name=name; <br />
<br />
this.age=age; <br />
<br />
this.p=p; <br />
<br />
} <br />
<br />
public Object clone() <br />
<br />
{ <br />
<br />
Student o=null; <br />
<br />
try <br />
<br />
{ <br />
<br />
o=(Student)super.clone(); <br />
<br />
} <br />
<br />
catch(CloneNotSupportedException e) <br />
<br />
{ <br />
<br />
System.out.println(e.toString()); <br />
<br />
} <br />
<br />
o.p=(Professor)p.clone(); <br />
<br />
return o; <br />
<br />
} <br />
<br />
} <br />
<br />
public static void main(String[] args) <br />
<br />
{ <br />
<br />
Professor p=new Professor("wangwu",50); <br />
<br />
Student s1=new Student("zhangsan",18,p); <br />
<br />
Student s2=(Student)s1.clone(); <br />
<br />
s2.p.name="lisi"; <br />
<br />
s2.p.age=30; <br />
<br />
System.out.println("name="+s1.p.name+","+"age="+s1.p.age);//学生1的教授不改变。 <br />
<br />
} <br />
<br />
<br />
3．利用串行化来做深复制 <br />
<br />
把对象写到流里的过程是串行化（Serilization）过程，但是在Java程序师圈子里又非常形象地称为&#8220;冷冻&#8221;或者&#8220;腌咸菜（picking）&#8221;过程；而把对象从流中读出来的并行化（Deserialization）过程则叫做&#8220;解冻&#8221;或者&#8220;回鲜(depicking)&#8221;过程。应当指出的是，写在流里的是对象的一个拷贝，而原对象仍然存在于JVM里面，因此&#8220;腌成咸菜&#8221;的只是对象的一个拷贝，Java咸菜还可以回鲜。 <br />
<br />
在Java语言里深复制一个对象，常常可以先使对象实现Serializable接口，然后把对象（实际上只是对象的一个拷贝）写到一个流里（腌成咸菜），再从流里读出来（把咸菜回鲜），便可以重建对象。 <br />
<br />
如下为深复制源代码。 <br />
<br />
public Object deepClone() <br />
<br />
{ <br />
<br />
//将对象写到流里 <br />
<br />
ByteArrayOutoutStream bo=new ByteArrayOutputStream(); <br />
<br />
ObjectOutputStream oo=new ObjectOutputStream(bo); <br />
<br />
oo.writeObject(this); <br />
<br />
//从流里读出来 <br />
<br />
ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray()); <br />
<br />
ObjectInputStream oi=new ObjectInputStream(bi); <br />
<br />
return(oi.readObject()); <br />
<br />
} <br />
<br />
<br />
这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的，否则，就需要仔细考察那些不可串行化的对象可否设成transient，从而将之排除在复制过程之外。上例代码改进如下。 <br />
<br />
<br />
class Professor implements Serializable <br />
<br />
{ <br />
<br />
String name; <br />
<br />
int age; <br />
<br />
Professor(String name,int age) <br />
<br />
{ <br />
<br />
this.name=name; <br />
<br />
this.age=age; <br />
<br />
} <br />
<br />
} <br />
<br />
class Student implements Serializable <br />
<br />
{ <br />
<br />
String name;//常量对象。 <br />
<br />
int age; <br />
<br />
Professor p;//学生1和学生2的引用值都是一样的。 <br />
<br />
Student(String name,int age,Professor p) <br />
<br />
{ <br />
<br />
this.name=name; <br />
<br />
this.age=age; <br />
<br />
this.p=p; <br />
<br />
} <br />
<br />
public Object deepClone() throws IOException, <br />
<br />
OptionalDataException,ClassNotFoundException <br />
<br />
{ <br />
<br />
//将对象写到流里 <br />
<br />
ByteArrayOutoutStream bo=new ByteArrayOutputStream(); <br />
<br />
ObjectOutputStream oo=new ObjectOutputStream(bo); <br />
<br />
oo.writeObject(this); <br />
<br />
//从流里读出来 <br />
<br />
ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray()); <br />
<br />
ObjectInputStream oi=new ObjectInputStream(bi); <br />
<br />
return(oi.readObject()); <br />
<br />
} <br />
<br />
<br />
} <br />
<br />
public static void main(String[] args) <br />
<br />
{ <br />
<br />
Professor p=new Professor("wangwu",50); <br />
<br />
Student s1=new Student("zhangsan",18,p); <br />
<br />
Student s2=(Student)s1.deepClone(); <br />
<br />
s2.p.name="lisi"; <br />
<br />
s2.p.age=30; <br />
<br />
System.out.println("name="+s1.p.name+","+"age="+s1.p.age); //学生1的教授不改变。 <br />
<br />
} <br />
<br />
<br />
4．参考资料 <br />
<br />
⑴阎宏，Java与模式，电子工业出版社，2006 <br />
<br />
⑵孙鑫Java讲座视频资料<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/143925.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-10 11:15 <a href="http://www.blogjava.net/wjun530/archive/2007/09/10/143925.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>获取JAVA路径 包括CLASSPATH外的路径</title><link>http://www.blogjava.net/wjun530/archive/2007/09/10/143923.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Mon, 10 Sep 2007 03:14:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/10/143923.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/143923.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/10/143923.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/143923.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/143923.html</trackback:ping><description><![CDATA[归纳一些网上取JAVA路径的方法： <br />
<br />
注明：如果从ANT启动程序，this.getClass().getResource("")取出来的比较怪，直接用JAVA命令行调试就可成功。<br />
<br />
得到classpath和当前类的绝对路径的一些方法<br />
<br />
获得CLASSPATH之外路径的方法：<br />
<br />
　　URL base = this.getClass（）.getResource（""）； //先获得本类的所在位置，如/home/popeye/testjava/build/classes/net/ String path = new File（base.getFile（）， "&#8230;&#8230;/&#8230;&#8230;/&#8230;&#8230;/"+name）.getCanonicalPath（）； //就可以得到/home/popeye/testjava/name<br />
<br />
下面是一些得到classpath和当前类的绝对路径的一些方法。你可能需要使用其中的一些方法来得到你需要的资源的绝对路径。<br />
<br />
1.FileTest.class.getResource（""）<br />
<br />
得到的是当前类FileTest.class文件的URI目录。不包括自己！<br />
<br />
如：file：/D：/java/eclipse32/workspace/jbpmtest3/bin/com/test/<br />
<br />
2.FileTest.class.getResource（"/"）<br />
<br />
得到的是当前的classpath的绝对URI路径。<br />
<br />
如：file：/D：/java/eclipse32/workspace/jbpmtest3/bin/<br />
<br />
3.Thread.currentThread（）.getContextClassLoader（）.getResource（""）<br />
<br />
得到的也是当前ClassPath的绝对URI路径。<br />
<br />
如：file：/D：/java/eclipse32/workspace/jbpmtest3/bin/<br />
<br />
4.FileTest.class.getClassLoader（）.getResource（""）<br />
<br />
得到的也是当前ClassPath的绝对URI路径。<br />
<br />
如：file：/D：/java/eclipse32/workspace/jbpmtest3/bin/<br />
<br />
5.ClassLoader.getSystemResource（""）<br />
<br />
得到的也是当前ClassPath的绝对URI路径。<br />
<br />
如：file：/D：/java/eclipse32/workspace/jbpmtest3/bin/<br />
<br />
我推荐使用Thread.currentThread（）.getContextClassLoader（）.getResource（""）来得到当前的classpath的绝对路径的URI表示法。<br />
<br />
在Web应用程序中，我们一般通过ServletContext.getRealPath（"/"）方法得到Web应用程序的根目录的绝对路径。这样，我们只需要提供相对于Web应用程序根目录的路径，就可以构建出定位资源的绝对路径。<br />
<br />
注意点：<br />
<br />
1.尽量不要使用相对于System.getProperty（"user.dir"）当前用户目录的相对路径。这是一颗定时炸弹，随时可能要你的命。<br />
<br />
2.尽量使用URI形式的绝对路径资源。它可以很容易的转变为URI，URL，File对象。<br />
<br />
3.尽量使用相对classpath的相对路径。不要使用绝对路径。使用上面ClassLoaderUtil类的public static URL getExtendResource（String relativePath）方法已经能够使用相对于classpath的相对路径定位所有位置的资源。<br />
<br />
4.绝对不要使用硬编码的绝对路径。因为，我们完全可以使用ClassLoader类的getResource（""）方法得到当前classpath的绝对路径。<br />
<br />
使用硬编码的绝对路径是完全没有必要的！它一定会让你死的很难看！程序将无法移植！<br />
<br />
如果你一定要指定一个绝对路径，那么使用配置文件，也比硬编码要好得多！<br />
<br />
当然，我还是推荐你使用程序得到classpath的绝对路径来拼资源的绝对路径。
<img src ="http://www.blogjava.net/wjun530/aggbug/143923.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-10 11:14 <a href="http://www.blogjava.net/wjun530/archive/2007/09/10/143923.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JUnit 介绍</title><link>http://www.blogjava.net/wjun530/archive/2007/09/05/142900.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Wed, 05 Sep 2007 06:55:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/05/142900.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/142900.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/05/142900.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/142900.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/142900.html</trackback:ping><description><![CDATA[一、JUnit简介<br />
&nbsp; JUnit是基于面向对象构建的java单元测试框架。<br />
&nbsp; JUnit是开放源代码项目，可按需要进行扩展。<br />
&nbsp; <br />
&nbsp;二、安装JUnit<br />
&nbsp; 首先获取JUnit的软件包，从http://www.junit.org下载最新的软件包。<br />
&nbsp; 将软件包在适当的目录下解包。<br />
&nbsp; 这样在安装目录下找到一个名为junit.jar的文件，将这个jar文件加入<br />
&nbsp;<br />
&nbsp;CLASSPATH系统变量。<br />
&nbsp;<br />
&nbsp;三、JUnit框架介绍：<br />
&nbsp;一）TestCase--测试用例，对每个测试类，都要定义一个测试用例。<br />
&nbsp;JUnit支持两种运行单个测试的方法：静态的和动态的方法。 <br />
&nbsp;1、静态的方法就是覆盖TestCase类的runTest()方法，一般是采用内部类的方式<br />
&nbsp;<br />
&nbsp;创建一个测试实例： <br />
&nbsp;TestCase test01 = new testCar("test getWheels") { <br />
&nbsp; public void runTest() { <br />
&nbsp; testGetWheels(); <br />
&nbsp; } <br />
&nbsp;}<br />
&nbsp;采用静态的方法要注意要给每个测试一个名字，这样你就可以区分哪个测试失败<br />
&nbsp;<br />
&nbsp;了。 <br />
&nbsp;<br />
&nbsp;2、动态的方法是用内省来实现runTest()以创建一个测试实例。这要求测试的名<br />
&nbsp;<br />
&nbsp;字就是需要调用的测试方法的名字： <br />
&nbsp;TestCase test01 = new testCar("testGetWheels"); <br />
&nbsp;JUnit会动态查找并调用指定的测试方法。动态的方法很简洁，但如果你键入了错<br />
&nbsp;<br />
&nbsp;误的名字就会得到一个令人奇怪的NoSuchMethodException异常。动态的方法和静<br />
&nbsp;<br />
&nbsp;态的方法都很好，你可以按照自己的喜好来选择。<br />
&nbsp;<br />
&nbsp;二）TestSuite <br />
&nbsp;一旦你创建了一些测试实例，下一步就是要让他们能一起运行。我们必须定义一<br />
&nbsp;<br />
&nbsp;个TestSuite。在JUnit中，这就要求你在TestCase类中定义一个静态的suite()方<br />
&nbsp;<br />
&nbsp;法。suite()方法就像main()方法一样，JUnit用它来执行测试。在suite()方法中<br />
&nbsp;<br />
&nbsp;，你将测试实例加到一个TestSuite对象中，并返回这个TestSuite对象。一个<br />
&nbsp;<br />
&nbsp;TestSuite对象可以运行一组测试。<br />
&nbsp;TestSuite和TestCase都实现了Test接口，而Test接口定义了运行测试所需的方法<br />
&nbsp;<br />
&nbsp;。这就允许你用TestCase和TestSuite的组合创建一个TestSuite。<br />
&nbsp;public static Test suite() { <br />
&nbsp; TestSuite suite= new TestSuite(); <br />
&nbsp; suite.addTest(new testCar("testGetWheels")); <br />
&nbsp; suite.addTest(new testCar("testGetSeats")); <br />
&nbsp; return suite; <br />
&nbsp;}<br />
&nbsp;public static Test suite() { <br />
&nbsp; return new TestSuite(testCar.class); <br />
&nbsp;}<br />
&nbsp;<br />
&nbsp;三）TestRunner<br />
&nbsp;有了TestSuite我们就可以运行这些测试了，JUnit提供了三种界面来运行测试 <br />
&nbsp;[Text UI] junit.textui.TestRunner <br />
&nbsp;[AWT UI] junit.awtui.TestRunner <br />
&nbsp;[Swing UI] junit.swingui.TestRunner <br />
&nbsp;java junit.textui.TestRunner yourTestclass<br />
&nbsp;<br />
&nbsp;Fixture<br />
&nbsp;如果需要在一个或若干个的类执行多个测试，这些类就成为了测试的context。在<br />
&nbsp;<br />
&nbsp;JUnit中被称为Fixture。当你编写测试代码时，你会发现你花费了很多时间配置/<br />
&nbsp;<br />
&nbsp;初始化相关测试的Fixture。将配置Fixture的代码放入测试类的构造方法中并不<br />
&nbsp;<br />
&nbsp;可取，因为我们要求执行多个测试，我并不希望某个测试的结果意外地（如果这<br />
&nbsp;<br />
&nbsp;是你要求的，那就另当别论了）影响其他测试的结果。通常若干个测试会使用相<br />
&nbsp;<br />
&nbsp;同的Fixture，而每个测试又各有自己需要改变的地方。 <br />
&nbsp;为此，JUnit提供了两个方法，定义在TestCase类中。 <br />
&nbsp;<br />
&nbsp;protected void setUp() throws java.lang.Exception <br />
&nbsp;protected void tearDown() throws java.lang.Exception <br />
&nbsp;<br />
&nbsp;覆盖setUp()方法，初始化所有测试的Fixture，如建立数据库连接，将每个测试<br />
&nbsp;<br />
&nbsp;略有不同的地方在testXXX()方法中进行配置。 <br />
&nbsp;覆盖tearDown()，释放你在setUp()中分配的永久性资源，如数据库连接。 <br />
&nbsp;当JUnit执行测试时，它在执行每个testXXXXX()方法前都调用setUp()，而在执行<br />
&nbsp;<br />
&nbsp;每个testXXXXX()方法后都调用tearDown()方法，由此保证了测试不会相互影响。 <br />
&nbsp;<br />
&nbsp;四）Assert<br />
&nbsp;Assert类中定义了相当多的assert方法，主要有assert(),assertEquals(), <br />
&nbsp;<br />
&nbsp;assertNull(), assertSame(), assertTrue(), fail()等方法。<br />
&nbsp;<br />
&nbsp;Faile失败是一个期望的被assert方法检查到的结果。<br />
&nbsp;Error错误则是意外的问题引起的，如ArrayIndexOutOfBoundsException。 <br />
&nbsp;<br />
&nbsp;<br />
&nbsp;四、测试流程： <br />
&nbsp;1. 扩展TestCase类；<br />
&nbsp;2. 覆盖runTest()方法（可选）；<br />
&nbsp;3. 对应测试目标类书写testXXXXX()方法；<br />
&nbsp;4、扩展TestSuite类，重载suite()方法，实现自定义的测试过程；<br />
&nbsp;5. 运行TestRunner进行测试；<br />
http://www.chinaitlab.com/www/news/article_show.asp?id=24311
<img src ="http://www.blogjava.net/wjun530/aggbug/142900.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-05 14:55 <a href="http://www.blogjava.net/wjun530/archive/2007/09/05/142900.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>理解敏捷开发的常见误区</title><link>http://www.blogjava.net/wjun530/archive/2007/09/05/142879.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Wed, 05 Sep 2007 05:49:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/05/142879.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/142879.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/05/142879.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/142879.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/142879.html</trackback:ping><description><![CDATA[好文章，大家共勉：<br />
<br />
<br />
<br />
1.&nbsp;敏捷是&#8220;一个&#8221;过程<br />
　　<br />
敏捷不是一个过程，是一类过程的统称，它们有一个共性，就是符合敏捷价值观，遵循敏捷的原则。<br />
<br />
<br />
敏捷的价值观如下：<br />
　　<br />
个体和交互&nbsp;胜过&nbsp;过程和工具<br />
　　<br />
可以工作的软件&nbsp;胜过&nbsp;面面俱到的文档<br />
<br />
客户合作&nbsp;胜过&nbsp;合同谈判<br />
　　<br />
响应变化&nbsp;胜过&nbsp;遵循计划<br />
　　<br />
由价值观引出的12条敏捷原则：<br />
　　<br />
我们最优先要做的是通过尽早的、持续的交付有价值的软件来使客户满意。<br />
　　<br />
即使到了开发的后期，也欢迎改变需求。敏捷过程利用变化来为客户创造竞争优势。<br />
　　<br />
经常性地交付可以工作的软件，交付的间隔可以从几个星期到几个月，交付的时间间隔越短越好。<br />
　　<br />
在整个项目开发期间，业务人员和开发人员必须天天都在一起工作。<br />
　　<br />
围绕被激励起来的个体来构建项目。给他们提供所需的环境和支持，并且信任他们能够完成工作。<br />
　　<br />
在团队内部，最具有效果并且富有效率的传递信息的方法，就是面对面的交谈。<br />
　　<br />
工作的软件是首要的进度度量标准。<br />
　　<br />
敏捷过程提倡可持续的开发速度。责任人、开发者和用户应该能够保持一个长期的、恒定的开发速度。<br />
　　<br />
不断地关注优秀的技能和好的设计会增强敏捷能力。<br />
　　<br />
简单??使未完成的工作最大化的艺术??是根本的。<br />
　　<br />
最好的构架、需求和设计出自于自组织的团队。<br />
　　<br />
每隔一定时间，团队会在如何才能更有效地工作方面进行反省，然后相应地对自己的行为进行调整。<br />
　　<br />
建立敏捷联盟的17位大师所创立的敏捷方法包括：极限编程，Scrum，特征驱动开发，动态系统开发方法，自适应软件开发，水晶方法，实用编程方法。这些方法统称为敏捷方法。<br />
　　<br />
其实每个人都可以从敏捷宣言和原则出发，明确问题，找出一些解决方法，形成自己的过程。我觉得国内的软件环境这么复杂，程序员的自主精神又这么强，敏捷方法应该是在中国首先提出才对，只是国人都有唯标准唯规范至上的心理定式，即使找出好办法，也觉得不规范，没有深入形成理论，无法提升高度，始终是跟着鬼子屁股后面走，我想这也是国外软件行业不成熟的表现之一吧。<br />
　　<br />
　　&nbsp;<br />
　　<br />
2.&nbsp;敏捷仅仅是一个软件过程<br />
　　<br />
如果仅仅从软件过程的角度去认识敏捷实施敏捷，效果不会太好。敏捷相对以前的软件工程最大的革新之处在于把人的作用提高到了过程至上，正如敏捷宣言的第一条&#8220;个体和交互胜过过程和工具&#8221;所说的。<br />
　　<br />
涉及到人的问题，就已经不再是过程所能覆盖的了，就到了企业管理的层面上了，包括企业的价值观和文化。这也是敏捷在国内实施的最大障碍：<br />
　　<br />
把客户当作合作伙伴而不是对手，从客户角度出发去想问题，充分的跟客户沟通，而不是出了问题推诿责任。目标是让软件实现客户的价值，而不是收钱就完事儿。<br />
　　<br />
把人的能动性调动起来，给动力而不是给压力。<br />
　　<br />
要实用而不是要规范。让开发人员理解并实施，体验到敏捷的好处，而不是盲目机械地实施规范。<br />
　　<br />
没有绝对的权威，每个人都有可取之处。<br />
　　<br />
　　&nbsp;<br />
　　<br />
3.&nbsp;迭代就是敏捷，UP属于敏捷。<br />
　　<br />
看到这么多人都把UP归入敏捷，我都开始怀疑是不是自己搞错了。但是在我的印象中：<br />
　　<br />
UP是重型的过程，虽然引入了迭代，但是其原则和价值观与敏捷是不同的。敏捷注重的是反馈，迭代周期尽量的短，重在客户的参与，通过客户的参与，获取持续的反馈，不断调整使整个项目走在正确的方向上。同时也给客户一个感受和思考的机会，因为对于大多数客户而言，目标是明确的（不排除有些客户目标也不明确），但是具体怎么做，开始时是没有想法的，只有看到具体的东西的时候，才知道&#8220;噢，原来可以这样，那我想把这里调整一下&#8221;。<br />
　　<br />
　　&nbsp;<br />
　　<br />
4.&nbsp;敏捷是彻底革命的。<br />
　　<br />
敏捷，特别是XP，让人有耳目一新的感觉，觉得以前的所有软件工程理论，设计方法都可以抛弃掉了，推翻一切，从头再来。抱着这种想法实施敏捷，那就错了，敏捷不是&#8220;石头里蹦出个孙大圣&#8221;，以前的软件过程中也有敏捷的影子，只是没有像敏捷一样上升到价值观和原则的高度，比如快速原型法。敏捷是在对已有的软件过程方法的改进，抛弃的是传统软件工程低效的外表，以往的软件过程中很多技巧都是很实用的。实施敏捷应该以现有的软件过程为基础，从敏捷宣言和原则出发，利用敏捷的方法来改善过程。<br />
　　<br />
　　&nbsp;<br />
　　<br />
5.&nbsp;敏捷是反文档的。<br />
　　<br />
文档只是为了达成目标的一种手段，如果这种手段是低效的，那就换一种手段。可是完全抛弃了文档，怎样解决沟通的问题？难道你想每次沟通都完全用手比划，用嘴说，跟不同的人重复表述同样的想法，那样更是低效的。<br />
　　<br />
应该清楚文档的本质是把知识显性化。在一个项目中存在很多需要沟通的知识，知识具备两种形态，显性的和隐性的，传统的观念是尽量把隐性知识显性化，即文档化，而忽略了这其中的代价（特别是更新同步文档的代价）。<br />
　　<br />
因此，在实施敏捷的时候，需要在团队内明确哪些知识是必须显性的，这些知识可以通过文档交流。哪些知识是可以隐性的，这些知识则完全可以通过口头的方式进行交流，以达到沟通的最佳效率。<br />
　　<br />
文档不是目的，有效沟通才是目的。<br />
　　<br />
　　&nbsp;<br />
　　<br />
6.&nbsp;为了敏捷而敏捷<br />
　　<br />
&#8220;嗯，敏捷这么好，我们也敏捷吧&#8221;，可能很多人会有这种想法。忘了以前是在哪儿看的大师采访录：<br />
　　<br />
Q：&#8220;我们现有的过程很好，不知道怎么用敏捷改进？&#8221;<br />
　　<br />
A：&#8220;既然很好，那就不要用敏捷&#8221;。<br />
　　<br />
做什么事情都要有明确目标的，敏捷虽好，得看你需不需要，能不能解决你现在头疼的问题，如果不是，那就不要给自己找麻烦了。<br />
　　<br />
　　&nbsp;<br />
　　<br />
7.&nbsp;敏捷是CMM的反义词<br />
　　<br />
在桂林会议的讨论中，很多人把CMM作为敏捷的反义词，我觉得这不是很合适。CMM只是一种衡量软件成熟度的标准，并非过程，和敏捷不是一类概念。如果要给敏捷找一个反义词，我觉得传统的瀑布式开发应该更合适一些。<br />
　　<br />
并且，我认为，如果CMM还能继续流行下去的话，应该会有公司可以用敏捷改善的过程通过CMM认证。<br />
　　<br />
　　&nbsp;<br />
　　<br />
8.&nbsp;敏捷是自由的，无约束的。<br />
　　<br />
敏捷强调的是自组织团队，发挥人的能动性，以动力代替压力，让人有绝对自由的错觉。但是应该清楚，凡是都是要讲究一个平衡，人也是两面的，消极的一面和积极的一面同时并存，绝对的自由会放纵人消极的一面。敏捷并非是绝对自由，无约束的。作为管理者，有一个职责，就是引导团队成员用自己积极的一面去压制消极的一面，不能放任团队中出现搭便车的现象，否则将打击整个团队的士气。如果实在无效，那就只能将其排除出团队了，这个惩罚够有约束力吧？<br />
　　<br />
　　&nbsp;<br />
　　<br />
9.&nbsp;重做就是重构<br />
　　<br />
重做不等于重构，很多场合这两个概念是混淆的。但是在敏捷中，重构的一个特征是必须可控的。当对系统结构进行大的调整时，如果没有测试驱动辅助的话，那么可控性就会很差，这不能叫做重构。<br />
<img src ="http://www.blogjava.net/wjun530/aggbug/142879.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-05 13:49 <a href="http://www.blogjava.net/wjun530/archive/2007/09/05/142879.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Eclipse中使用JUnit</title><link>http://www.blogjava.net/wjun530/archive/2007/09/04/142766.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Tue, 04 Sep 2007 14:14:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/04/142766.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/142766.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/04/142766.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/142766.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/142766.html</trackback:ping><description><![CDATA[<font size="2"><font face="宋体">这篇文章将向你介绍Junit，一个用来在项目中进行测试和调试的工具。在介绍完TDD（以测试驱动开发）理论后，将进一步讲解怎样在流行的Eclipse中建立你自己的JUnit测试。向你展示如何测试Hello World这样简单的程序。</font></font>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 许多书上都讨论了自动测试，但是只有很少的著作注意到这么一个问题，那就是怎样把这些测试组织起来。随着测试的增加，放置和调用这些测试却变得更加麻烦。这将成为一个重要问题，以至于出现了TDD，极限编程（XP）使TDD得以普及。另外，你可以这样理解TDD：通过测试来开发。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><font size="2"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; </span><strong><span style="font-family: 宋体">TDD的主要规范：</span></strong></font></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 在编写程序代码之前，与之对应的自动测试必须被写好。甚至程序代码并不存在，那也要看见一个失败的测试结果。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 在测试通过后，副本代码必须被丢弃。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 有一个具体步骤（可能指的是《Extreme Programming》）可以被任何一个程序员来参考，而不需要特殊的其他方法。在我们开始写测试之前，这些步骤（章节）应该被首先阅读——怎样组织自动测试。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 讲解一下不同种类的测试：</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; <strong>单元测试：</strong>检测模块（也就是类）的正确性。如果对象需要访问外部的数据资源，例如数据库，就需要模拟一个mock objects，但在实际中真实数据与测试环境是不同的。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; <strong>客户测试：</strong>这是功能性、系统、和验收测试。用来测试整体的系统特性。在XP中，这些测试由用户编写。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; <strong>综合测试：</strong>介于用户测试和单元测试之间的桥梁。综合测试帮助测试应用程序的交互性。一般情况下，mock objects不被用于综合测试，它会增加测试时间。同样，综合测试经常依赖特殊的测试环境，例如数据库送来的测试数据。综合测试也需要用到外部类库。例如为J2EE应用程序进行综合测试的类库Cactus。解释这些测试超出了本文的范围，需要更加详细的信息请参考</font><a href="http://jakarta.apache.org/cactus/"><font size="2">http://jakarta.apache.org/cactus/</font></a><font size="2">。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; <strong>开发人员测试：</strong>这是用来让开发人员检验自己代码或新函数的。对于每一个开发人员，只要有可能，就需要有更多的测试来检验代码。组织这些测试和组织程序代码一样重要。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 在以下章节，只要提到&#8220;测试&#8221;，那就指的是开发人员测试。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp; &nbsp;</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 我们几乎准备好开始建立测试了，先应该为我们的测试选择名字。你也许会说，&#8220;这不是问题：把&#8216;Test&#8217;这个字放在类名前面，就好了！&#8221;不会这么快！让我来说一下这个步骤存在的问题：</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 在TDD中，被测试的类或者方法还不存在。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 一个测试能够覆盖多个方法，甚至多个类，这是可能的。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 以上只是一些普遍问题；还存在更多的问题。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 让我来提一个建议，在测试命名时：测试类的名字应该让人一眼就知道这是一个测试类，且能说明它要测试什么，注意是否和其他类重名。按照以上建议做，就很简单了，也不用担心名字太长或难听。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 即将在Eclipse中用JUnit工具创建我们第一个测试了。假设你已经下载了一个最新的Eclipse版本。如果还没有，你应该去官方站点http://www.eclipse.org下载。还需要JUnit，也可以从http://www.junit.org/下载。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 运行Eclipse。新建一个workplace项目，点击<strong>文件</strong>-&gt;<strong>新建</strong>-&gt;<strong>项目</strong>，选择Java项目，点击<strong>下一步</strong>。起一个项目名称，例如ProjectWithJUnit。点击<strong>完成</strong>。这样就完成新项目的建立了。再来配置一下Eclipse，在构建路径中添加JUnit类库。在工具条上点击<strong>项目</strong>-&gt;<strong>属性</strong>，选择<strong>Java构建路径</strong>，<strong>库</strong>，选择<strong>添加外部JAR</strong>，浏览Junit被存储的目录，选择junit.jar，点击<strong>打开</strong>。你将会看见JUnit出现在库的列表中。点击<strong>确定</strong>，让Eclipse重建路径。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 现在开发我们的&#8220;Hello World&#8221;例子。按照TDD的规则，应该在代码建立以前先把测试写好。为了能够在某出开始，我们假设未来的类名是HelloWorld，并且有一个方法Say()，这个方法返回String的值（例如&#8220;Hello World!&#8221;）。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 建立测试，在ProjectWithJUnit的标题上面点击右键，选择<strong>新建</strong>-&gt;<strong>其他</strong>，展开&#8220;Java&#8221;选项，选择<strong>JUnit</strong>。在右边的栏目对话框中选择测试案例，然后下一步。参考图1。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;<img alt="" hspace="0" src="http://www.51testing.com/ddimg/uploadimg/20051024/1728410.gif" align="baseline" border="0" /></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: 9pt; font-family: 宋体">图1. 在Eclipse中建立JUnit测试</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; </span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; <font size="2">在测试类这一栏中，写上将要被测试的类名HelloWorld。选择一个<strong>测试案例</strong>的名字，例如TestThatWeGetHelloWorldPrompt（是的，看上去很长，但是很清楚它的行为。）点击<strong>完成</strong>。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; TestThatWeGetHelloWorldPrompt的代码如下：</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; import junit.framework.TestCase;</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; public class TestThatWeGetHelloWorldPrompt</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; extends TestCase {</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public TestThatWeGetHelloWorldPrompt(</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String name) {</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(name);</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void testSay() {</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HelloWorld hi = new HelloWorld();</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertEquals("Hello World!", hi.say());</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void main(String[] args) {</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; junit.textui.TestRunner.run(</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TestThatWeGetHelloWorldPrompt.class);</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; }</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 代码并不复杂；只是有点与众不同。然而，让我们考察一下细节。我们继承了JUnit的TestCase类，它在JUnit的javadocs定义为&#8220;运行众多测试的夹具。&#8221;JUnit也有TestSuite类，它是一组测试案例的集合，但在本文中不做讨论。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 建立测试案例的步骤如下：</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 1、建立一个junit.framework.TestCase的实例。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 2、定义一些以&#8220;test&#8221;开头的无返回方法（例如testWasTransactionSuccessful()，testShow()，等等）。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; TestThatWeGetHelloWorldPrompt.java包含这些：TestCase的子类和一个叫做testSay()的方法。这个方法调用了assertEquals()函数，它用来比较我们预期的值和由say()返回的值。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; main()方法用来运行测试和显示输出的。JUnit的TestRunner处理测试，提供基于图像和文本的输出表现形式。我们使用基于文本的版本，因为Eclipse支持它，且也适合我们。当开始运行后，基于文本的版本测试会以文本形式输出，Eclipse会把这些输出自动变成图像界面的输出。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 按照TDD规范，首次运行测试，应该故意让它失败。点击<strong>运行</strong>-&gt;<strong>运行为</strong>-&gt;<strong>Junit测试</strong>（记住TestThatWeGetHelloWorldPrompt.java应该被突出的显示在包资源管理器中）。在左边窗口，应该看见JUnit窗口而不是<strong>包资源管理器</strong>，它显示一个红条，一次失败的测试，具体的失败原因参看图2。如果没有自动显示这些内容，点击<strong>JUnit</strong>标签（在底部的左边）。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<img alt="" hspace="0" src="http://www.51testing.com/ddimg/uploadimg/20051024/1728411.gif" align="baseline" border="0" /></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: 9pt; font-family: 宋体">图2. JUnit中失败的测试</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; </span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 很好！的却失败了。现在我们来建立被测试代码：在包资源管理器窗口的ProjectWithJUnit标题上右击，选择<strong>新建</strong>-&gt;<strong>类</strong>。选择类名，我们已经假设了它叫HelloWorld，然后直接点击<strong>完成</strong>。为HelloWorld.java填入下列代码：</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public class HelloWorld {</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public String say() {</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return("Hello World!");</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="color: #000066; font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体"><font size="2">&nbsp;&nbsp;&nbsp; 这段代码很简单，甚至不需要注解，我们再来看看结果。按照上面描述过的方式，在<strong>JUnit</strong>的窗口中显示了一个绿条，参看图3。绿条证明测试成功。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <img alt="" hspace="0" src="http://www.51testing.com/ddimg/uploadimg/20051024/1728412.gif" align="baseline" border="0" /></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: 9pt; font-family: 宋体">图3. JUnit中成功的测试</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp; <font size="2">现在，我们想再让测试失败一次，但原因不同。这有助于展示JUnit测试中不同的报错信息。修改assertEquals()代码，把&#8220;Hello World!&#8221;变成&#8220;Hello Me!&#8221;。当再次运行JUnit时，结果变成了红条，在<strong>JUnit</strong>窗口的底部输出了失败原因，参看图4。</font></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <img alt="" hspace="0" src="http://www.51testing.com/ddimg/uploadimg/20051024/1728413.gif" align="baseline" border="0" /></span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: 9pt; font-family: 宋体">图4. JUnit中的ComparisonError</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-family: 宋体">&nbsp;&nbsp; &nbsp;<font size="2">最后，我想说一下关于测试是开发过程中的必要部分的话题。测试代码一直是开发中的重要部分。经过近几年的发展，已得到了很大的提高，这要归功于强大的理论研究（比如&#8220;expectations-based development&#8221;等等），和快速发展的测试工具包，还有测试过程的改进。如果你对这篇文章感兴趣，那请你花一些时间来正式的学习一下测试理论吧，这对你的工作很有用。</font></span></p>
<img src ="http://www.blogjava.net/wjun530/aggbug/142766.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-04 22:14 <a href="http://www.blogjava.net/wjun530/archive/2007/09/04/142766.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>junit使用简明手册 </title><link>http://www.blogjava.net/wjun530/archive/2007/09/03/142468.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Mon, 03 Sep 2007 15:09:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/09/03/142468.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/142468.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/09/03/142468.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/142468.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/142468.html</trackback:ping><description><![CDATA[<div class="postText">用XP进行开发的过程，unit test是必不可少的环节。作为unit test，junit是首选的工具。本文从使用目的、如何使用、以及使用中需要考虑的问题，简略描述了junit的基本用法。<br />
<br />
使用目的<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;junit是java中书写unit test的framework，目前一些流行的unit test工具大都都是在junit上扩展而来的。目前它的版本是junit3.8.1，可以从www.junit.org上下载。<br />
<br />
用法<br />
1. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;基本使用步骤，Junit的使用非常简单，它的基本使用步骤：<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;创建，从junit.framework.TestCase派生unit test需要的test case<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;书写测试方法，提供类似于如下函数签名的测试方法：<br />
<br />
public void testXXXXX();<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编译，书写完test case后，编译所写的test case类<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;运行，启动junit test runner，来运行这个test case。<br />
<br />
Junit提供了2个基本的test runner：字符界面和图形界面。启动命令分别如下：<br />
<br />
a 图形界面：<br />
<br />
java junit.swingui.TestRunner XXXXX<br />
<br />
b 字符界面：<br />
<br />
java junit.textui.TestRunner XXXXX<br />
<br />
2. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用例子：<br />
<br />
import junit.frmework.TestCase;<br />
<br />
public class TestSample extends TestCaset{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void testMethod1(){<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertTrue( true);<br />
<br />
}<br />
<br />
}<br />
<br />
3. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setUp与tearDown，这两个函数是junit framework中提供初始化和反初始化每个测试方法的。setUp在每个测试方法调用前被调用，负责初始化测试方法所需要的测试环境；tearDown在每个测试方法被调用之后被调用，负责撤销测试环境。它们与测试方法的关系可以描述如下：<br />
<br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;测试开始 -&gt; setUp -&gt; testXXXX -&gt; tearDown -&gt;测试结束<br />
<br />
<br />
<br />
<br />
4. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用例子：<br />
<br />
import junit.frmework.TestCase;<br />
<br />
public class TestSample extends TestCaset{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;protected void setUp(){<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//初始化&#8230;&#8230;<br />
<br />
}<br />
<br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void testMethod1(){<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertTrue( true);<br />
<br />
}<br />
<br />
<br />
<br />
potected void tearDown(){<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//撤销初始化&#8230;&#8230;<br />
<br />
}<br />
<br />
}<br />
<br />
5. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;区分fail、exception。<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fail，期望出现的错误。产生原因：assert函数出错（如assertFalse(true)）；fail函数产生（如fail(&#8230;&#8230;)）。<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exception，不期望出现的错误，属于unit test程序运行时抛出的异常。它和普通代码运行过程中抛出的runtime异常属于一种类型。<br />
<br />
对于assert、fail等函数请参见junit的javadoc。<br />
<br />
6. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用例子：<br />
<br />
import junit.frmework.TestCase;<br />
<br />
public class TestSample extends TestCaset{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;protected void setUp(){<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//初始化&#8230;&#8230;<br />
<br />
}<br />
<br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void testMethod1(){<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;&#8230;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;boolean b= &#8230;&#8230;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertTrue( b);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new Exception( &#8220;This is a test.&#8221;);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fail( &#8220;Unable point.&#8221;); &nbsp;&nbsp;&nbsp;&nbsp;//不可能到达<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}catch(Exception e){<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fail( &#8220;Yes, I catch u&#8221;); //应该到达点<br />
<br />
}<br />
<br />
&#8230;&#8230;<br />
<br />
}<br />
<br />
<br />
<br />
potected void tearDown(){<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//撤销初始化&#8230;&#8230;<br />
<br />
}<br />
<br />
}<br />
<br />
7. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;组装TestSuite，运行更多的test。在junit中，Test、TestCase和TestSuite三者组成了composiste pattern。通过组装自己的TestSuite，可以完成对添加到这个TestSuite中的所有的TestCase的调用。而且这些定义的TestSuite还可以组装成更大的TestSuite，这样同时也方便了对于不断增加的TestCase的管理和维护。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;它的另一个好处就是，可以从这个TestCase树的任意一个节点（TestSuite或TestCase）开始调用，来完成这个节点以下的所有TestCase的调用。提高了unit test的灵活性。<br />
<br />
8. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用例子：<br />
<br />
import junit.framework.Test;<br />
<br />
import junit.framework.TestSuite;<br />
<br />
public class TestAll{<br />
<br />
public class TestAll{<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//定义一个suite，对于junit的作用可以视为类似于java应用程序的main。<br />
<br />
&nbsp;&nbsp;&nbsp;public static Test suite(){<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestSuite suite = new TestSuite("Running all tests.");<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;suite.addTestSuite( TestCase1.class);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;suite.addTestSuite( TestCase2.class);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return suite;<br />
<br />
&nbsp;&nbsp;&nbsp;}<br />
<br />
}<br />
<br />
运行同运行单独的一个TestCase是一样的，参见step 1 &#8220;运行&#8221;。<br />
<br />
9. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用Ant junit task。我们除了使用java来直接运行junit之外，我们还可以使用junit提供的junit task与ant结合来运行。涉及的几个主要的ant task如下：<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;junit&gt;，定义一个junit task<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;batchtest&gt;，位于&lt;junit&gt;中，运行多个TestCase<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;test&gt;，位于&lt;junit&gt;中，运行单个TestCase<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;formatter&gt;，位于&lt;junit&gt;中，定义一个测试结果输出格式<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;junitreport&gt;，定义一个junitreport task<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;report&gt;，位于&lt;junitreport&gt;中，输出一个junit report<br />
<br />
具体的语法请参见相关文档。<br />
<br />
10. &nbsp;&nbsp;使用例子：<br />
<br />
&lt;junit printsummary="yes" haltonfailure="no"&gt;<br />
<br />
&nbsp;&nbsp;&nbsp;&lt;classpath&gt;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;path refid="classpath"/&gt;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="${dist.junit}"/&gt;<br />
<br />
&nbsp;&nbsp;&nbsp;&lt;/classpath&gt;<br />
<br />
&nbsp;&nbsp;&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&lt;formatter type="brief" usefile="false"/&gt;<br />
<br />
&nbsp;&nbsp;&nbsp;&lt;formatter type="xml"/&gt;<br />
<br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&lt;batchtest todir="${doc.junitReport}"&gt;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;fileset dir="${dist.junit}" includes="**/*Test.class" /&gt;<br />
<br />
&nbsp;&nbsp;&nbsp;&lt;/batchtest&gt;<br />
<br />
&lt;/junit&gt;<br />
<br />
<br />
<br />
&lt;junitreport todir="${doc.junitReport}"&gt;<br />
<br />
&nbsp;&nbsp;&nbsp;&lt;fileset dir="${doc.junitReport}"&gt;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;include name="TEST*-*.xml"/&gt;<br />
<br />
&nbsp;&nbsp;&nbsp;&lt;/fileset&gt;<br />
<br />
&nbsp;&nbsp;&nbsp;&lt;report format="frames" styledir="${junit.styleDir}" todir="${doc.junitReport}"/&gt;<br />
<br />
&lt;/junitreport&gt;<br />
<br />
检查表<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;junit的使用并不很难，然而要书写一个好的TestCase却并非易事。一个不好的TestCase往往是既浪费了时间，也起不了实际的作用。相反，一个好的TestCase，不仅可以很好的指出代码中存在的问题，而且也可以作为代码更准确的文档，同时还在持续集成的过程中起非常重要的作用。在此给出书写TestCase时需要注意的几点：<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;测试的独立性：一次只测试一个对象，方便定位出错的位置。这有2层意思：一个TestCase，只测试一个对象；一个TestMethod，只测试这个对象中的一个方法。<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;给测试方法一个合适的名字。<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在assert函数中给出失败的原因，如：assertTrue( &#8220;&#8230; should be true&#8221;, &nbsp;&#8230;&#8230;)，方便查错。在这个例子中，如果无法通过assertTrue，那么给出的消息将被显示。在junit中每个assert函数都有第一个参数是出错时显示消息的函数原型。<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;测试所有可能引起失败的地方，如：一个类中频繁改动的函数。对于那些仅仅只含有getter/setter的类，如果是由IDE（如Eclipse）产生的，则可不测；如果是人工写，那么最好测试一下。<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在setUp和tearDown中的代码不应该是与测试方法相关的，而应该是全局相关的。如针对与测试方法A和B，在setUp和tearDown中的代码应该是A和B都需要的代码。<br />
<br />
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;测试代码的组织：相同的包，不同的目录。这样，测试代码可以访问被测试类的protected变量/方法，方便测试代码的编写。放在不同的目录，则方便了测试代码的管理以及代码的打包和发布。一个例子如下：<br />
<br />
src &nbsp;&nbsp;&lt;=源代码根目录<br />
<br />
<img style="vertical-align: middle" height="19" src="http://www.uml.org.cn/j2ee/images/016.gif" width="19" border="0"  alt="" />-com<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<img style="vertical-align: middle" height="19" src="http://www.uml.org.cn/j2ee/images/016.gif" width="19" border="0"  alt="" />-mod1<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img style="vertical-align: middle" height="19" src="http://www.uml.org.cn/j2ee/images/016.gif" width="19" border="0"  alt="" />-class1<br />
<br />
junit &nbsp;&nbsp;&lt;=测试代码根目录<br />
<br />
<img style="vertical-align: middle" height="19" src="http://www.uml.org.cn/j2ee/images/016.gif" width="19" border="0"  alt="" />-com<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<img style="vertical-align: middle" height="19" src="http://www.uml.org.cn/j2ee/images/016.gif" width="19" border="0"  alt="" />-mod1<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img style="vertical-align: middle" height="19" src="http://www.uml.org.cn/j2ee/images/016.gif" width="19" border="0"  alt="" />-class1 <br />
</div>
 <img src ="http://www.blogjava.net/wjun530/aggbug/142468.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-09-03 23:09 <a href="http://www.blogjava.net/wjun530/archive/2007/09/03/142468.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java程序员需要了解的几个开源协议介绍</title><link>http://www.blogjava.net/wjun530/archive/2007/08/31/141827.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Fri, 31 Aug 2007 13:01:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/08/31/141827.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/141827.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/08/31/141827.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/141827.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/141827.html</trackback:ping><description><![CDATA[介绍几个比较常见的开源协议： <br />
<br />
Mozilla Public License <br />
<br />
MPL License，允许免费重发布、免费修改，但要求修改后的代码版权归软件的发起者。这种授权维护了商业软件的利益，，它要求基于这种软件得修改无偿贡献版权给该软件。这样，围绕该软件得所有代码得版权都集中在发起开发人得手中。但MPL是允许修改，无偿使用得。MPL软件对链接没有要求。 <br />
<br />
SD开源协议 <br />
<br />
BSD开源协议是一个给于使用者很大自由的协议。可以自由的使用，修改源代码，也可以将修改后的代码作为开源或者专有软件再发布。 当你发布使用了BSD协议的代码，或则以BSD协议代码为基础做二次开发自己的产品时，需要满足三个条件： <br />
<br />
1. 如果再发布的产品中包含源代码，则在源代码中必须带有原来代码中的BSD协议。 <br />
<br />
2. 如果再发布的只是二进制类库/软件，则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议。 <br />
<br />
3. 不可以用开源代码的作者/机构名字和原来产品的名字做市场推广。 <br />
<br />
BSD代码鼓励代码共享，但需要尊重代码作者的著作权。BSD由于允许使用者修改和重新发布代码，也允许使用或在BSD代码上开发商业软件发布和销售，因此是对商业集成很友好的协议。而很多的公司企业在选用开源产品的时候都首选BSD协议，因为可以完全控制这些第三方的代码，在必要的时候可以修改或者二次开发。 <br />
<br />
Apache Licence 2.0 <br />
<br />
Apache Licence是著名的非盈利开源组织Apache采用的协议。该协议和BSD类似，同样鼓励代码共享和尊重原作者的著作权，同样允许代码修改，再发布(作为开源或商业软件)。需要满足的条件： <br />
<br />
1. 需要给代码的用户一份Apache Licence <br />
<br />
2. 如果你修改了代码，需要再被修改的文件中说明。 <br />
<br />
3. 在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议，商标，专利声明和其他原来作者规定需要包含的说明。 <br />
<br />
4. 如果再发布的产品中包含一个Notice文件，则在Notice文件中需要带有Apache Licence。你可以在Notice中增加自己的许可，但不可以表现为对Apache Licence构成更改。 <br />
<br />
Apache Licence也是对商业应用友好的许可。使用者也可以在需要的时候修改代码来满足需要并作为开源或商业产品发布/销售。 <br />
<br />
GPL <br />
<br />
GPL许可证是自由软件的应用最广泛的软件许可证，人们可以修改程式的一个或几个副本或程式的任何部分，以此形成基於这些程式的衍生作品。必须在修改过的档案中附有明显的说明：您修改了此一档案及任何修改的日期。 您必须让您发布或出版的作品，包括本程式的全部或一部分，或内含本程式的全部或部分所衍生的作品，允许第三方在此许可证条款下使用，并且不得因为此项授权行为而收费。 <br />
<br />
LGPL <br />
<br />
Linux就是采用了GPL。GPL协议和BSD, Apache Licence等鼓励代码重用的许可很不一样。GPL的出发点是代码的开源/免费使用和引用/修改/衍生代码的开源/免费使用，但不允许修改后和衍生的代码做为闭源的商业软件发布和销售。这也就是为什么我们能用免费的各种linux，包括商业公司的linux和linux上各种各样的由个人，组织，以及商业软件公司开发的免费软件了。 <br />
<br />
GPL协议的主要内容是只要在一个软件中使用(&#8220;使用&#8221;指类库引用，修改后的代码或者衍生代码)GPL协议的产品，则该软件产品必须也采用GPL协议，既必须也是开源和免费。这就是所谓的&#8221;传染性&#8221;。GPL协议的产品作为一个单独的产品使用没有任何问题，还可以享受免费的优势。 <br />
<br />
由于GPL严格要求使用了GPL类库的软件产品必须使用GPL协议，对于使用GPL协议的开源代码，商业软件或者对代码有保密要求的部门就不适合集成/采用作为类库和二次开发的基础。 <br />
<br />
其它细节如再发布的时候需要伴随GPL协议等和BSD/Apache等类似 <br />
<br />
Public Domain <br />
<br />
公共域授权。将软件授权为公共域，这些软件包没有授权协议，任何人都可以随意使用它。 <br />
<br />
Artistic许可 <br />
<br />
使作者保持对进一步开发的控制。
<img src ="http://www.blogjava.net/wjun530/aggbug/141827.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-08-31 21:01 <a href="http://www.blogjava.net/wjun530/archive/2007/08/31/141827.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>程序员 :超越软件蓝领的七种武器</title><link>http://www.blogjava.net/wjun530/archive/2007/08/28/140252.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Mon, 27 Aug 2007 23:59:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/08/28/140252.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/140252.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/08/28/140252.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/140252.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/140252.html</trackback:ping><description><![CDATA[<font face=Arial>信息技术的发展时间虽然不长，但其爆炸式的发展速度使信息技术迅速覆盖社会和人类生活的各个角落。程序员们是这场信息化浪潮的见证者之一，更是其中的主要参与者，这是时代赋予每个程序员的机会和责任。&nbsp;<br><br>信息技术的更新速度是惊人的，程序员的职业生涯则是一个要求不断学习的过程，永远不能固步自封。本人在工作期间曾看见过很多程序员只要有闲暇时间就浏览一些没有太大作用的网页，在网上聊天，打游戏，浪费了大量的时间，十分不可取。而另外一种情况是，IT技术的日新月异使很多程序员眼花缭乱，什么都想学，却又不知从何学起，今天看看这个，明天学学那个，贪多不熟。&nbsp;<br><br>虽然IT技术发展迅速，但很多技术都是有规律可循，一些基本的概念、原理和方法还很通用，可以举一反三。本人根据自己的体会和经验，向那些刚刚踏入IT行业的新程序员们或正在迷茫的程序员们推荐程序员必须掌握的七种武器，有了这七种武器，虽不敢说笑傲江湖，但将自己立于不败之地还是可以的。&nbsp;<br><br>第一种武器：开发工具&nbsp;<br><br>至少熟练掌握两到三种开发工具的使用，这是程序员的立身之本，其中C/C++和JAVA是我重点推荐的开发工具，C/C++以其高效率和高度的灵活性成为开发工具中的利器，很多系统级的软件还是用C/C++编写。而JAVA的跨平台和与WEB很好的结合是JAVA的优势所在，而本人对SUN公司的&#8220;网络即计算机&#8221;的概念相当欣赏，并相信JAVA即其相关的技术集JAVA&nbsp;One会成为未来的主流开发工具之一。其次，如果能掌握一种简便的可视化开发工具，如VB，PowerBuilder，Delphi，C++&nbsp;Builder，则更好，这些开发工具减小了开发难度，并能够强化程序员对象模型的概念。另外，需要掌握基本的脚本语言，如shell,perl等，至少能读懂这些脚本代码。&nbsp;<br><br>第二种武器：数据库&nbsp;<br><br>为什么数据库是如此重要？很多应用程序都是以数据库的数据为中心，而数据库的产品也有不少，其中关系型数据库仍是主流形式，所以程序员至少熟练掌握一两种数据库，对关系型数据库的关键元素要非常清楚，要熟练掌握SQL的基本语法。虽然很多数据库产品提供了可视化的数据库管理工具，但SQL是基础，是通用的数据库操作方法。如果没有机会接触商业数据库系统，可以使用免费的数据库产品是一个不错的选择，如mySQL,&nbsp;Postgres等。&nbsp;<br><br>第三种武器：操作系统&nbsp;<br><br>当前主流的操作系统是Windows，Linux/Unix，熟练地使用这些操作系统是必须的，但只有这些还远远不够。要想成为一个真正的编程高手，需要深入了解操作系统，了解它的内存管理机制、进程/线程调度、信号、内核对象、系统调用、协议栈实现等。Linux作为开发源码的操作系统，是一个很好的学习平台，Linux几乎具备了所有现代操作系统的特征。虽然Windows系统的内核实现机制的资料较少，但通过互联网还是能获取不少资料。只有对操作系统有一定的了解后，你会发现自己上了一个新的台阶。&nbsp;<br><br>第四种武器：网络协议TCP/IP&nbsp;<br><br>在互联网如此普及的今天，如果您还没有对互联网的支撑协议TCP/IP协议栈有很好的掌握，就需要迅速补上这一课，网络技术已改变了软件运行的模式，从最早的客户/服务器结构，到今天的WEB&nbsp;Services，再到未来的网格计算，这一切都离不开以TCP/IP协议栈为基础的网络协议支持，所以，深入掌握TCP/IP协议是非常必要的。至少，你需要了解ISO七层协议模型，IP/UDP/TCP/HTTP等常用协议的原理和三次握手机制。&nbsp;<br><br>第五种武器：DCOM/CORBA/XML/WEB&nbsp;Services&nbsp;<br><br>随着技术的发展，软件与网络的无缝结合是必然趋势，软件系统的位置无关性是未来计算模式的重要特征之一，DCOM/CORBA是当前两大主流的分布计算的中间件平台，DCOM是微软COM(组件对象模型)的扩展，而CORBA是OMG支持的规范。程序员需要做的不仅仅是利用商业的开发平台来开发软件，而是要理解这些技术的初衷，即为什么需要这项技术，如果你能理解了这一点，再回头看这些技术的具体实现，就如庖丁解牛，迎刃而解。XML/WebServices重要性不言而喻，XML以其结构化的表示方法和超强的表达能力被喻为互联网上的&#8220;世界语&#8221;，是分布计算的基石之一。&nbsp;<br><br>第六种武器：软件工程与CMM&nbsp;<br><br>现代大型软件系统的开发中，工程化的开发控制取代个人英雄主义，成为软件系统成功的保证，一个编程高手并不一定是一个优秀的程序员，一个优秀的程序员是将出色的编程能力和开发技巧同严格的软件工程思想有机结合，编程只是软件生命周期中的其中一环，优秀的程序员应该掌握软件开发各个阶段的基本技能，如市场分析，可行性分析，需求分析，结构设计，详细设计，软件测试等。一句话可以概括我的看法：&#8220;创意无限，流程保证&#8221;。&nbsp;<br><br>第七种武器：强烈的好奇心&nbsp;<br><br>什么才是一个程序员的终极武器呢，那就是强烈的好奇心和学习精神。没有比强烈的好奇心和学习精神更好的武器了，它是程序员们永攀高峰的源泉和动力所在。&nbsp;<br><br>其实学好一种就够用了。</font>
<img src ="http://www.blogjava.net/wjun530/aggbug/140252.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-08-28 07:59 <a href="http://www.blogjava.net/wjun530/archive/2007/08/28/140252.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>掌握JDK1.5枚举类型</title><link>http://www.blogjava.net/wjun530/archive/2007/08/28/140251.html</link><dc:creator>王君</dc:creator><author>王君</author><pubDate>Mon, 27 Aug 2007 23:56:00 GMT</pubDate><guid>http://www.blogjava.net/wjun530/archive/2007/08/28/140251.html</guid><wfw:comment>http://www.blogjava.net/wjun530/comments/140251.html</wfw:comment><comments>http://www.blogjava.net/wjun530/archive/2007/08/28/140251.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wjun530/comments/commentRss/140251.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wjun530/services/trackbacks/140251.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;引自:<a class=l2 href="http:///" target=_blank><u><font color=#0000ff>http://www.it.com.cn/f/edu/053/29/93843.htm</font></u></a><br>　　Enum作为Sun全新引进的一个关键字，看起来很象是特殊的class,&nbsp;它也可以有自己的变量，可以定义自己的方法，可以实现一个或者多个接口。&nbsp;当我们在声明一个enum类型时，我们应该注意到enum类型有如下的一些特征。&nbsp;<br><br>　　1．它不能有public的构造函数，这样做可以保证客户代码没有办法新建一个enum的实例。&nbsp;<br><br>　　2．所有枚举值都是public&nbsp;,&nbsp;static&nbsp;,&nbsp;final的。注意这一点只是针对于枚举值，我们可以和在普通类里面定义&nbsp;变量一样定义其它任何类型的非枚举变量，这些变量可以用任何你想用的修饰符。&nbsp;<br><br>　　3．Enum默认实现了java.lang.Comparable接口。&nbsp;<br><br>　　4．Enum覆载了了toString方法，因此我们如果调用Color.Blue.toString()默认返回字符串&#8221;Blue&#8221;.&nbsp;<br><br>　　5．Enum提供了一个valueOf方法，这个方法和toString方法是相对应的。调用valueOf(&#8220;Blue&#8221;)将返回Color.Blue.因此我们在自己重写toString方法的时候就要注意到这一点，一把来说应该相对应地重写valueOf方法。&nbsp;<br><br>　　6．Enum还提供了values方法，这个方法使你能够方便的遍历所有的枚举值。&nbsp;<br><br>　　7．Enum还有一个oridinal的方法，这个方法返回枚举值在枚举类种的顺序，这个顺序根据枚举值声明的顺序而定，这里Color.Red.ordinal()返回0。&nbsp;<br><br>　　了解了这些基本特性，我们来看看如何使用它们。&nbsp;<br><br>　　1．遍历所有有枚举值.&nbsp;知道了有values方法，我们可以轻车熟路地用ForEach循环来遍历了枚举值了。&nbsp;<br><br><strong>　　for&nbsp;(Color&nbsp;c:&nbsp;Color.values())&nbsp;<br>　　System.out.println(&#8220;find&nbsp;value:&#8221;&nbsp;+&nbsp;c);&nbsp;</strong><br><br>　　2．在enum中定义方法和变量，比如我们可以为Color增加一个方法随机返回一个颜色。&nbsp;<br><br>　　<strong>public&nbsp;enum&nbsp;Color&nbsp;{&nbsp;<br>　　Red,&nbsp;<br>　　Green,&nbsp;<br>　　Blue;&nbsp;<br><br>　　/*&nbsp;<br>　　*定义一个变量表示枚举值的数目。&nbsp;<br>　　*(我有点奇怪为什么sun没有给enum直接提供一个size方法).&nbsp;<br>　　*/&nbsp;<br>　　private&nbsp;static&nbsp;int&nbsp;number&nbsp;=&nbsp;Color.values().length&nbsp;;&nbsp;<br><br>　　/**&nbsp;<br>　　*&nbsp;随机返回一个枚举值&nbsp;<br>　　@return&nbsp;a&nbsp;random&nbsp;enum&nbsp;value.&nbsp;<br>　　*/&nbsp;<br>　　public&nbsp;static&nbsp;Color&nbsp;getRandomColor(){&nbsp;<br>　　long&nbsp;random&nbsp;=&nbsp;System.currentTimeMillis()&nbsp;%&nbsp;number;&nbsp;<br>　　switch&nbsp;((int)&nbsp;random){&nbsp;<br>　　　case&nbsp;0:&nbsp;<br>　　　　return&nbsp;Color.Red;&nbsp;<br>　　　case&nbsp;1:&nbsp;<br>　　　　return&nbsp;Color.Green;&nbsp;<br>　　　case&nbsp;2:&nbsp;<br>　　　　return&nbsp;Color.Blue;&nbsp;<br>　　　default&nbsp;:&nbsp;return&nbsp;Color.Red;&nbsp;<br>　　}&nbsp;<br>　　}&nbsp;<br>　　}</strong>&nbsp;<br><br>　　可以看出这在枚举类型里定义变量和方法和在普通类里面定义方法和变量没有什么区别。唯一要注意的只是变量和方法定义必须放在所有枚举值定义的后面，否则编译器会给出一个错误。&nbsp;<br><br>　　3．覆载(Override)toString,&nbsp;valueOf方法&nbsp;<br><br>　　前面我们已经知道enum提供了toString,valueOf等方法，很多时候我们都需要覆载默认的toString方法，那么对于enum我们怎么做呢。其实这和覆载一个普通class的toString方法没有什么区别。&nbsp;<br><br>　　<strong>&#8230;.&nbsp;<br>　　public&nbsp;String&nbsp;toString(){&nbsp;<br>　　switch&nbsp;(this){&nbsp;<br>　　case&nbsp;Red:&nbsp;<br>　　　return&nbsp;"Color.Red";&nbsp;<br>　　case&nbsp;Green:&nbsp;<br>　　　return&nbsp;"Color.Green";&nbsp;<br>　　case&nbsp;Blue:&nbsp;<br>　　　return&nbsp;"Color.Blue";&nbsp;<br>　　default:&nbsp;<br>　　　return&nbsp;"Unknow&nbsp;Color";&nbsp;<br>　　}&nbsp;<br>　　}&nbsp;<br>　　&#8230;.&nbsp;</strong><br>　　这时我们可以看到，此时再用前面的遍历代码打印出来的是&nbsp;<br><br>　　Color.Red&nbsp;<br>　　Color.Green&nbsp;<br>　　Color.Blue&nbsp;<br><br>　　而不是&nbsp;<br><br>　　Red&nbsp;<br>　　Green&nbsp;<br>　　Blue.&nbsp;<br><br>　　可以看到toString确实是被覆载了。一般来说在覆载toString的时候我们同时也应该覆载valueOf方法，以保持它们相互的一致性。&nbsp;<br><br>　　4．使用构造函数&nbsp;<br><br>　　虽然enum不可以有public的构造函数，但是我们还是可以定义private的构造函数，在enum内部使用。还是用Color这个例子。&nbsp;<br><br>　　<strong>public&nbsp;enum&nbsp;Color&nbsp;{&nbsp;<br>　　Red("This&nbsp;is&nbsp;Red"),&nbsp;<br>　　Green("This&nbsp;is&nbsp;Green"),&nbsp;<br>　　Blue("This&nbsp;is&nbsp;Blue");&nbsp;<br><br>　　private&nbsp;String&nbsp;desc;&nbsp;<br><br>　　Color(String&nbsp;desc){&nbsp;<br>　　this.desc&nbsp;=&nbsp;desc;&nbsp;<br>　　}&nbsp;<br><br>　　public&nbsp;String&nbsp;getDesc(){&nbsp;<br>　　return&nbsp;this.desc;&nbsp;<br>　　}&nbsp;<br><br>　　}&nbsp;</strong><br><br>　　这里我们为每一个颜色提供了一个说明信息,&nbsp;然后定义了一个构造函数接受这个说明信息。&nbsp;<br><br>　　要注意这里构造函数不能为public或者protected,&nbsp;从而保证构造函数只能在内部使用，客户代码不能new一个枚举值的实例出来。这也是完全符合情理的，因为我们知道枚举值是public&nbsp;static&nbsp;final的常量而已。&nbsp;<br><br>　　5．实现特定的接口&nbsp;<br><br>　　我们已经知道enum可以定义变量和方法，它要实现一个接口也和普通class实现一个接口一样，这里就不作示例了。&nbsp;<br><br>　　6．定义枚举值自己的方法。&nbsp;<br><br>　　前面我们看到可以为enum定义一些方法，其实我们甚至可以为每一个枚举值定义方法。这样，我们前面覆载&nbsp;toString的例子可以被改写成这样。&nbsp;<br><br>　　<strong>public&nbsp;enum&nbsp;Color&nbsp;{&nbsp;<br>　　Red&nbsp;{&nbsp;<br>　　public&nbsp;String&nbsp;toString(){&nbsp;<br>　　　return&nbsp;"Color.Red";&nbsp;<br>　　}&nbsp;<br>　　},&nbsp;<br>　　Green&nbsp;{&nbsp;<br>　　public&nbsp;String&nbsp;toString(){&nbsp;<br>　　　return&nbsp;"Color.Green";&nbsp;<br>　　}&nbsp;<br>　　},&nbsp;<br>　　Blue{&nbsp;<br>　　public&nbsp;String&nbsp;toString(){&nbsp;<br>　　　return&nbsp;"Color.Blue";&nbsp;<br>　　}&nbsp;<br>　　};&nbsp;<br>　　}&nbsp;</strong><br><br>　　从逻辑上来说这样比原先提供一个&#8220;全局&#8220;的toString方法要清晰一些。&nbsp;<br><br>　　总的来说，enum作为一个全新定义的类型，是希望能够帮助程序员写出的代码更加简单易懂，个人觉得一般也不需要过多的使用enum的一些高级特性，否则就和简单易懂的初衷想违背了。
<img src ="http://www.blogjava.net/wjun530/aggbug/140251.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wjun530/" target="_blank">王君</a> 2007-08-28 07:56 <a href="http://www.blogjava.net/wjun530/archive/2007/08/28/140251.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>