﻿<?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>Tue, 02 Dec 2008 04:21:18 GMT</lastBuildDate><pubDate>Tue, 02 Dec 2008 04:21:18 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>0</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>1</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 