﻿<?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-思想比知识更重要 成长比成功更重要-随笔分类-J2SE</title><link>http://www.blogjava.net/renyangok/category/17021.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 23 Jan 2008 12:00:56 GMT</lastBuildDate><pubDate>Wed, 23 Jan 2008 12:00:56 GMT</pubDate><ttl>60</ttl><item><title>方法和函数的区别（method &amp; function）</title><link>http://www.blogjava.net/renyangok/archive/2007/11/02/157759.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Fri, 02 Nov 2007 07:26:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/11/02/157759.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/157759.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/11/02/157759.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/157759.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/157759.html</trackback:ping><description><![CDATA[Not a whole lot.<br />
<br />
I believe the technical difference is that a function is not in a class, whereas a method is.<br />
<br />
You'll see method and member function used interchangeably which is
fine (a function that is a member of a class, get it? Correct term when
discussing C++). Function and method shouldn't be used interchangeably,
but often will be.<br />
<br />
I'd suggest you don't sweat it, they both represent blocks of code that do something and might return a value.
<img src ="http://www.blogjava.net/renyangok/aggbug/157759.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-11-02 15:26 <a href="http://www.blogjava.net/renyangok/archive/2007/11/02/157759.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDK1.4引入的新特性之一--断言（assert）</title><link>http://www.blogjava.net/renyangok/archive/2007/10/12/152316.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Fri, 12 Oct 2007 05:16:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/10/12/152316.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/152316.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/10/12/152316.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/152316.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/152316.html</trackback:ping><description><![CDATA[<div><span style="font-size: 10pt; color: #9400d3; font-family: 宋体;"><span style="font-size: 10pt; color: #9400d3; font-family: 宋体;">断言概述</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">编写代码时，我们总是会做出一些假设，断言就是用于在代码中捕捉这些假设<span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
可以将断言看作是异常处理的一种高级形式</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">断言表示为一些布尔表达式，程序员相信在程序中的某个特定点该表达式值为真</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">可以在任何时候启用和禁用断言验证，因此可以在测试时启用断言而在部署时禁用断言。同样，程序投入运行后，最终用户在遇到问题时可以重新起用断言。</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">使用断言可以创建更稳定，品质更好且易于除错的代码</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
</span><span style="color: #9370db;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">当需要在一个值为</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">FALSE</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">时中断当前操作的话，可以使用断言</span></span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
</span><span style="color: #9370db;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">单元测试必须使用断言（</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">Junit/JunitX</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">）</span></span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">除了类型检查和单元测试外，断言还提供了一种确定个种特性是否在程序中得到维护的极好的方法</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">使用断言使我们向按契约式设计更近了一步</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
<br />
</span><span style="font-size: 10pt; color: #9400d3; font-family: 宋体;">常见的断言特性</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">前置条件断言：代码执行之前必须具备的特性</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">后置条件断言：代码执行之后必须具备的特性</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">前后不变断言：代码执行前后不能变化的特性</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
<br />
</span><span style="font-size: 10pt; color: #9400d3; font-family: 宋体;">断言使用方式</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">断言可以有两种形式</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<span style="color: #9370db;">1.assert Expression1 <br />
2.assert Expression1:Expression2 <br />
</span></span><span style="color: #9370db;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">其中</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">Expression1</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">应该总是一个布尔值，</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">Expression2</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">是断言失败时输出的失败消息的字符串。如果</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">Expression1</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">为假，则抛出一个</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"> AssertionError</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">，这是一个错误，而不是一个异常，也就是说是一个不可控制异常（</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">unchecked Exception),AssertionError</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">由于是错误，所以可以不捕获，但不推荐这样做，因为那样会使你的系统进入不稳定状态。</span></span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
<br />
</span><span style="font-size: 10pt; color: #9400d3; font-family: 宋体;">起用断言</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
</span><span style="color: #9370db;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">断言在默认情况下是关闭的，要在编译时启用断言，需要使用</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">source1.4</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">标记</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">既</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">javac source1.4 Test.java ,</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">在运行时启用断言需要使用</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"> -ea</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">参数</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">。要在系统类中启用和禁用断言可以使用</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"> -esa </span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">和</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"> -dsa</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">参数。</span></span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">例如：</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<span style="color: #9370db;">public &gt;&nbsp; public AssertExampleOne(){} <br />
&nbsp; public static void main(String args[]){ <br />
&nbsp;&nbsp;&nbsp; int x=10; <br />
&nbsp;&nbsp;&nbsp; System.out.println("Testing Assertion that x==100"); <br />
&nbsp;&nbsp;&nbsp; assert x=100;"Out assertion failed!"; <br />
&nbsp;&nbsp;&nbsp; System.out.println("Test passed!"); <br />
&nbsp; } <br />
} <br />
<br />
</span></span><span style="color: #9370db;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">如果编译时未加</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"> -source1.4,</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">则编译通不过</span></span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
</span><span style="color: #9370db;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">在执行时未加</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"> -ea </span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">时输出为</span></span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<span style="color: #9370db;">Testing Assertion that x==100 <br />
Test passed <br />
jre</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">忽略了断言的就代码，而使用了该参数就会输出为</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<span style="color: #9370db;">Testing Assertion that x==100 <br />
Exception in thread "main" java.lang.AssertionError: Out assertion failed! <br />
at AssertExampleOne.main(AssertExampleOne.java:6) <br />
<br />
<br />
</span></span><span style="font-size: 10pt; color: #9400d3; font-family: 宋体;">断言的副作用</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
</span><span style="color: #9370db;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">由于程序员的问题，断言的使用可能会带来副作用</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">，例如：</span></span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<span style="color: #9370db;">boolean isEnable=false; <br />
//... <br />
assert isEnable=true; <br />
</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">这个断言的副作用是因为它修改了程序中变量的值并且未抛出错误，这样的错误如果不细心的检查是很难发现的。但是同时我们可以根据以上的副作用得到一个有用的特性，根据它来测试断言是否打开。</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
<span style="color: #9370db;">public &gt;<br />
&nbsp; public static void main(String args[]){ <br />
&nbsp;&nbsp;&nbsp; boolean isEnable=false; <br />
&nbsp;&nbsp;&nbsp; //... <br />
&nbsp;&nbsp;&nbsp; assert isEnable=true; <br />
&nbsp;&nbsp;&nbsp; if(isEnable==false){ <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new RuntimeException("Assertion shoule be enable!"); <br />
&nbsp;&nbsp;&nbsp; } <br />
&nbsp; } <br />
} <br />
<br />
<br />
</span></span><span style="font-size: 10pt; color: #9400d3; font-family: 宋体;">何时需要使用断言</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
<span style="color: #9370db;">1.</span></span><span style="color: #9370db;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">可以在预计正常情况下程序不会到达的地方放置断言</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">：</span></span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert false <br />
2.</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">断言可以用于检查传递给私有方法的参数。（对于公有方法，因为是提供给外部的接口，所以必须在方法中有相应的参数检验才能保证代码的健壮性）</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<span style="color: #9370db;">3.</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">使用断言测试方法执行的前置条件和后置条件</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<span style="color: #9370db;">4.</span></span><span style="color: #9370db;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">使用断言检查类的不变状态，确保任何情况下，某个变量的状态必须满足。（如</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">age</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">属性应大于</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">0</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">小于某个合适值）</span></span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
<br />
</span><span style="font-size: 10pt; color: #9400d3; font-family: 宋体;">什么地方不要使用断言</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">断言语句不是永远会执行，可以屏蔽也可以启用</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">因此：</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<span style="color: #9370db;">1.</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">不要使用断言作为公共方法的参数检查，公共方法的参数永远都要执行</span></span><span style="font-size: 10pt; color: #9400d3; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<span style="color: #9370db;">2.</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">断言语句不可以有任何边界效应，不要使用断言语句去修改变量和改变方法的返回值</span><br />
<br />
下边是介绍断言的用法</span><span style="font-size: 10pt; color: #9400d3; font-family: 宋体;"><span style="font-size: 10pt; color: #9400d3; font-family: 宋体;">:</span></span><br />
<br />
assert<span style="font-size: 10pt; color: #9370db; font-family: 宋体;">是在</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">J2SE1.4</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">中引入的新特性，</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assertion</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">就是在代码中包括的布尔型状态，程序员认为这个状态是</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">true</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">。一般来说</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">在开发的时候是检查程序的安全性的，在发布的时候通常都不使用</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">。在</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">1.4</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">中添加了</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">关键字和</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">java.lang.AssertError</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">类的支持。</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">首先，我们有必要从一个例子说起</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert <br />
<br />
public &gt;&nbsp; public static void main(String[] args) { <br />
&nbsp;&nbsp;&nbsp; AssertTest at = new AssertTest(); <br />
&nbsp;&nbsp;&nbsp; at.assertMe(true); <br />
&nbsp;&nbsp;&nbsp; at.assertMe(false); <br />
&nbsp; }&nbsp;<br />
&nbsp; private void assertMe(boolean boo) { <br />
&nbsp;&nbsp;&nbsp; assert boo?true:false; <br />
&nbsp;&nbsp;&nbsp; System.out.println("true condition"); <br />
&nbsp; } <br />
} <br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">程序中包含了</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">的话，你要用</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">javac -source 1.4 xxx.java</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">来编译，否则编译器会报错的。要想让</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">得部分运行的话，要使用</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">java -ea xxx</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">来运行，否则包含</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">得行会被忽略。下面我们运行</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<span style="color: #9370db;">javac -source 1.4 AssertTest.java <br />
java -ea AssertTest <br />
</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">看看结果的输出是：</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
<span style="color: #9370db;">true condition <br />
Exception in thread "main" java.lang.AssertionError <br />
at AssertTest.assertMe(AssertTest.java:13) <br />
at AssertTest.main(AssertTest.java:7) <br />
<br />
</span></span><span style="color: #9370db;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">当我们运行</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">at.assertMe(true)</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">得时候，由于</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert boo?true:false</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">相当于</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"> assert true;</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">因此没有任何问题，程序往下执行打印出</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">true condition</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">，但是执行</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">at.assertMe(false)</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">的时候相当于</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert false</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">，这个时候解释器就会抛出</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">AssertionError</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">了，程序就终止了。大家必须清楚</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">AssertionError</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">是继承自</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">Error</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">得，因此你可以不再程序中</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">catch</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">它的，当然你也可以在程序中</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">catch</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">它然后程序可以继续执行。例如：</span></span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<span style="color: #9370db;">public &gt;&nbsp; public static void main(String[] args) { <br />
&nbsp;&nbsp;&nbsp; AssertTest at = new AssertTest(); <br />
&nbsp;&nbsp;&nbsp; try { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; at.assertMe(true); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; at.assertMe(false); <br />
&nbsp;&nbsp;&nbsp; } catch(AssertionError ae) { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("AsseriontError catched"); <br />
&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp; System.out.println("go on"); <br />
&nbsp; } <br />
&nbsp; private void assertMe(boolean boo) { <br />
&nbsp;&nbsp;&nbsp; assert boo?true:false; <br />
&nbsp;&nbsp;&nbsp; System.out.println("true condition"); <br />
&nbsp; } <br />
} <br />
<br />
assert</span></span><span style="color: #9370db;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">还有另外一种表达的方式，就是</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert exp1:exp2;</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">其中</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">exp1</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">是个</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">boolean</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">返回值得表达式，而</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">exp2</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">可以是原始的数据类型或者对象都可以例如：</span></span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<span style="color: #9370db;">boolean boo = true; <br />
String str = null; <br />
assert boo = false</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">：</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">str="error"; <br />
<br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">我们刚开始讲得</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert exp1</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">得形式，当</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">exp1</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">是</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">false</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">得时候，</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">AssertionError</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">得默认构造器会被调用，但是</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert exp1:exp2</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">这样的形式，当</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">exp1</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">为</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">true</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">的时候后面</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">exp2</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">被或略，如果</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">false</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">的话，后面的表达式的结果会被计算出来并作为</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">AssertionError</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">得构造器参数。看下面的例子：</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<span style="color: #9370db;">public &gt;&nbsp; public static void main(String[] args) { <br />
&nbsp;&nbsp;&nbsp; AssertTest at = new AssertTest(); <br />
&nbsp;&nbsp;&nbsp; at.assertMe(true); <br />
&nbsp;&nbsp;&nbsp; at.assertMe(false); <br />
&nbsp; } <br />
&nbsp; private void assertMe(boolean boo) { <br />
&nbsp;&nbsp;&nbsp; String s = null; <br />
&nbsp;&nbsp;&nbsp; assert boo?true:false:s = "hello world"; <br />
&nbsp;&nbsp;&nbsp; System.out.println("true condition"); <br />
&nbsp; } <br />
}</span><br />
<br />
运行的时候会得到这样的结果:</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<span style="color: #9370db;">true condition <br />
Exception in thread "main" java.lang.AssertionError: hello world <br />
at AssertTest.assertMe(AssertTest.java:14) <br />
at AssertTest.main(AssertTest.java:7) <br />
<br />
Assert</span></span><span style="color: #9370db;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">最好不要滥用，原因是</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">并不一定都是</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">enable</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">的，下面两种情况就不应该用</span></span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert <br />
<br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">不要在</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">public</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">的方法里面检查参数是不是为</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">null</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">之类的操作，</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">例如<br />
<br />
public int get(String s) { <br />
&nbsp; assert s != null; <br />
} <br />
</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">如果需要检查也最好通过</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">if s = null </span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">抛出</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">NullPointerException</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">来检查</span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="color: #9370db;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><br />
<br />
不要用</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">来检查方法操作的返回值来判断方法操作的结果，</span></span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="color: #9370db;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">例如<br />
<br />
assert list.removeAll();<br />
<br />
这样看起来好像没有问题</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">但是想想如果</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">assert </span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">被</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">disable</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">呢，那样他就不会被执行了</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">所以</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">removeAll()</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">操作就没有被执行</span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">可以这样代替</span></span></span><span style="font-size: 10pt; color: #9370db; font-family: 宋体;"> <br />
boolean boo = list.removeAl(); <br />
assert boo;</span></div>
<span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">
<span style="font-size: 10pt; color: #9370db; font-family: 宋体;"><span style="font-size: 10pt; color: #9370db; font-family: 宋体;">
</span></span></span></span>
<img src ="http://www.blogjava.net/renyangok/aggbug/152316.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-10-12 13:16 <a href="http://www.blogjava.net/renyangok/archive/2007/10/12/152316.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux下安装jython环境</title><link>http://www.blogjava.net/renyangok/archive/2007/07/13/130099.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Fri, 13 Jul 2007 07:42:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/07/13/130099.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/130099.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/07/13/130099.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/130099.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/130099.html</trackback:ping><description><![CDATA[一，安装jdk：<br><br>（这里的方法是用于ubuntu或debian的，把下载的jdk构建成deb包，我觉得是为了便于包管理，否则删除的时候都不知道删除哪些文件，很麻烦。）<br>1. 获取JDK<br>可以选择从Java官方下载: ::URL::http://java.sun.com 或者从其它网站下载.我用的版本是:jdk-1_5_0-linux-i586.bin<br><br>2. 构建打包环境<br>Debian专门提供了SDK 的DEB包构建工具: java-package,而Ubuntu是基于Debian的,所以<br># apt-get install -u java-package fakeroot<br><br>在apt-get之前最好update一下<br><br>3. 创建.deb 软件包<br><br>这一步要以普通用户运行,如果以Root运行是不允许的.会有下面的提示:<br><br>You are real root -- unfortunately, some Java distributions have<br>install scripts that directly manipulate /etc, and may cause some<br>inconsistencies on your system. Instead, you should become a<br>non-root user and run:<br><br>fakeroot make-jpkg jdk-1_5_0-linux-i586.bin<br><br>which will allow no damage to be done to your system files and<br>still permit the Java distribution to successfully extract.<br><br>Aborting.<br><br>以普通用户执行:<br>＄ fakeroot make-jpkg jdk-1_5_0_06-linux-i586.bin<br>接下来做一些必要的选择.几分钟后,就应当出现软件包创建成功的提示.你在当前目录下会发现类似:<br>sun-j2sdk1.5_1.5.0+update00_i386.deb的软件包<br><br>4. 安装<br>切换回root执行以下命令:<br># dpkg -i sun-j2sdk1.5_1.5.0+update06_i386.deb<br><br>5.配置环境<br><br>在 ~/.bashrc脚本文件中加入类似如下内容<br><br>PATH=＄PATH:/usr/lib/j2sdk1.5-sun/bin:/usr/lib/j2sdk1.5-sun/jre/bin<br>JAVA_HOME=/usr/lib/j2sdk1.5-sun<br>JRE_HOME=/usr/lib/j2sdk1.5-sun/jre<br>CLASSPATH=.:/usr/lib/j2sdk1.5-sun/lib/tools.jar:/usr/lib/j2sdk1.5-sun/lib/dt.jar <br>export PATH<br>export JRE_HOME<br>export JAVA_HOME<br>export CLASSPATH<br><br>6. 测试<br>创建一个简单的java程序(Hello.java)<br>public class Hello<br>{<br>public Hello()<br>{<br>}<br><br>public static void main(String[] args)<br>{<br>System.out.println("Hello World!";<br>}<br><br>}<br>然后<br>＄javac Hello.java<br>检查当前目录会生成一个Hello.class的文件, 然后运行<br>＄java Hello<br>Hello World!<br>OK,测试成功!<br><br>7. 中文化安装中文字体:<br>在 ＄JAVA_HOME/jre/lib/fonts/ 目录下创建一个fallback目录.<br>复制中文字体(例如:simsun.ttf 至此目录.<br><br>8. 安装插件<br>对于此种方法安装的Java环境, 浏览器插件文件位置应当位于:<br>/usr/lib/j2sdk1.5-sun/jre/plugin/i386/ns7/libjavaplugin_oji.so<br><br>以 firefox1.5.0.1为例:<br># cd /usr/lib/mozilla-firefox/plugins<br># ln -s \<br>/usr/lib/j2sdk1.5-sun/jre/plugin/i386/ns7/libjavaplugin_oji.so<br><br>卸载JDK:<br># apt-get remove --purge sun-j2sdk1.5<br>卸载插件, 直接删除符号链接:<br># rm /usr/lib/mozilla-firefox/plugins/libjavaplugin_oji.so<br><br>二，安装jython：<br><br>1，http://www.jython.org/Project/installation.html下载jython安装文件，运行命令&#8220;java -jar jython_installer-2.2rc2.jar&#8221;，jython即安装成功。比如安装在/home/justin/java/jython2.2目录下<br><br>2，把jython包加入classpath，即把上面的classpath改为：CLASSPATH=.:/usr/lib/j2sdk1.5-sun/lib/tools.jar:/usr/lib/j2sdk1.5-sun/lib/dt.jar:/home/justin/java/jython2.2/jython.jar<br>此后就可以在java文件中加入python库了，例如：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;org.python.util.PythonInterpreter;&nbsp;<br><br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;org.python.core.</span><span style="color: #000000;">*</span><span style="color: #000000;">;&nbsp;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;SimpleEmbedded&nbsp;{&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;main(String&nbsp;[]args)<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;PyException<br><br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PythonInterpreter&nbsp;interp&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;"><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;PythonInterpreter();<br><br>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">Hello,&nbsp;brave&nbsp;new&nbsp;world</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;interp.exec(</span><span style="color: #000000;">"</span><span style="color: #000000;">import&nbsp;sys</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;interp.exec(</span><span style="color: #000000;">"</span><span style="color: #000000;">print&nbsp;sys</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;interp.set(</span><span style="color: #000000;">"</span><span style="color: #000000;">a</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;PyInteger(</span><span style="color: #000000;">42</span><span style="color: #000000;">));<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;interp.exec(</span><span style="color: #000000;">"</span><span style="color: #000000;">print&nbsp;a</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;interp.exec(</span><span style="color: #000000;">"</span><span style="color: #000000;">x&nbsp;=&nbsp;2+2</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PyObject&nbsp;x&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;interp.get(</span><span style="color: #000000;">"</span><span style="color: #000000;">x</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br><br>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">x:&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">+</span><span style="color: #000000;">x);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">Goodbye,&nbsp;cruel&nbsp;world</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</span></div>
<br>3，将选择的/home/justin/java/jython2.2/jython安装路径添加到 PATH 环境变量。现在只要输入&#8220;jython&#8221;就可以运行交互式 PATH ：<strong></strong><br>
$ jython<br>
Jython 2.1 on java1.4.0_01 (JIT: null)<br>
Type "copyright", "credits" or "license" for more information.<br>&gt;&gt;&gt;# 通过 Jython 访问标准 Java 库<br>
&gt;&gt;&gt; from java.util import Random<br>
&gt;&gt;&gt; rng = Random()<br>
&gt;&gt;&gt; i = rng.nextBoolean()<br>
&gt;&gt;&gt; print i<br><br>jython 解释器对于快速检查和作提示都很方便，但您不必在这其中完成所有工作 ― Jython 还允许您在源文件中编写代码，并随后运行该代码（<br>from java.util import Random<br>rng = Random()<br>#This is a comment in Jython<br>print "Flipping a coin..."<br>if rng.nextBoolean():<br>&nbsp;&nbsp;&nbsp; print "Came up heads"<br>else:<br>&nbsp;&nbsp;&nbsp; print "Came up tails"<br>用jython运行该文件，即可<br><img src ="http://www.blogjava.net/renyangok/aggbug/130099.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-07-13 15:42 <a href="http://www.blogjava.net/renyangok/archive/2007/07/13/130099.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hashtable和HashMap类的区别</title><link>http://www.blogjava.net/renyangok/archive/2007/03/29/107326.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Thu, 29 Mar 2007 12:32:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/03/29/107326.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/107326.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/03/29/107326.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/107326.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/107326.html</trackback:ping><description><![CDATA[
		<div class="tit">Hashtable和HashMap类的区别转自：<a href="http://hi.baidu.com/danghj/blog/item/635c862f15949c381f3089a4.html">http://hi.baidu.com/danghj/blog/item/635c862f15949c381f3089a4.html</a></div>
		<div class="date">
		</div>
		<p>
		</p>
		<table style="TABLE-LAYOUT: fixed">
				<tbody>
						<tr>
								<td>
										<div class="cnt">
												<p>Hashtable和HashMap类有三个重要的不同之处。第一个不同主要是历史原因。Hashtable是基于陈旧的Dictionary类的，HashMap是Java 1.2引进的Map接口的一个实现。<br /><br />也许最重要的不同是Hashtable的方法是同步的，而HashMap的方法不是。这就意味着，虽然你可以不用采取任何特殊的行为就可以在一个多线程的应用程序中用一个Hashtable，但你必须同样地为一个HashMap提供外同步。一个方便的方法就是利用Collections类的静态的synchronizedMap()方法，它创建一个线程安全的Map对象，并把它作为一个封装的对象来返回。这个对象的方法可以让你同步访问潜在的HashMap。这么做的结果就是当你不需要同步时，你不能切断Hashtable中的同步（比如在一个单线程的应用程序中），而且同步增加了很多处理费用。<br /><br />第三点不同是，只有HashMap可以让你将空值作为一个表的条目的key或value。HashMap中只有一条记录可以是一个空的key，但任意数量的条目可以是空的value。这就是说，如果在表中没有发现搜索键，或者如果发现了搜索键，但它是一个空的值，那么get()将返回null。如果有必要，用containKey()方法来区别这两种情况。<br /><br />一些资料建议，当需要同步时，用Hashtable，反之用HashMap。但是，因为在需要时，HashMap可以被同步，HashMap的功能比Hashtable的功能更多，而且它不是基于一个陈旧的类的，所以有人认为，在各种情况下，HashMap都优先于Hashtable。<br /><br />关于Properties<br />有时侯，你可能想用一个hashtable来映射key的字符串到value的字符串。DOS、Windows和Unix中的环境字符串就有一些例子，如key的字符串PATH被映射到value的字符串C:\WINDOWS;C:\WINDOWS\SYSTEM。Hashtables是表示这些的一个简单的方法，但Java提供了另外一种方法。<br /><br />Java.util.Properties类是Hashtable的一个子类，设计用于String keys和values。Properties对象的用法同Hashtable的用法相象，但是类增加了两个节省时间的方法，你应该知道。<br /><br />Store()方法把一个Properties对象的内容以一种可读的形式保存到一个文件中。Load()方法正好相反，用来读取文件，并设定Properties对象来包含keys和values。<br /><br />注意，因为Properties扩展了Hashtable，你可以用超类的put()方法来添加不是String对象的keys和values。这是不可取的。另外，如果你将store()用于一个不包含String对象的Properties对象，store()将失败。作为put()和get()的替代，你应该用setProperty()和getProperty()，它们用String参数。<br /><br />好了，我希望你现在可以知道如何用hashtables来加速你的处理了。</p>
												<p> </p>
												<p> </p>
												<p>下面再转一篇关于两个类的区别，比较简单的过一下</p>
												<div>
														<font color="#333333">最近同学找工作，经常被问到这个问题rt，所以。。。。。。</font>
												</div>
												<div> </div>
												<div>
														<font color="#333333">
																<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">HashTable</strong>的应用非常广泛，HashMap是新框架中用来代替<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">HashTable</strong>的类，也就是说建议使用HashMap，不要使用<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">HashTable</strong>。</font>
												</div>
												<div> </div>
												<div>
														<font color="#333333">这里简单分析他们的区别。 <br />1.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">HashTable</strong>的方法是<font color="#0080ff">同步</font>的，HashMap<font color="#0080ff">未经同步</font>，所以在多线程场合要手动同步HashMap这个区别就像Vector和ArrayList一样。（最主要的区别）<br /><br />2.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">HashTable</strong><font color="#0080ff">不允许null值</font>(key和value都不可以),<font color="#0080ff">HashMap允许null值(key和value都可以，只容许有一个null值的key，可以有多个null值的value</font>)。<br /><br />3.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">HashTable</strong>有一个contains(Object value)，功能和containsValue(Object value)功能一样。<br /><br />4.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">HashTable</strong>使用Enumeration，HashMap使用Iterator。<br /><br />以上只是表面的不同，它们的实现也有很大的不同。<br /><br />5.<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">HashTable</strong>中hash数组默认大小是11，增加的方式是 old*2+1。HashMap中hash数组的默认大小是16，而且一定是2的指数。<br /><br />6.哈希值的使用不同，<strong style="COLOR: black; BACKGROUND-COLOR: #ffff66">HashTable</strong>直接使用对象的hashCode，代码是这样的：<br />int hash = key.hashCode();<br />int index = (hash &amp; 0x7FFFFFFF) % tab.length;<br />而HashMap重新计算hash值，而且用<a name="baidusnap1"></a><strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">与</strong>代替求模：<br />int hash = hash(k);<br />int i = indexFor(hash, table.length);<br /></font>
												</div>
												<div>
														<table width="680" border="0">
																<tbody>
																		<tr>
																				<td width="403">
																						<font color="#333333" size="2">static int hash(Object x) {<br />　　 int h = x.hashCode();<br /><br />　　 h += ~(h &lt;&lt; 9);<br />　　 h ^= (h &gt;&gt;&gt; 14);<br />　　 h += (h &lt;&lt; 4);<br />　　 h ^= (h &gt;&gt;&gt; 10);<br />　　 return h;<br />}<br />static int indexFor(int h, int length) {<br />　　 return h &amp; (length-1);<br />}<br />以上只是一些比较突出的区别，当然他们的实现上还是有很多不同的，比如<br />HashMap对null的操作。</font>
																				</td>
																		</tr>
																</tbody>
														</table>
												</div>
										</div>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/renyangok/aggbug/107326.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-03-29 20:32 <a href="http://www.blogjava.net/renyangok/archive/2007/03/29/107326.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java.math包应用</title><link>http://www.blogjava.net/renyangok/archive/2007/03/16/104283.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Fri, 16 Mar 2007 07:54:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/03/16/104283.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/104283.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/03/16/104283.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/104283.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/104283.html</trackback:ping><description><![CDATA[
		<p>java.math.Math类常用的常量和方法：</p>
		<p>Math.PI 记录的圆周率<br />Math.E记录e的常量<br />Math.abs 求绝对值<br />Math.sin 正弦函数 Math.asin 反正弦函数<br />Math.cos 余弦函数 Math.acos 反余弦函数<br />Math.tan 正切函数 Math.atan 反正切函数&amp;nbsp;Math.atan2 商的反正切函数<br />Math.toDegrees 弧度转化为角度 Math.toRadians 角度转化为弧度<br />Math.ceil 得到不小于某数的最大整数<br />Math.floor 得到不大于某数的最大整数<br />Math.IEEEremainder 求余<br />Math.max 求两数中最大<br />Math.min 求两数中最小<br />Math.sqrt 求开方<br />Math.pow 求某数的任意次方, 抛出ArithmeticException处理溢出异常<br />Math.exp 求e的任意次方<br />Math.log10 以10为底的对数<br />Math.log 自然对数<br />Math.rint 求距离某数最近的整数（可能比某数大，也可能比它小）<br />Math.round 同上，返回int型或者long型（上一个函数返回double型）<br />Math.random 返回0，1之间的一个随机数</p>
		<p>java.math.BigInteger(大整数)：<br />BigInteger bi1=new BigInteger("1234567890123456890");<br />BigInteger bi2=BigInteger.valueOf(123L);<br />bi1=bi1.add(bi2);//b1+b2<br />bi1=bi1.multiply(bi2);//b1*b2<br />bi1=bi1.subtract(bi2);//b1-b2<br />bi1=bi1.divide(bi2);// b1/b2</p>
		<p>java.math.BigDecimal(大浮点数):<br />BigDecimal bd = new BigDecimal("3.1415926");<br />bd = bd.setScale(2,BigDecimal.ROUND_DOWN);//取3.1415926小数点后面二位<br /></p>
<img src ="http://www.blogjava.net/renyangok/aggbug/104283.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-03-16 15:54 <a href="http://www.blogjava.net/renyangok/archive/2007/03/16/104283.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jdk5.0学习笔记</title><link>http://www.blogjava.net/renyangok/archive/2007/03/08/102597.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Thu, 08 Mar 2007 07:06:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/03/08/102597.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/102597.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/03/08/102597.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/102597.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/102597.html</trackback:ping><description><![CDATA[
		<p>1、classpath不用再定义.;***\lib\tools.jar;***\lib\rt.jar，因为jre会自动寻找lib目录</p>
		<p>2、如果想要用jdk5.0编译出jdk1.4可运行的class文件需要带-source和-target两个参数<br />eg: javac -source 1.4 -target 1.4 Hello.java</p>
		<p>3、命令行读入int i = System.in.read();//读入输入字符串的第一个字符的int值；<br />读整个字符串时：<br />public class Test{<br /> public static void main(String[] args){<br />  byte[] a = new byte[100];<br />  try {<br />   System.in.read(a);<br />  } catch (IOException e) {<br />   e.printStackTrace();<br />  }<br />  System.out.println(new String(a));<br /> }<br />}</p>
		<p>jdk5.0中命令行读入的方法更好，可以读成不同类型的数据：<br />//Scanner取得输入的依据是：空格键、Tab键或Enter键<br />import java.util.Scanner;</p>
		<p>public class ScannerDemo{<br /> public static void main(String[] args){<br />  Scanner scanner = new Scanner(System.in);<br />  System.out.print("请输入姓名");<br />  System.out.printf("您好%s!\n", scanner.next());<br />  System.out.print("请输入年龄");<br />  System.out.printf("您好%d!\n", scanner.nextInt());<br />  //还有scanner.nextFloat(),scanner.nextBoolean();<br /> }<br />}</p>
		<p>//BufferReader取得输入的依据是：Enter键<br />import java.io.*;</p>
		<p>public class BufferReaderDemo{<br /> public static void main(String[] args){<br />  BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));<br />  System.out.print("请输入一系列文字");<br />  String text = bufferedReader.readLine();<br />  System.out.print("您输入的是：" + text);<br /> }<br /> }<br />}</p>
		<p>4、aotuboxing和unboxing，jdk5.0可以自动对基本类型和它们的包装类型自动转换。</p>
		<p>5、数组<br />数组的索引值:由0开始的原因：索引值表示偏移量，第一个值的偏移为0.</p>
		<p>数组的初始化：byte/short/int = 0; long = ol; float = o.0f; double = 0.0d; char = \u0000; boolean = false; Objective = null;</p>
		<p>一维数组：<br />法一：int[] i = {1,2,3};<br />法二：int[] i = new int[]{1,2,3};<br />法三：int[] i = new int[3]; i[0] = 1; i[1] = 2; i[2] = 3;</p>
		<p>多维数组：<br />法一：int[][] i = {{...},...,{...}};<br />法二：int[][] i = int[][]{{...},...,{...}};<br />法三：int[][] i = int[3][]; i[0] = {1,2,3}; i[0] = {1,2,3}; i[0] = {1,2,3};<br />法四：int[][] i = int[3][3];</p>
		<p>不规则数组：行列不等</p>
		<p>数组的常用方法：都是java.util.Arrays类的方法<br />sort()//制定数组快速排序<br />binarySearch()//对已排序的数组搜索，找到返回索引，否则返回负值<br />fill()//根据数组的数据类型填默认值<br />equals()//比较两数组<br />jdk1.5中新增:<br />deepEquals()//深度比较<br />deepToString()//深度输出</p>
		<p>foreach与数组：<br />String[] a = {"asd","efge","efg"};<br />for(String s : a)<br /> System.out.println(s);</p>
		<p>5、字符串<br />java.lang.StringBuilder是jdk5.0新增的类，它与StringBuffer具有相同接口，只是单机非多线程情况下用StringBuilder效率较高，因为StringBuilder没处理同步问题；多线程下用StringBuffer好。</p>
		<p>字符串分离：<br /> String s = "23/twomen/tlai/t jeje";<br /> String[] a = s.split("/t");<br /> for(int i = 0; i &lt; a.length; i++){<br />  System.out.print(a[i] +　" ");<br /> }<br />输出结构：23 women lai  jeje<br /><br />由于工作关系学习jdk5.0的步伐暂时停止，以后有机会继续看《jdk5.0学习笔记》，回来写我的总结。</p>
<img src ="http://www.blogjava.net/renyangok/aggbug/102597.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-03-08 15:06 <a href="http://www.blogjava.net/renyangok/archive/2007/03/08/102597.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java处理url中的特殊字符（如&amp;,%...）</title><link>http://www.blogjava.net/renyangok/archive/2007/01/12/93537.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Fri, 12 Jan 2007 13:14:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/01/12/93537.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/93537.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/01/12/93537.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/93537.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/93537.html</trackback:ping><description><![CDATA[URL(Uniform Resoure Locator，统一资源定位器)是Internet中对资源进行统一定位和管理的标志。<br />一个完整的URL包括如下内容：<br />1.         应用协议名称，包括http,ftp,file等标志<br />2.         资源定位，是由(.)分割等网络路径<br />3.         端口号，按照规定，http应用端口是80，telnet协议应用端口是23。<br />4.         服务器中的文件路径<br />5.         文件中的编码位置<br />一个完整的URL如下：<br /><a href="http://SomeUser:mypassword@www.some_server.com:8080/path/file.html">http://SomeUser:mypassword@www.some_server.com:8080/path/file.html</a><br /> URL无法显示某些特殊符号，这个时候就要使用编码了。编码的格式为：一个百分号，后面跟对应字符的ASCII（16进制）码值。例如 空格的编码值是"%20"。（ASCII参考）<br />有些字符在URL中具有特殊含义，基本编码规则如下：<br />特殊含义                                                            十六进制值 <br />1.+ 表示空格（在 URL 中不能使用空格）          %20 <br />2./ 分隔目录和子目录                                              %2F <br />3.? 分隔实际的 URL 和参数                                   %3F <br />4.% 指定特殊字符                                                    %25 <br />5.# 表示书签                                                             %23 <br />6.&amp; URL 中指定的参数间的分隔符                        %26 <br />java中URL 的编码和解码函数<br />java.net.URLEncoder.encode(String s)和java.net.URLDecoder.decode(String s);<br />在javascript 中URL 的编码和解码函数<br />escape(String s)和unescape(String s) ;<img src ="http://www.blogjava.net/renyangok/aggbug/93537.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-01-12 21:14 <a href="http://www.blogjava.net/renyangok/archive/2007/01/12/93537.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java反射经典实例 Java Reflection Cookbook </title><link>http://www.blogjava.net/renyangok/archive/2007/01/12/93341.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Fri, 12 Jan 2007 02:03:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/01/12/93341.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/93341.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/01/12/93341.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/93341.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/93341.html</trackback:ping><description><![CDATA[(转自：<a href="http://blog.csdn.net/nomads/archive/2006/09/05/1178867.aspx">http://blog.csdn.net/nomads/archive/2006/09/05/1178867.aspx</a>)<br />Java提供了一套机制来动态执行方法和构造方法，以及数组操作等，这套机制就叫——反射。反射机制是如今很多流行框架的实现基础，其中包括Spring、Hibernate等。原理性的问题不是本文的重点，接下来让我们在实例中学习这套精彩的机制。<br /><br />1. 得到某个对象的属性<br /><br /><div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: rgb(0,128,128)">1</span> <span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> Object getProperty(Object owner, String fieldName) </span><span style="COLOR: rgb(0,0,255)">throws</span><span style="COLOR: rgb(0,0,0)"> Exception {<br /></span><span style="COLOR: rgb(0,128,128)">2</span> <span style="COLOR: rgb(0,0,0)">    Class ownerClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> owner.getClass();<br /></span><span style="COLOR: rgb(0,128,128)">3</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)">4</span> <span style="COLOR: rgb(0,0,0)">    Field field </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> ownerClass.getField(fieldName);<br /></span><span style="COLOR: rgb(0,128,128)">5</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)">6</span> <span style="COLOR: rgb(0,0,0)">    Object property </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> field.get(owner);<br /></span><span style="COLOR: rgb(0,128,128)">7</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)">8</span> <span style="COLOR: rgb(0,0,0)">    </span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)"> property;<br /></span><span style="COLOR: rgb(0,128,128)">9</span> <span style="COLOR: rgb(0,0,0)">}</span></div><br />Class ownerClass = owner.getClass()：得到该对象的Class。<br /><br />Field field = ownerClass.getField(fieldName)：通过Class得到类声明的属性。<br /><br />Object property = field.get(owner)：通过对象得到该属性的实例，如果这个属性是非公有的，这里会报IllegalAccessException。<br /><br /><br /><br />2. 得到某个类的静态属性<br /><br /><div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: rgb(0,128,128)"> 1</span> <span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> Object getStaticProperty(String className, String fieldName)<br /></span><span style="COLOR: rgb(0,128,128)"> 2</span> <span style="COLOR: rgb(0,0,0)">            </span><span style="COLOR: rgb(0,0,255)">throws</span><span style="COLOR: rgb(0,0,0)"> Exception {<br /></span><span style="COLOR: rgb(0,128,128)"> 3</span> <span style="COLOR: rgb(0,0,0)">    Class ownerClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> Class.forName(className);<br /></span><span style="COLOR: rgb(0,128,128)"> 4</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)"> 5</span> <span style="COLOR: rgb(0,0,0)">    Field field </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> ownerClass.getField(fieldName);<br /></span><span style="COLOR: rgb(0,128,128)"> 6</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)"> 7</span> <span style="COLOR: rgb(0,0,0)">    Object property </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> field.get(ownerClass);<br /></span><span style="COLOR: rgb(0,128,128)"> 8</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)"> 9</span> <span style="COLOR: rgb(0,0,0)">    </span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)"> property;<br /></span><span style="COLOR: rgb(0,128,128)">10</span> <span style="COLOR: rgb(0,0,0)">}</span></div><br /><br />Class ownerClass = Class.forName(className) ：首先得到这个类的Class。<br /><br />Field field = ownerClass.getField(fieldName)：和上面一样，通过Class得到类声明的属性。<br /><br />Object property = field.get(ownerClass) ：这里和上面有些不同，因为该属性是静态的，所以直接从类的Class里取。<br /><br /><br />3. 执行某对象的方法<br /><br /><div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: rgb(0,128,128)"> 1</span> <span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> Object invokeMethod(Object owner, String methodName, Object[] args) </span><span style="COLOR: rgb(0,0,255)">throws</span><span style="COLOR: rgb(0,0,0)"> Exception {<br /></span><span style="COLOR: rgb(0,128,128)"> 2</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)"> 3</span> <span style="COLOR: rgb(0,0,0)">    Class ownerClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> owner.getClass();<br /></span><span style="COLOR: rgb(0,128,128)"> 4</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)"> 5</span> <span style="COLOR: rgb(0,0,0)">    Class[] argsClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,255)">new</span><span style="COLOR: rgb(0,0,0)"> Class[args.length];<br /></span><span style="COLOR: rgb(0,128,128)"> 6</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)"> 7</span> <span style="COLOR: rgb(0,0,0)">    </span><span style="COLOR: rgb(0,0,255)">for</span><span style="COLOR: rgb(0,0,0)"> (</span><span style="COLOR: rgb(0,0,255)">int</span><span style="COLOR: rgb(0,0,0)"> i </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,0)">0</span><span style="COLOR: rgb(0,0,0)">, j </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> args.length; i </span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)"> j; i</span><span style="COLOR: rgb(0,0,0)">++</span><span style="COLOR: rgb(0,0,0)">) {<br /></span><span style="COLOR: rgb(0,128,128)"> 8</span> <span style="COLOR: rgb(0,0,0)">        argsClass[i] </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> args[i].getClass();<br /></span><span style="COLOR: rgb(0,128,128)"> 9</span> <span style="COLOR: rgb(0,0,0)">    }<br /></span><span style="COLOR: rgb(0,128,128)">10</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)">11</span> <span style="COLOR: rgb(0,0,0)">    Method method </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> ownerClass.getMethod(methodName, argsClass);<br /></span><span style="COLOR: rgb(0,128,128)">12</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)">13</span> <span style="COLOR: rgb(0,0,0)">    </span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)"> method.invoke(owner, args);<br /></span><span style="COLOR: rgb(0,128,128)">14</span> <span style="COLOR: rgb(0,0,0)">}</span></div><br />Class owner_class = owner.getClass() ：首先还是必须得到这个对象的Class。<br /><br />5～9行：配置参数的Class数组，作为寻找Method的条件。<br /><br />Method method = ownerClass.getMethod(methodName, argsClass)：通过Method名和参数的Class数组得到要执行的Method。<br /><br />method.invoke(owner, args)：执行该Method，invoke方法的参数是执行这个方法的对象，和参数数组。返回值是Object，也既是该方法的返回值。<br /><br /><br />4. 执行某个类的静态方法<br /><br /><div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: rgb(0,128,128)"> 1</span> <span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> Object invokeStaticMethod(String className, String methodName,<br /></span><span style="COLOR: rgb(0,128,128)"> 2</span> <span style="COLOR: rgb(0,0,0)">            Object[] args) </span><span style="COLOR: rgb(0,0,255)">throws</span><span style="COLOR: rgb(0,0,0)"> Exception {<br /></span><span style="COLOR: rgb(0,128,128)"> 3</span> <span style="COLOR: rgb(0,0,0)">    Class ownerClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> Class.forName(className);<br /></span><span style="COLOR: rgb(0,128,128)"> 4</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)"> 5</span> <span style="COLOR: rgb(0,0,0)">    Class[] argsClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,255)">new</span><span style="COLOR: rgb(0,0,0)"> Class[args.length];<br /></span><span style="COLOR: rgb(0,128,128)"> 6</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)"> 7</span> <span style="COLOR: rgb(0,0,0)">    </span><span style="COLOR: rgb(0,0,255)">for</span><span style="COLOR: rgb(0,0,0)"> (</span><span style="COLOR: rgb(0,0,255)">int</span><span style="COLOR: rgb(0,0,0)"> i </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,0)">0</span><span style="COLOR: rgb(0,0,0)">, j </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> args.length; i </span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)"> j; i</span><span style="COLOR: rgb(0,0,0)">++</span><span style="COLOR: rgb(0,0,0)">) {<br /></span><span style="COLOR: rgb(0,128,128)"> 8</span> <span style="COLOR: rgb(0,0,0)">        argsClass[i] </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> args[i].getClass();<br /></span><span style="COLOR: rgb(0,128,128)"> 9</span> <span style="COLOR: rgb(0,0,0)">    }<br /></span><span style="COLOR: rgb(0,128,128)">10</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)">11</span> <span style="COLOR: rgb(0,0,0)">    Method method </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> ownerClass.getMethod(methodName, argsClass);<br /></span><span style="COLOR: rgb(0,128,128)">12</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)">13</span> <span style="COLOR: rgb(0,0,0)">    </span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)"> method.invoke(</span><span style="COLOR: rgb(0,0,255)">null</span><span style="COLOR: rgb(0,0,0)">, args);<br /></span><span style="COLOR: rgb(0,128,128)">14</span> <span style="COLOR: rgb(0,0,0)">}</span></div><br /><br />基本的原理和实例3相同，不同点是最后一行，invoke的一个参数是null，因为这是静态方法，不需要借助实例运行。<br /><br /><br /><br />5. 新建实例<br /><div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: rgb(0,128,128)"> 1</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)"> 2</span> <span style="COLOR: rgb(0,0,0)"></span><span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> Object newInstance(String className, Object[] args) </span><span style="COLOR: rgb(0,0,255)">throws</span><span style="COLOR: rgb(0,0,0)"> Exception {<br /></span><span style="COLOR: rgb(0,128,128)"> 3</span> <span style="COLOR: rgb(0,0,0)">    Class newoneClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> Class.forName(className);<br /></span><span style="COLOR: rgb(0,128,128)"> 4</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)"> 5</span> <span style="COLOR: rgb(0,0,0)">    Class[] argsClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,255)">new</span><span style="COLOR: rgb(0,0,0)"> Class[args.length];<br /></span><span style="COLOR: rgb(0,128,128)"> 6</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)"> 7</span> <span style="COLOR: rgb(0,0,0)">    </span><span style="COLOR: rgb(0,0,255)">for</span><span style="COLOR: rgb(0,0,0)"> (</span><span style="COLOR: rgb(0,0,255)">int</span><span style="COLOR: rgb(0,0,0)"> i </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,0)">0</span><span style="COLOR: rgb(0,0,0)">, j </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> args.length; i </span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)"> j; i</span><span style="COLOR: rgb(0,0,0)">++</span><span style="COLOR: rgb(0,0,0)">) {<br /></span><span style="COLOR: rgb(0,128,128)"> 8</span> <span style="COLOR: rgb(0,0,0)">        argsClass[i] </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> args[i].getClass();<br /></span><span style="COLOR: rgb(0,128,128)"> 9</span> <span style="COLOR: rgb(0,0,0)">    }<br /></span><span style="COLOR: rgb(0,128,128)">10</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)">11</span> <span style="COLOR: rgb(0,0,0)">    Constructor cons </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> newoneClass.getConstructor(argsClass);<br /></span><span style="COLOR: rgb(0,128,128)">12</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)">13</span> <span style="COLOR: rgb(0,0,0)">    </span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)"> cons.newInstance(args);<br /></span><span style="COLOR: rgb(0,128,128)">14</span> <span style="COLOR: rgb(0,0,0)"><br /></span><span style="COLOR: rgb(0,128,128)">15</span> <span style="COLOR: rgb(0,0,0)">}</span></div><br /><br />这里说的方法是执行带参数的构造函数来新建实例的方法。如果不需要参数，可以直接使用newoneClass.newInstance()来实现。<br /><br />Class newoneClass = Class.forName(className)：第一步，得到要构造的实例的Class。<br /><br />第5～第9行：得到参数的Class数组。<br /><br />Constructor cons = newoneClass.getConstructor(argsClass)：得到构造子。<br /><br />cons.newInstance(args)：新建实例。<br /><br /><br />6. 判断是否为某个类的实例<br /><br /><div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: rgb(0,128,128)">1</span> <span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,255)">boolean</span><span style="COLOR: rgb(0,0,0)"> isInstance(Object obj, Class cls) {<br /></span><span style="COLOR: rgb(0,128,128)">2</span> <span style="COLOR: rgb(0,0,0)">    </span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)"> cls.isInstance(obj);<br /></span><span style="COLOR: rgb(0,128,128)">3</span> <span style="COLOR: rgb(0,0,0)">}</span></div><br /><br /><br />7. 得到数组中的某个元素<br /><div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: rgb(0,128,128)">1</span> <span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> Object getByArray(Object array, </span><span style="COLOR: rgb(0,0,255)">int</span><span style="COLOR: rgb(0,0,0)"> index) {<br /></span><span style="COLOR: rgb(0,128,128)">2</span> <span style="COLOR: rgb(0,0,0)">    </span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)"> Array.get(array,index);<br /></span><span style="COLOR: rgb(0,128,128)">3</span> <span style="COLOR: rgb(0,0,0)">}</span></div><br /><br /><br />附完整源码：<br /><br /><div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: rgb(0,0,255)">import</span><span style="COLOR: rgb(0,0,0)"> java.lang.reflect.Array;<br /></span><span style="COLOR: rgb(0,0,255)">import</span><span style="COLOR: rgb(0,0,0)"> java.lang.reflect.Constructor;<br /></span><span style="COLOR: rgb(0,0,255)">import</span><span style="COLOR: rgb(0,0,0)"> java.lang.reflect.Field;<br /></span><span style="COLOR: rgb(0,0,255)">import</span><span style="COLOR: rgb(0,0,0)"> java.lang.reflect.Method;<br /><br /><br /></span><span style="COLOR: rgb(0,128,0)">/**</span><span style="COLOR: rgb(0,128,0)"><br /> * Java Reflection Cookbook<br /> *<br /> * </span><span style="COLOR: rgb(128,128,128)">@author</span><span style="COLOR: rgb(0,128,0)"> Michael Lee<br /> * </span><span style="COLOR: rgb(128,128,128)">@since</span><span style="COLOR: rgb(0,128,0)"> 2006-8-23<br /> * </span><span style="COLOR: rgb(128,128,128)">@version</span><span style="COLOR: rgb(0,128,0)"> 0.1a<br /> </span><span style="COLOR: rgb(0,128,0)">*/</span><span style="COLOR: rgb(0,0,0)"><br /><br /></span><span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,255)">class</span><span style="COLOR: rgb(0,0,0)"> Reflection {<br />    </span><span style="COLOR: rgb(0,128,0)">/**</span><span style="COLOR: rgb(0,128,0)"><br />     * 得到某个对象的公共属性<br />     *<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> owner, fieldName<br />     * </span><span style="COLOR: rgb(128,128,128)">@return</span><span style="COLOR: rgb(0,128,0)"> 该属性对象<br />     * </span><span style="COLOR: rgb(128,128,128)">@throws</span><span style="COLOR: rgb(0,128,0)"> Exception<br />     *<br />     </span><span style="COLOR: rgb(0,128,0)">*/</span><span style="COLOR: rgb(0,0,0)"><br />    </span><span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> Object getProperty(Object owner, String fieldName) </span><span style="COLOR: rgb(0,0,255)">throws</span><span style="COLOR: rgb(0,0,0)"> Exception {<br />        Class ownerClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> owner.getClass();<br /><br />        Field field </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> ownerClass.getField(fieldName);<br /><br />        Object property </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> field.get(owner);<br /><br />        </span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)"> property;<br />    }<br /><br />    </span><span style="COLOR: rgb(0,128,0)">/**</span><span style="COLOR: rgb(0,128,0)"><br />     * 得到某类的静态公共属性<br />     *<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> className   类名<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> fieldName   属性名<br />     * </span><span style="COLOR: rgb(128,128,128)">@return</span><span style="COLOR: rgb(0,128,0)"> 该属性对象<br />     * </span><span style="COLOR: rgb(128,128,128)">@throws</span><span style="COLOR: rgb(0,128,0)"> Exception<br />     </span><span style="COLOR: rgb(0,128,0)">*/</span><span style="COLOR: rgb(0,0,0)"><br />    </span><span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> Object getStaticProperty(String className, String fieldName)<br />            </span><span style="COLOR: rgb(0,0,255)">throws</span><span style="COLOR: rgb(0,0,0)"> Exception {<br />        Class ownerClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> Class.forName(className);<br /><br />        Field field </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> ownerClass.getField(fieldName);<br /><br />        Object property </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> field.get(ownerClass);<br /><br />        </span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)"> property;<br />    }<br /><br /><br />    </span><span style="COLOR: rgb(0,128,0)">/**</span><span style="COLOR: rgb(0,128,0)"><br />     * 执行某对象方法<br />     *<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> owner<br />     *            对象<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> methodName<br />     *            方法名<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> args<br />     *            参数<br />     * </span><span style="COLOR: rgb(128,128,128)">@return</span><span style="COLOR: rgb(0,128,0)"> 方法返回值<br />     * </span><span style="COLOR: rgb(128,128,128)">@throws</span><span style="COLOR: rgb(0,128,0)"> Exception<br />     </span><span style="COLOR: rgb(0,128,0)">*/</span><span style="COLOR: rgb(0,0,0)"><br />    </span><span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> Object invokeMethod(Object owner, String methodName, Object[] args)<br />            </span><span style="COLOR: rgb(0,0,255)">throws</span><span style="COLOR: rgb(0,0,0)"> Exception {<br /><br />        Class ownerClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> owner.getClass();<br /><br />        Class[] argsClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,255)">new</span><span style="COLOR: rgb(0,0,0)"> Class[args.length];<br /><br />        </span><span style="COLOR: rgb(0,0,255)">for</span><span style="COLOR: rgb(0,0,0)"> (</span><span style="COLOR: rgb(0,0,255)">int</span><span style="COLOR: rgb(0,0,0)"> i </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,0)">0</span><span style="COLOR: rgb(0,0,0)">, j </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> args.length; i </span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)"> j; i</span><span style="COLOR: rgb(0,0,0)">++</span><span style="COLOR: rgb(0,0,0)">) {<br />            argsClass[i] </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> args[i].getClass();<br />        }<br /><br />        Method method </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> ownerClass.getMethod(methodName, argsClass);<br /><br />        </span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)"> method.invoke(owner, args);<br />    }<br /><br /><br />      </span><span style="COLOR: rgb(0,128,0)">/**</span><span style="COLOR: rgb(0,128,0)"><br />     * 执行某类的静态方法<br />     *<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> className<br />     *            类名<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> methodName<br />     *            方法名<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> args<br />     *            参数数组<br />     * </span><span style="COLOR: rgb(128,128,128)">@return</span><span style="COLOR: rgb(0,128,0)"> 执行方法返回的结果<br />     * </span><span style="COLOR: rgb(128,128,128)">@throws</span><span style="COLOR: rgb(0,128,0)"> Exception<br />     </span><span style="COLOR: rgb(0,128,0)">*/</span><span style="COLOR: rgb(0,0,0)"><br />    </span><span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> Object invokeStaticMethod(String className, String methodName,<br />            Object[] args) </span><span style="COLOR: rgb(0,0,255)">throws</span><span style="COLOR: rgb(0,0,0)"> Exception {<br />        Class ownerClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> Class.forName(className);<br /><br />        Class[] argsClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,255)">new</span><span style="COLOR: rgb(0,0,0)"> Class[args.length];<br /><br />        </span><span style="COLOR: rgb(0,0,255)">for</span><span style="COLOR: rgb(0,0,0)"> (</span><span style="COLOR: rgb(0,0,255)">int</span><span style="COLOR: rgb(0,0,0)"> i </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,0)">0</span><span style="COLOR: rgb(0,0,0)">, j </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> args.length; i </span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)"> j; i</span><span style="COLOR: rgb(0,0,0)">++</span><span style="COLOR: rgb(0,0,0)">) {<br />            argsClass[i] </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> args[i].getClass();<br />        }<br /><br />        Method method </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> ownerClass.getMethod(methodName, argsClass);<br /><br />        </span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)"> method.invoke(</span><span style="COLOR: rgb(0,0,255)">null</span><span style="COLOR: rgb(0,0,0)">, args);<br />    }<br /><br /><br /><br />    </span><span style="COLOR: rgb(0,128,0)">/**</span><span style="COLOR: rgb(0,128,0)"><br />     * 新建实例<br />     *<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> className<br />     *            类名<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> args<br />     *            构造函数的参数<br />     * </span><span style="COLOR: rgb(128,128,128)">@return</span><span style="COLOR: rgb(0,128,0)"> 新建的实例<br />     * </span><span style="COLOR: rgb(128,128,128)">@throws</span><span style="COLOR: rgb(0,128,0)"> Exception<br />     </span><span style="COLOR: rgb(0,128,0)">*/</span><span style="COLOR: rgb(0,0,0)"><br />    </span><span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> Object newInstance(String className, Object[] args) </span><span style="COLOR: rgb(0,0,255)">throws</span><span style="COLOR: rgb(0,0,0)"> Exception {<br />        Class newoneClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> Class.forName(className);<br /><br />        Class[] argsClass </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,255)">new</span><span style="COLOR: rgb(0,0,0)"> Class[args.length];<br /><br />        </span><span style="COLOR: rgb(0,0,255)">for</span><span style="COLOR: rgb(0,0,0)"> (</span><span style="COLOR: rgb(0,0,255)">int</span><span style="COLOR: rgb(0,0,0)"> i </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,0)">0</span><span style="COLOR: rgb(0,0,0)">, j </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> args.length; i </span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)"> j; i</span><span style="COLOR: rgb(0,0,0)">++</span><span style="COLOR: rgb(0,0,0)">) {<br />            argsClass[i] </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> args[i].getClass();<br />        }<br /><br />        Constructor cons </span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)"> newoneClass.getConstructor(argsClass);<br /><br />        </span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)"> cons.newInstance(args);<br /><br />    }<br /><br /><br />    <br />    </span><span style="COLOR: rgb(0,128,0)">/**</span><span style="COLOR: rgb(0,128,0)"><br />     * 是不是某个类的实例<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> obj 实例<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> cls 类<br />     * </span><span style="COLOR: rgb(128,128,128)">@return</span><span style="COLOR: rgb(0,128,0)"> 如果 obj 是此类的实例，则返回 true<br />     </span><span style="COLOR: rgb(0,128,0)">*/</span><span style="COLOR: rgb(0,0,0)"><br />    </span><span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> </span><span style="COLOR: rgb(0,0,255)">boolean</span><span style="COLOR: rgb(0,0,0)"> isInstance(Object obj, Class cls) {<br />        </span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)"> cls.isInstance(obj);<br />    }<br />    <br />    </span><span style="COLOR: rgb(0,128,0)">/**</span><span style="COLOR: rgb(0,128,0)"><br />     * 得到数组中的某个元素<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> array 数组<br />     * </span><span style="COLOR: rgb(128,128,128)">@param</span><span style="COLOR: rgb(0,128,0)"> index 索引<br />     * </span><span style="COLOR: rgb(128,128,128)">@return</span><span style="COLOR: rgb(0,128,0)"> 返回指定数组对象中索引组件的值<br />     </span><span style="COLOR: rgb(0,128,0)">*/</span><span style="COLOR: rgb(0,0,0)"><br />    </span><span style="COLOR: rgb(0,0,255)">public</span><span style="COLOR: rgb(0,0,0)"> Object getByArray(Object array, </span><span style="COLOR: rgb(0,0,255)">int</span><span style="COLOR: rgb(0,0,0)"> index) {<br />        </span><span style="COLOR: rgb(0,0,255)">return</span><span style="COLOR: rgb(0,0,0)"> Array.get(array,index);<br />    }<br />}</span></div> <img src ="http://www.blogjava.net/renyangok/aggbug/93341.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-01-12 10:03 <a href="http://www.blogjava.net/renyangok/archive/2007/01/12/93341.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>URI , URL , URN 联系与区别</title><link>http://www.blogjava.net/renyangok/archive/2006/12/31/91176.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Sun, 31 Dec 2006 07:27:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/31/91176.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/91176.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/31/91176.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/91176.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/91176.html</trackback:ping><description><![CDATA[
		<p>URI(Uniform Resoure Identifier:统一资源标识符),URL(Uniform Resoure Location:统一资源定位器),URN(Uniform Resource Name统一资源</p>
		<p>名),URC(Uniform Resource Citation统一资源引用符)</p>
		<p>URI、URL和URN是识别、定位和命名互联网上的资源的标准途径; URL,URN是URI的子集.</p>
		<p>     URI不能定位或读取/写入资源。这是统一的资源定位器（URL）的任务。URL是一种URI，但是它的大纲组件是已知的网络协议（简称协议</p>
		<p>），并且它把URI组件与某种协议处理程序（一种资源定位器和根据协议建立的约束规则与资源通讯的读/写机制）。</p>
		<p>　　URI一般不能为资源提供持久不便的名称。这是统一的资源命名（URN）的任务。URN也是一种URI，但是全球唯一的、持久不便的，即使资</p>
		<p>源不在存在或不再使用。 </p>
		<p>      web上地址的基本形式是URI,它代表统一资源标识符。有两种形式：<br />      URL：目前URI的最普遍形式就是无处不在的URL或统一资源定位器。<br />      URN：URL的一种更新形式，统一资源名称(URN, Uniform Resource Name)不依赖于位置，并且有可能减少失效连接的个数。但是其流行</p>
		<p>还需假以时日，因为它需要更精密软件的支持。</p>
		<p>体系中的URI、URL和URN是彼此关联的。URI的范畴位于体系的顶层，URL和URN的范畴位于体系的底层。这种排列显示URL和URN都是URI的子范畴</p>
		<p>，　URI表示的是统一的资源标识，它是以某种统一的（标准化的）方式标识资源的简单字符串。典型情况下，这种字符串以scheme（命名URI</p>
		<p>的名字空间的标识符--一组相关的名称）开头，语法如下：</p>
		<p>[scheme:] scheme-specific-part </p>
		<p>URI以scheme和冒号开头。Scheme用大写/小写字母开头，后面为空或者跟着更多的大写/小写字母、数字、加号、减号和点号。冒号把scheme与</p>
		<p>scheme-specific-part分开了，并且scheme-specific-part的语法和语义（意思）由URI的名字空间决定。 　 </p>
		<p>其中一个例子是<a href="http://www.cnn.com">http://www.cnn.com</a>，其中http是scheme，//www.cnn.com是 scheme-specific-part，并且它的scheme与scheme-specific-</p>
		<p>part被冒号分开了。 </p>
		<p>我们可以把URI按照绝对的或相对的分类。绝对的URI指以scheme（后面跟着冒号）开头的URI。前面提到的<a href="http://www.cnn.com">http://www.cnn.com</a>就是绝对的URI</p>
		<p>的一个例子，其它的例子还有<a href="mailto:jeff@javajeff.com">mailto:jeff@javajeff.com</a>、<a href="news:comp.lang.java.help">news:comp.lang.java.help</a>和xyz://whatever。你可以把绝对的URI看作是以某种方</p>
		<p>式引用某种资源，而这种方式对标识符出现的环境没有依赖。 　　 </p>
		<p>如果使用文件系统作类比，绝对的URI类似于从根目录开始的某个文件的路径。与绝对的URI不同的，相对的URI不是以scheme（后面跟着冒号）</p>
		<p>开始的URI。 </p>
		<p>它的一个例子是articles/articles.html。你可以把相对的URI看作是以某种方式引用某种资源，而这种方式依赖于标识符出现的环境。如果用</p>
		<p>文件系统作类比，相对的URI类似于从当前目录开始的文件路径。</p>
		<p>URI：</p>
		<p>Web上可用的每种资源 - HTML文档、图像、视频片段、程序等 - 由一个通过通用资源标志符（Universal Resource Identifier, 简称"URI"）</p>
		<p>进行定位。 <br />URI一般由三部分组成：<br />1. 访问资源的命名机制。 <br />2. 存放资源的主机名。 <br />3. 资源自身的名称，由路径表示。 <br />注：大多数人可能熟悉"URL"，而不是URI。URL是URI命名机制的一个子集。</p>
		<p>URL：</p>
		<p>URL是Uniform Resource Location的缩写，译为"统一资源定位符"。通俗地说，URL是Internet上用来描述信息资源的字符串，主要用在各种</p>
		<p>WWW客户程序和服务器程序上，特别是著名的Mosaic。采用URL可以用一种统一的格式来描述各种信息资源，包括文件、服务器的地址和目录等</p>
		<p>。 <br />URL的格式<br />URL的格式由下列三部分组成： <br />第一部分是协议（或称为服务方式）； <br />第二部分是存有该资源的主机IP地址（有时也包括端口号）； <br />第三部分是主机资源的具体地址。，如目录和文件名等。 <br />第一部分和第二部分之间用"：//"符号隔开，第二部分和第三部分用"/"符号隔开。第一部分和第二部分是不可缺少的，第三部分有时可以省略</p>
		<p>。<br />URL的缺点：<br />当信息资源的存放地点发生变化时，必须对URL作相应的改变。因此人们正在研究新的信息资源表示方法，例如：URI(Universal Resource </p>
		<p>Identifier)即"通用资源标识"（参见RFC 1630）、URN（Uniform Resource Name）即"统一资源名"和URC（Uniform Resource Citation）即"</p>
		<p>统一资源引用符"等。</p>
		<p> </p>
<img src ="http://www.blogjava.net/renyangok/aggbug/91176.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-31 15:27 <a href="http://www.blogjava.net/renyangok/archive/2006/12/31/91176.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中ThreadLocal的设计与使用</title><link>http://www.blogjava.net/renyangok/archive/2006/12/27/90381.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Wed, 27 Dec 2006 13:37:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/27/90381.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/90381.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/27/90381.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/90381.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/90381.html</trackback:ping><description><![CDATA[
		<p>早在Java 1.2推出之时，Java平台中就引入了一个新的支持：java.lang.ThreadLocal，给我们在编写多线程程序时提供了一种新的选择。使用这个工具类可以很简洁地编写出优美的多线程程序，虽然ThreadLocal非常有用，但是似乎现在了解它、使用它的朋友还不多。 </p>
		<p>ThreadLocal是什么 </p>
		<p>ThreadLocal并非是一个线程的本地实现版本，它并不是一个Thread，而是thread local variable（线程局部变量）。也许把它命名为ThreadLocalVar更加合适。线程局部变量（ThreadLocal）其实的功用非常简单，就是为每一个使用该变量的线程都提供一个变量值的副本，是每一个线程都可以独立地改变自己的副本，而不会和其它线程的副本冲突。从线程的角度看，就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明，在其它的一些语言编译器实现（如IBM XL FORTRAN）中，它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持，而是提供了一个ThreadLocal的类来提供支持，所以，在Java中编写线程局部变量的代码相对比较笨拙，这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。 </p>
		<p>ThreadLocal的设计 </p>
		<p>首先看看ThreadLocal的接口： </p>
		<p>Object get() ; </p>
		<p>// 返回当前线程的线程局部变量副本 protected Object initialValue(); // 返回该线程局部变量的当前线程的初始值 </p>
		<p>void set(Object value); </p>
		<p>// 设置当前线程的线程局部变量副本的值 </p>
		<p>ThreadLocal有3个方法，其中值得注意的是initialValue()，该方法是一个protected的方法，显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值，这个方法是一个延迟调用方法，在一个线程第1次调用get()或者set(Object)时才执行，并且仅执行1次。ThreadLocal中的确实实现直接返回一个null： </p>
		<p>protected Object initialValue() { return null; } </p>
		<p>ThreadLocal是如何做到为每一个线程维护变量的副本的呢？其实实现的思路很简单，在ThreadLocal类中有一个Map，用于存储每一个线程的变量的副本。比如下面的示例实现： </p>
		<p>
				<br />public class ThreadLocal<br />{<br />private Map values = Collections.synchronizedMap(new HashMap());<br />public Object get()<br />{<br />Thread curThread = Thread.currentThread();<br />Object o = values.get(curThread);<br />if (o == null &amp;&amp; !values.containsKey(curThread))<br />{<br />o = initialValue();<br />values.put(curThread, o);<br />}<br />return o;<br />}</p>
		<p>public void set(Object newValue)<br />{<br />values.put(Thread.currentThread(), newValue);<br />}</p>
		<p>public Object initialValue()<br />{<br />return null;<br />}<br />}</p>
		<p> </p>
		<p>
				<br />当然，这并不是一个工业强度的实现，但JDK中的ThreadLocal的实现总体思路也类似于此。 </p>
		<p>ThreadLocal的使用 </p>
		<p>如果希望线程局部变量初始化其它值，那么需要自己实现ThreadLocal的子类并重写该方法，通常使用一个内部匿名类对ThreadLocal进行子类化，比如下面的例子，SerialNum类为每一个类分配一个序号 </p>
		<p>
				<br />public class SerialNum<br />{<br />// The next serial number to be assigned</p>
		<p>private static int nextSerialNum = 0;</p>
		<p>private static ThreadLocal serialNum = new ThreadLocal()<br />{<br />protected synchronized Object initialValue()<br />{<br />return new Integer(nextSerialNum++);<br />}<br />};</p>
		<p>
				<br />public static int get()<br />{<br />return ((Integer) (serialNum.get())).intValue();<br />}</p>
		<p>}</p>
		<p> </p>
		<p>
				<br />SerialNum类的使用将非常地简单，因为get()方法是static的，所以在需要获取当前线程的序号时，简单地调用： </p>
		<p>int serial = SerialNum.get(); </p>
		<p>即可。 </p>
		<p>在线程是活动的并且ThreadLocal对象是可访问的时，该线程就持有一个到该线程局部变量副本的隐含引用，当该线程运行结束后，该线程拥有的所以线程局部变量的副本都将失效，并等待垃圾收集器收集。 </p>
		<p>ThreadLocal与其它同步机制的比较 </p>
		<p>ThreadLocal和其它同步机制相比有什么优势呢？ThreadLocal和其它所有的同步机制都是为了解决多线程中的对同一变量的访问冲突，在普通的同步机制中，是通过对象加锁来实现多个线程对同一变量的安全访问的。这时该变量是多个线程共享的，使用这种同步机制需要很细致地分析在什么时候对变量进行读写，什么时候需要锁定某个对象，什么时候释放该对象的锁等等很多。所有这些都是因为多个线程共享了资源造成的。ThreadLocal就从另一个角度来解决多线程的并发访问，ThreadLocal会为每一个线程维护一个和该线程绑定的变量的副本，从而隔离了多个线程的数据，每一个线程都拥有自己的变量副本，从而也就没有必要对该变量</p>
<img src ="http://www.blogjava.net/renyangok/aggbug/90381.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-27 21:37 <a href="http://www.blogjava.net/renyangok/archive/2006/12/27/90381.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>2.4版本的servlet规范对filter增加的标签详解</title><link>http://www.blogjava.net/renyangok/archive/2006/12/22/89393.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Fri, 22 Dec 2006 00:46:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/22/89393.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/89393.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/22/89393.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/89393.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/89393.html</trackback:ping><description><![CDATA[2.4版本的servlet规范在部属描述符中新增加了一个&lt;dispatcher&gt;元素，这个元素有四个可能的值：即REQUEST,FORWARD,INCLUDE和ERROR，可以在一个&lt;filter-mapping&gt;元素中加入任意数目的&lt;dispatcher&gt;，使得filter将会作用于直接从客户端过来的request，通过forward过来的request，通过include过来的request和通过&lt;error-page&gt;过来的request。如果没有指定任何&lt;   dispatcher   &gt;元素，默认值是REQUEST。可以通过下面几个例子来辅助理解。   <br />  例1：   <br />  &lt;filter-mapping&gt;   <br />  &lt;filter-name&gt;Logging   Filter&lt;/filter-name&gt;   <br />  &lt;url-pattern&gt;/products/*&lt;/url-pattern&gt;   <br />  &lt;/filter-mapping&gt;   <br />  这种情况下，过滤器将会作用于直接从客户端发过来的以/products/…开始的请求。因为这里没有制定任何的&lt;   dispatcher   &gt;元素，默认值是REQUEST。   <br />    <br />  例2：   <br />  &lt;filter-mapping&gt;   <br />                  &lt;filter-name&gt;Logging   Filter&lt;/filter-name&gt;   <br />                  &lt;servlet-name&gt;ProductServlet&lt;/servlet-name&gt;   <br />                  &lt;dispatcher&gt;INCLUDE&lt;/dispatcher&gt;   <br />  &lt;/filter-mapping&gt;   <br />  这种情况下，如果请求是通过request   dispatcher的include方法传递过来的对ProductServlet的请求，则要经过这个过滤器的过滤。其它的诸如从客户端直接过来的对ProductServlet的请求等都不需要经过这个过滤器。   <br />  指定filter的匹配方式有两种方法：直接指定url-pattern和指定servlet，后者相当于把指定的servlet对应的url-pattern作为filter的匹配模式   <br />  filter的路径匹配和servlet是一样的，都遵循servlet规范中《SRV.11.2   Specification   of   Mappings》一节的说明   <br />    <br />  例3：   <br />  &lt;filter-mapping&gt;   <br />                  &lt;filter-name&gt;Logging   Filter&lt;/filter-name&gt;   <br />                  &lt;url-pattern&gt;/products/*&lt;/url-pattern&gt;   <br />                  &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt;   <br />                  &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;   <br />  &lt;/filter-mapping&gt;   <br />  在这种情况下，如果请求是以/products/…开头的，并且是通过request   dispatcher的forward方法传递过来或者直接从客户端传递过来的，则必须经过这个过滤器。   <br /><img src ="http://www.blogjava.net/renyangok/aggbug/89393.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-22 08:46 <a href="http://www.blogjava.net/renyangok/archive/2006/12/22/89393.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java生成UUID(Universally Unique Identifier)</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87189.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:33:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87189.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87189.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87189.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87189.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87189.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: UUID(Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字，它保证对在同一时空中的所有机器都是唯一的。按照开放软件基金会(OSF)制定的标准计算，用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字。由以下几部分的组合：当前日期和时间(UUID的第一个部分与时间有关，如果你在生成一个UUID之后，过几秒又生成一个UUID，则第一个部分不同...&nbsp;&nbsp;<a href='http://www.blogjava.net/renyangok/archive/2006/12/12/87189.html'>阅读全文</a><img src ="http://www.blogjava.net/renyangok/aggbug/87189.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:33 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87189.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>候捷谈Java反射机制</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87186.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:32:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87186.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87186.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87186.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87186.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87186.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Java														反射机制																																																																																										 																																摘要					...&nbsp;&nbsp;<a href='http://www.blogjava.net/renyangok/archive/2006/12/12/87186.html'>阅读全文</a><img src ="http://www.blogjava.net/renyangok/aggbug/87186.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:32 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87186.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>practical java &amp; effective java 读书笔记</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87187.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:32:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87187.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87187.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87187.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87187.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87187.html</trackback:ping><description><![CDATA[
		<p> 第一章 一般技术<br />1.java只有唯一一种参数传递方式：by value（值传递）。对于primitive types（基本型别）很容易理解，对于object references（对象引用），传递的是object reference的拷贝。<br />2.polymorphism（多态）优于instanceof：instanceof很容易被误用，很多场合都应该以多态代替，无论何时看到instanceof，请判断是否可以改进以消除它。<br />3.避免创建重复对象。比如一个类A的某个方法新建了一个类B，且此类B不会改变，则每次建立该类A的一个对象就会新建B的对象，此时应把<br />B设为private static final。<br />4.清除过期的对象引用。<br />5.避免使用终结函数。因为终结函数可能得不到执行或很久后得到执行，所以要避免使用。显示的中止方法通常与try-finally结构结合使用，防止出现异常时终结函数得不到执行。<br />eg: Foo foo = new Foo(...);<br />    try{<br />        //do what must be done with foo    <br />    }finally{<br />        foo.terminate();<br />    }<br />6.通过私有构造函数来强化不可实例化的能力。比如一些工具类不希望被实例化，然而在缺少显示构造函数时编译器会自动提供一个默认构造函数，为防止以上情况要构造一个显示的私有的构造函数。<br />eg:public class UtilityClass{<br />     private UtilityClass(){<br />     }<br />   }<br />7.通过私有构造函数强化singleton属性。singleton是指这样的类，它只能实例化一次。singleton通常被用来代表那些本质上具有唯一性的系统组件，比如视频显示或者文件系统。<br />  eg:public class Elvis{<br />       public static final Elvis INSTANCE = new Elvis();<br />       private Elvis(){<br />       }<br />     } <br />8.考虑用静态工厂方法代替构造函数，但如果没有其他强烈的因素，最好还是简单的使用构造函数，毕竟它是语言规范。静态工厂方法实际上是一个简单的静态方法，他返回的是类的一个实例。<br />  有点：a.与构造函数不同，静态工厂方法具有名字。<br />        b.与构造函数不同，它们每次被调用的时候不要求非得创建一个对象。<br />        c.与构造函数不同，与构造函数不同，它们可以返回一个原类型的子类型对象。<br />第二章 所有对象都通用的方法（equals(),hashCode(),toString(),clone(),Comparable接口）<br />一.按规则使用equals():<br />1.使用equals的规则：<br />  a.如果一个class的两个对象占据不同的内存空间也可被视为逻辑相等的话，那么得为这个class提供一个equals()<br />  b.检查是否等于this<br />  c.比较关键域以判断两个对象是否相等<br />  d.如果有java.lang.Object以外的任何base class实现了equals(),那么就应该调用super.equals()<br />  e.如果只允许同一个class所产生的对象被视为相等，则通常使用getClass()<br />    eg1:一般情况<br />    public boolean equals(Object obj){<br />        if(this == obj){<br />          return true;<br />        }<br />        if(obj != nul &amp;&amp; getClass() == obj.getClass()){<br />          Test test = (Test)obj;<br />          if(***){//相等条件<br />              return true;<br />          }<br />        }<br />        return false;<br />      }<br />    eg2:调用super.equals()情况<br />    public boolean equals(Object obj){<br />      if(super.equals(obj)){//已经包含了this == obj; obj !=null &amp;&amp; getClass() == obj.getClass()的判断<br />        Test test = (Test)obj;<br />          if(***){//相等条件<br />              return true;<br />          }<br />        }<br />        return false;<br />      }</p>
		<p>  f.只有在不得不对derived class对象与base classes对象进行比较的场合中，才使用instanceof，并且你应该明白这样做带来的可能问题和复杂性,并且derived class和base classes都用instanceof实现equals()时，这种比较不会展现“对称相等性”。<br />    Base b;Derived d;//分别表示父类、子类<br />    1)父类实现equals，子类继承父类的equals，b.equals(d) == d.equals(d)；<br />    2)父类子类分别实现了equals，b.equals(d) != d.equals(b);<br />    3)父类未实现equals，子类实现了equals，b.equals(d) != d.equals(b);<br />2.对于既不是float也不是double类型的primitive types，使用==操作符；对于对象引用域，可以递归的调用equals方法；对于float域，先使用Float.floatToIntBits转换成int类型值，然后使用==操作符比较int类型的值；对于double域，先使用Double.doubleToLongBits转换成int类型的值，然后使用==操作符比较long类型的值.(这是由于存在Float.NaN、-0.0f以及类似的double类型的常量)<br />二.hashCode():<br />1。改写equals时总是要改写hashCode方法，如果不这样作，会导致该类无法与所有基于散列值（hash）的集合类在一起正常工作，这样的集合类包括HashMap、HashSet、HashTable<br />2。hashCode方法的简单方法：<br />  1。把某个非零数值（比如17），保存在int result变量里。<br />  2。对于对象中每一个关键域f（指equals方法中考虑的每一个域），完成以下步骤：<br />  a）为该域计算int类型的散列码c：<br />    i.该域为boolean型，c = f ? 0 : 1<br />    ii.byte, char, short, int型， c = (int)f<br />    iii.long型， c = (int)(f ^ (f &gt;&gt;&gt; 32))<br />    iv.float型， c = Float.floatToIntBits(f)<br />    v.double型， Double.doubleToLongBits(f)得到long型，然后按iii计算散列值<br />    vi.如果是对象引用，c = (this.*** == null) ? 0 : this.***.hashCode();<br />    vii.如果该域是个数组，则把其中每一个元素当作单独的域来处理<br />   b)result = 37 * result + c;//把每个c都组合到result中<br />   3。返回result<br />   eg1：<br /> public int hashCode() {<br />     int result = 17;<br />     //对于关键域是id的情况<br />     int idValue = (this.getId() == null) ? 0 : this.getId().hashCode();<br />     result = (result * 37) + idValue;<br />     //如果还有第二个关键域name<br />     //int nameValue = (this.getName() == null) ? 0 : this.getName().hashCode();<br />     //result = (result * 37) + nameValue;<br />     this.hashValue = result;<br /> return this.hashValue;<br /> }<br />    eg2:<br /> 如果一个类是非可变的，并且计算散列码代价较大，则应把散列码存到对象内部：<br /> private int hashValue = 17;//先定义hashValue，不需要get/set方法<br /> ........................<br /> public int hashCode() {//对于关键域是id的情况<br />     if (this.hashValue == 17) {<br />  int result = 17;<br />  int idValue = (this.getId() == null) ? 0 : this.getId().hashCode();<br />  result = (result * 37) + idValue;<br />  //如果还有第二个关键域name<br />  //int nameValue = (this.getName() == null) ? 0 : this.getName().hashCode();<br />  //result = (result * 37) + nameValue;<br />  this.hashValue = result;<br />     }<br />     return this.hashValue;<br /> }<br />三。toString():会使这个类用起来更加方便。<br />四。谨慎的改写clone()。实现拷贝的方法有两个：一是实现cloneable接口（effective java 39页，没仔细看），二是提供拷贝构造函数<br />  public Yum(Yum yum);<br />  或是上面的微小变形：提供一个静态工厂来代替构造函数：<br />  public static Yum newInstance(Yum yum);<br />五、用到搜索、排序、计算极值的情况时，考虑实现Comparable接口。<br />public int compareTo(Object o)//方法不需要手工检查参数的类型，如参数类型不符合会抛出ClassCastException；如参数为null，该方法抛出NullPointerException。<br />第三章 类和接口<br />1。使类和成员（变量、方法、内部类、内部接口）的可访问能力最小化。<br />2。private和friendly成员都是一个类实现中的一部分，并不会影响到导出API。然而，如果这些域所在的类实现了Serializable接口，那么这些成员可能会被泄漏到导出API中。<br />3。如果一个方法改写了超类中的一个方法，那么子类中该方法的访问级别不能低于父类中该方法的访问级别。特别是：类实现了接口，那么接口中的方法在这个类中必须声明为公有的，因为接口中方法默认为public abstract。<br />六、异常处理<br />1.决不可忽略异常，即catch后什么也不做。<br />2.决不可掩盖异常<br />try{<br />  e1;//异常1<br />  e2;//异常2<br />}catch(Exception e){<br />  e.printStackTrace()<br />}//只能捕获异常2<br />办法：要仔细分析，用栈来保存异常<br />3.覆写异常处理时：<br />父类不抛出异常时，自类不能抛出异常。<br />父类抛出异常时，自类三种情况：a）不抛出异常b）抛出父类异常c）抛出父类异常的派生异常。<br />4.只要有finally块就一定会进入，即使try－catch块有return/break/continue语句。<br />5.养成将try/catch块放在循环外的习惯，在不启动JIT时节省时间。</p>
<img src ="http://www.blogjava.net/renyangok/aggbug/87187.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:32 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87187.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java范型功略</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87185.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:31:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87185.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87185.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87185.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87185.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87185.html</trackback:ping><description><![CDATA[
		<!--StartFragment --> 在已发布的Java1.4中在核心代码库中增加了许多新的API(如Loging,正则表达式，NIO)等，在最新发布的JDK1.5和即将发布的JDK1.6中也新增了许多API，其中比较有重大意义的就是Generics(范型）。<br /><br /><strong>一.什么是Generics?</strong><br /><br />Generics可以称之为参数类型(parameterized types),由编译器来验证从客户端将一种类型传送给某一对象的机制。如Java.util.ArrayList,编译器可以用Generics来保证类型安全。<br />在我们深入了解Generics之前,我们先来看一看当前的java 集合框架（Collection)。在j2SE1.4中所有集合的Root Interface是Collection<br /><br />Collections example without genericity: Example 1<br /><br /><br /><pre class="overflow">1 protected void collectionsExample() {<br />2  ArrayList list = new ArrayList();<br />3  list.add(new String("test string"));<br />4  list.add(new Integer(9)); // purposely placed here to create a runtime ClassCastException<br />5  inspectCollection(list);<br />6 }<br />7<br />8<br />9 protected void inspectCollection(Collection aCollection) {<br />10  Iterator i = aCollection.iterator();<br />11  while (i.hasNext()) {<br />12   String element = (String) i.next();<br />13  }<br />14 }</pre><br /><br />以上的样例程序包含的两个方法，collectionExample方法建立了一个简单的集合类型ArrayList，并在ArrayList中增加了一个 String和一个Integer对象.而在inspecCollection方法中，我们迭代这个ArrayList用String进行Cast。我们看第二个方法，就出现了一个问题,Collection在内部用的是Object，而我们要取出Collection中的对象时,需要进行Cast，那么开发者必需用实际的类型进行Cast,像这种向下造型，编译器无法进行检查,如此一来我们就要冒在代码在运行抛出ClassCastException的危险。我们看inspecCollection方法，编译时没有问题，但在运行时就会抛出ClassCastException异常。所以我们一定要远离这个重大的运行时错误<br /><br /><br /><strong>二.使用Generics</strong><br />从上一章节中的CassCastException这种异常，我们期望在代码编译时就能够捕捉到,下面我们使用范型修改上一章的样例程序。<br />//Example 2<br /><pre class="overflow">1 protected void collectionsExample() {<br />2  ArrayList&lt;String&gt; list = new ArrayList&lt;String&gt;();<br />3  list.add(new String("test string"));<br />4  // list.add(new Integer(9)); this no longer compiles<br />5  inspectCollection(list);<br />6 }<br />7 <br />8 <br />9 protected void inspectCollection(Collection&lt;String&gt; aCollection) {<br />10  Iterator&lt;String&gt; i = aCollection.iterator();<br />11  while(i.hasNext()) {<br />12   String element = i.next();<br />13  }<br />14 }</pre><br /><br />从上面第2行我们在创建ArrayList时使用了新语法，在JDK1.5中所有的Collection都加入了Generics的声明。例:<br />//Example 3<br /><pre class="overflow">1 public class ArrayList&lt;E&gt; extends AbstractList&lt;E&gt; {<br />2  // details omitted...<br />3  public void add(E element) {<br />4   // details omitted<br />5  }<br />6  public Iterator&lt;E&gt; iterator() {<br />7   // details omitted<br />8  }<br />9 }</pre><br /><br />这个E是一个类型变量，并没有对它进行具体类型的定义,它只是在定义ArrayList时的类型占位符,在Example 2中的我们在定义ArrayList的实例时用String绑定在E上,当我们用add(E element)方法向ArrayList中增加对象时, 那么就像下面的写法一样： public void add(String element)；因为在ArrayList所有方法都会用String来替代E,无论是方法的参数还是返回值。这时我们在看Example 2中的第四行，编译就会反映出编译错误。<br />所以在java中增加Generics主要的目的是为了增加类型安全。<br /><br />通过上面的简单的例子我们看到使用Generics的好处有：<br />1.在类型没有变化时，Collection是类型安全的。<br />2.内在的类型转换优于在外部的人工造型。<br />3.使Java 接口更加强壮,因为它增加了类型。<br />4.类型的匹配错误在编译阶段就可以捕捉到，而不是在代码运行时。<br /><br />受约束类型变量<br />虽然许多Class被设计成Generics，但类型变量可以是受限的<br />public class C1&lt;T extends Number&gt; { }<br />public class C2&lt;T extends Person &amp; Comparable&gt; { } <br />第一个T变量必须继承Number，第二个T必须继承Person和实现Comparable<br /><br /><strong>三.Generics 方法</strong><br /><br />像Generics类一样，方法和构造函数也可以有类型参数。方法的参数和返回值都可以有类型参数，进行Generics。<br />//Example 4<br /><pre class="overflow">1 public &lt;T extends Comparable&gt; T max(T t1, T t2) {<br />2  if (t1.compareTo(t2) &gt; 0)<br />3   return t1;<br />4  else return t2;<br />5 }</pre><br /><br />这里，max方法的参数类型为单一的T类型，而T类型继承了Comparable，max的参数和返回值都有相同的超类。下面的Example 5显示了max方法的几个约束。<br />//Example 5　<br /><pre class="overflow">1 Integer iresult = max(new Integer(100), new Integer(200));<br />2 String sresult = max("AA", "BB");<br />3 Number nresult = max(new Integer(100), "AAA"); // does not compile</pre><br /><br />在Example 5第1行参数都为Integer，所以返回值也是Integer，注意返回值没有进行造型。<br />在Example 5第2行参数都为String，所以返回值也是String，注意返回值没有进行造型。以上都调用了同一个方法。<br />在Example 5第3行产生以下编译错误：<br /><span style="COLOR: blue">Example.java:10: incompatible types<br />found  : java.lang.Object&amp;java.io.Serializable&amp;java.lang.Comparable&lt;?&gt;<br />required: java.lang.Number<br />    Number nresult = max(new Integer(100), "AAA");</span><br /><br />这个错误发生是因为编译器无法确定返回值类型，因为String和Integer都有相同的超类Object,注意就算我们修正了第三行，这行代码在运行仍然会报错，因为比较了不同的对象。<br /><br /><strong>四.向下兼容</strong><br />任何一个新的特色在新的JDK版本中出来后，我们首先关心的是如何于以前编写的代码兼容。也就是说我们编写的Example 1程序不需要任何的改变就可以运行,但是编译器会给出一个"ROW TYPE"的警告。<font color="#ff0000">在JDK1.4中编写的代码如何在JVM1.5中完全兼容运行,我们要人工进行一个:Type erasure处理过程</font><br /><br /><strong>五.通配符</strong><br /><br />//Example 6<br /><pre class="overflow">List&lt;String&gt; stringList = new ArrayList&lt;String&gt;(); //1<br />List&lt;Object&gt; objectList = stringList ;//2<br />objectList .add(new Object()); // 3<br />String s = stringList .get(0);//4</pre>乍一看，Example 6 是正确的。但stringList本意是存放String类型的ArrayList,而objectList中可以存入任何对象，当在第3行进行处理时， stringList也就无法保证是String类型的ArrayList,此时编译器不允许这样的事出现，所以第3行将无法编译。<br /><br />//Example 7<br /><pre class="overflow">void printCollection(Collection&lt;Object&gt; c) <br />{ for (Object e : c) { <br />System.out.println(e);<br />}}</pre><br /><br />Example 7的本意是打印所有Collection的对象,但是正如Example 6所说的，编译会报错，此时就可以用通配符“？”来修改Example 7<br /><br />//Example 8<br /><pre class="overflow">void printCollection(Collection&lt;?&gt; c) <br />{ for (Object e : c) { <br />System.out.println(e);<br />}}</pre><br /><br />Example 8中所有Collection类型就可以方便的打印了<br /><br />有界通配符 &lt;T extends Number&gt;(上界) &lt;T super Number&gt;(下界) <br /><br /><strong>六.创建自己的范型</strong><br />以下代码来自http://www.java2s.com/ExampleCode/Language-Basics<br />1.一个参数的Generics<br />//Example 9(没有使用范型)<br /><pre class="overflow">class NonGen {  <br />  Object ob; // ob is now of type Object <br />  // Pass the constructor a reference to   <br />  // an object of type Object <br />  NonGen(Object o) {  <br />    ob = o;  <br />  }  <br />  // Return type Object. <br />  Object getob() {  <br />    return ob;  <br />  }  <br />  // Show type of ob.  <br />  void showType() {  <br />    System.out.println("Type of ob is " +  <br />                       ob.getClass().getName());  <br />  }  <br />}  <br />// Demonstrate the non-generic class.  <br />public class NonGenDemo {  <br />  public static void main(String args[]) {  <br />    NonGen iOb;   <br />    // Create NonGen Object and store <br />    // an Integer in it. Autoboxing still occurs. <br />    iOb = new NonGen(88);  <br />    // Show the type of data used by iOb. <br />    iOb.showType(); <br />    // Get the value of iOb. <br />    // This time, a cast is necessary. <br />    int v = (Integer) iOb.getob();  <br />    System.out.println("value: " + v);  <br />    System.out.println();  <br />    // Create another NonGen object and  <br />    // store a String in it. <br />    NonGen strOb = new NonGen("Non-Generics Test");  <br />    // Show the type of data used by strOb. <br />    strOb.showType(); <br />    // Get the value of strOb. <br />    // Again, notice that a cast is necessary.  <br />    String str = (String) strOb.getob();  <br />    System.out.println("value: " + str);  <br />    // This compiles, but is conceptually wrong! <br />    iOb = strOb; <br />    v = (Integer) iOb.getob(); // runtime error! <br />  }  <br />}</pre>  <br /><br />//Example 10(使用范型)<br /><pre class="overflow">class Example1&lt;T&gt;{<br /> private T t;<br /> Example1(T o){<br />  this.t=o;<br />  }<br /> T getOb(){<br />  return t;<br /> }<br /> void ShowObject(){<br />  System.out.println("对象的类型是："+t.getClass().getName());<br /> }<br />}<br />public class GenericsExample1 {<br /><br /> /**<br />  * @param args<br />  */<br /> public static void main(String[] args) {<br />  // TODO Auto-generated method stub<br />  Example1&lt;Integer&gt; examplei=new Example1&lt;Integer&gt;(100);<br />  examplei.ShowObject();<br />  System.out.println("对象是："+examplei.getOb());<br />  Example1&lt;String&gt; examples=new Example1&lt;String&gt;("Bill");<br />  examples.ShowObject();<br />  System.out.println("对象是："+examples.getOb());<br /> }<br /><br />}</pre><br /><br />我们来看Example 9没有使用范型，所以我们需要进行造型，而Example 10我们不需要任何的造型<br /><br />2.二个参数的Generics<br /><br />//Example 11<br /><pre class="overflow">class TwoGen&lt;T, V&gt; { <br />   T ob1; <br />   V ob2; <br />   // Pass the constructor a reference to  <br />   // an object of type T. <br />   TwoGen(T o1, V o2) { <br />     ob1 = o1; <br />     ob2 = o2; <br />   } <br />   // Show types of T and V. <br />   void showTypes() { <br />     System.out.println("Type of T is " + <br />                        ob1.getClass().getName()); <br />     System.out.println("Type of V is " + <br />                        ob2.getClass().getName()); <br />   } <br />   T getob1() { <br />     return ob1; <br />   } <br />   V getob2() { <br />     return ob2; <br />   } <br /> } <br /><br />public class GenericsExampleByTwoParam {<br /><br /> /**<br />  * @param args<br />  */<br /> public static void main(String[] args) {<br />  // TODO Auto-generated method stub<br />  TwoGen&lt;Integer, String&gt; tgObj = <br />       new TwoGen&lt;Integer, String&gt;(88, "Generics"); <br />     // Show the types. <br />     tgObj.showTypes(); <br />     // Obtain and show values. <br />     int v = tgObj.getob1(); <br />     System.out.println("value: " + v); <br />     String str = tgObj.getob2(); <br />     System.out.println("value: " + str); <br />   } <br /><br /> }</pre><br /><br />3.Generics的Hierarchy<br /><br />//Example 12<br /><pre class="overflow">class Stats&lt;T extends Number&gt; {  <br />   T[] nums; // array of Number or subclass <br />   // Pass the constructor a reference to   <br />   // an array of type Number or subclass. <br />   Stats(T[] o) {  <br />     nums = o;  <br />   }  <br />   // Return type double in all cases. <br />   double average() {  <br />     double sum = 0.0; <br />     for(int i=0; i &lt; nums.length; i++)  <br />       sum += nums[i].doubleValue(); <br />     return sum / nums.length; <br />   }  <br /> }  <br />public class GenericsExampleByHierarchy {<br /><br /><br /> /**<br />  * @param args<br />  */<br /> public static void main(String[] args) {<br />  // TODO Auto-generated method stub<br /><br />   Integer inums[] = { 1, 2, 3, 4, 5 }; <br />     Stats&lt;Integer&gt; iob = new Stats&lt;Integer&gt;(inums);   <br />     double v = iob.average(); <br />     System.out.println("iob average is " + v); <br />     Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 }; <br />     Stats&lt;Double&gt; dob = new Stats&lt;Double&gt;(dnums);   <br />     double w = dob.average(); <br />     System.out.println("dob average is " + w); <br />     // This won't compile because String is not a <br />     // subclass of Number. <br />//     String strs[] = { "1", "2", "3", "4", "5" }; <br />//     Stats&lt;String&gt; strob = new Stats&lt;String&gt;(strs);   <br />//     double x = strob.average(); <br />//     System.out.println("strob average is " + v); <br />   }  <br /> }</pre>  <br /><br />4.使用通配符<br />//Example 14<br /><pre class="overflow">class StatsWildCard&lt;T extends Number&gt; {<br /> T[] nums; // array of Number or subclass<br /> // Pass the constructor a reference to<br /> // an array of type Number or subclass.<br /> StatsWildCard(T[] o) {<br />  nums = o;<br /> }<br /> // Return type double in all cases.<br /> double average() {<br />  double sum = 0.0;<br />  for (int i = 0; i &lt; nums.length; i++)<br />   sum += nums[i].doubleValue();<br />  return sum / nums.length;<br /> }<br /> // Determine if two averages are the same.<br /> // Notice the use of the wildcard.<br /> boolean sameAvg(StatsWildCard&lt;?&gt; ob) {<br />  if (average() == ob.average())<br />   return true;<br />  return false;<br /> }<br />}<br /><br />public class GenericsExampleByWildcard {<br /><br /> /**<br />  * @param args<br />  */<br /> public static void main(String[] args) {<br />  // TODO Auto-generated method stub<br />  Integer inums[] = { 1, 2, 3, 4, 5 };<br />  StatsWildCard&lt;Integer&gt; iob = new StatsWildCard&lt;Integer&gt;(inums);<br />  double v = iob.average();<br />  System.out.println("iob average is " + v);<br />  Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };<br />  StatsWildCard&lt;Double&gt; dob = new StatsWildCard&lt;Double&gt;(dnums);<br />  double w = dob.average();<br />  System.out.println("dob average is " + w);<br />  Float fnums[] = { 1.0F, 2.0F, 3.0F, 4.0F, 5.0F };<br />  StatsWildCard&lt;Float&gt; fob = new StatsWildCard&lt;Float&gt;(fnums);<br />  double x = fob.average();<br />  System.out.println("fob average is " + x);<br />  // See which arrays have same average.<br />  System.out.print("Averages of iob and dob ");<br />  if (iob.sameAvg(dob))<br />   System.out.println("are the same.");<br />  else<br />   System.out.println("differ.");<br />  System.out.print("Averages of iob and fob ");<br />  if (iob.sameAvg(fob))<br />   System.out.println("are the same.");<br />  else<br />   System.out.println("differ.");<br /><br /> }<br /><br />}</pre><br /><br /><strong>5.使用边界通配符</strong><br />//Example 15<br /><pre class="overflow">class TwoD { <br />  int x, y; <br />  TwoD(int a, int b) { <br />    x = a; <br />    y = b; <br />  } <br />} <br />// Three-dimensional coordinates. <br />class ThreeD extends TwoD { <br />  int z; <br />  ThreeD(int a, int b, int c) { <br />    super(a, b); <br />    z = c; <br />  } <br />} <br />// Four-dimensional coordinates. <br />class FourD extends ThreeD { <br />  int t; <br />  FourD(int a, int b, int c, int d) { <br />    super(a, b, c); <br />    t = d;  <br />  } <br />} <br />// This class holds an array of coordinate objects. <br />class Coords&lt;T extends TwoD&gt; { <br />  T[] coords; <br />  Coords(T[] o) { coords = o; } <br />} <br />// Demonstrate a bounded wildcard. <br />public class BoundedWildcard { <br />  static void showXY(Coords&lt;?&gt; c) { <br />    System.out.println("X Y Coordinates:"); <br />    for(int i=0; i &lt; c.coords.length; i++) <br />      System.out.println(c.coords[i].x + " " + <br />                         c.coords[i].y); <br />    System.out.println(); <br />  } <br />  static void showXYZ(Coords&lt;? extends ThreeD&gt; c) { <br />    System.out.println("X Y Z Coordinates:"); <br />    for(int i=0; i &lt; c.coords.length; i++) <br />      System.out.println(c.coords[i].x + " " + <br />                         c.coords[i].y + " " + <br />                         c.coords[i].z); <br />    System.out.println(); <br />  } <br />  static void showAll(Coords&lt;? extends FourD&gt; c) { <br />    System.out.println("X Y Z T Coordinates:"); <br />    for(int i=0; i &lt; c.coords.length; i++) <br />      System.out.println(c.coords[i].x + " " + <br />                         c.coords[i].y + " " + <br />                         c.coords[i].z + " " + <br />                         c.coords[i].t); <br />    System.out.println(); <br />  } <br />  public static void main(String args[]) { <br />    TwoD td[] = { <br />      new TwoD(0, 0), <br />      new TwoD(7, 9), <br />      new TwoD(18, 4), <br />      new TwoD(-1, -23) <br />    }; <br />    Coords&lt;TwoD&gt; tdlocs = new Coords&lt;TwoD&gt;(td);     <br />    System.out.println("Contents of tdlocs."); <br />    showXY(tdlocs); // OK, is a TwoD <br />//  showXYZ(tdlocs); // Error, not a ThreeD <br />//  showAll(tdlocs); // Erorr, not a FourD <br />    // Now, create some FourD objects. <br />    FourD fd[] = { <br />      new FourD(1, 2, 3, 4), <br />      new FourD(6, 8, 14, 8), <br />      new FourD(22, 9, 4, 9), <br />      new FourD(3, -2, -23, 17) <br />    }; <br />    Coords&lt;FourD&gt; fdlocs = new Coords&lt;FourD&gt;(fd);     <br />    System.out.println("Contents of fdlocs."); <br />    // These are all OK. <br />    showXY(fdlocs);  <br />    showXYZ(fdlocs); <br />    showAll(fdlocs); <br />  } <br />} </pre><br /><br /><br />6.ArrayList的Generics<br />//Example 16<br /><pre class="overflow">public class ArrayListGenericDemo {<br />  public static void main(String[] args) {<br />    ArrayList&lt;String&gt; data = new ArrayList&lt;String&gt;();<br />    data.add("hello");<br />    data.add("goodbye");<br /><br />    // data.add(new Date()); This won't compile!<br /><br />    Iterator&lt;String&gt; it = data.iterator();<br />    while (it.hasNext()) {<br />      String s = it.next();<br />      System.out.println(s);<br />    }<br />  }<br />} </pre><br /><br />7.HashMap的Generics<br />//Example 17<br /><pre class="overflow">public class HashDemoGeneric {<br />  public static void main(String[] args) {<br />    HashMap&lt;Integer,String&gt; map = new HashMap&lt;Integer,String&gt;();<br /><br />    map.put(1, "Ian");<br />    map.put(42, "Scott");<br />    map.put(123, "Somebody else");<br /><br />    String name = map.get(42);<br />    System.out.println(name);<br />  }<br />} </pre><br /><br />8.接口的Generics<br />//Example 18<br /><pre class="overflow">interface MinMax&lt;T extends Comparable&lt;T&gt;&gt; { <br />  T min(); <br />  T max(); <br />} <br />// Now, implement MinMax <br />class MyClass&lt;T extends Comparable&lt;T&gt;&gt; implements MinMax&lt;T&gt; { <br />  T[] vals; <br />  MyClass(T[] o) { vals = o; } <br />  // Return the minimum value in vals. <br />  public T min() { <br />    T v = vals[0]; <br />    for(int i=1; i &lt; vals.length; i++) <br />      if(vals[i].compareTo(v) &lt; 0) v = vals[i]; <br />    return v; <br />  } <br />  // Return the maximum value in vals. <br />  public T max() { <br />    T v = vals[0]; <br />    for(int i=1; i &lt; vals.length; i++) <br />      if(vals[i].compareTo(v) &gt; 0) v = vals[i]; <br />    return v; <br />  } <br />} <br />public class GenIFDemo { <br />  public static void main(String args[]) { <br />    Integer inums[] = {3, 6, 2, 8, 6 }; <br />    Character chs[] = {'b', 'r', 'p', 'w' }; <br />    MyClass&lt;Integer&gt; iob = new MyClass&lt;Integer&gt;(inums); <br />    MyClass&lt;Character&gt; cob = new MyClass&lt;Character&gt;(chs); <br />    System.out.println("Max value in inums: " + iob.max()); <br />    System.out.println("Min value in inums: " + iob.min()); <br />    System.out.println("Max value in chs: " + cob.max()); <br />    System.out.println("Min value in chs: " + cob.min()); <br />  } <br />}</pre><br /><br />9.Exception的Generics<br />//Example 20<br /><pre class="overflow">interface Executor&lt;E extends Exception&gt; {<br />    void execute() throws E;<br />}<br /><br />public class GenericExceptionTest {<br />    public static void main(String args[]) {<br />        try {<br />            Executor&lt;IOException&gt; e =<br />                new Executor&lt;IOException&gt;() {<br />                public void execute() throws IOException<br />                {<br />                    // code here that may throw an<br />                    // IOException or a subtype of<br />                    // IOException<br />                }<br />            };<br /><br />            e.execute();<br />        } catch(IOException ioe) {<br />            System.out.println("IOException: " + ioe);<br />            ioe.printStackTrace();<br />        }<br />    }<br />}  </pre><br /><br /><p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=661415</p><img src ="http://www.blogjava.net/renyangok/aggbug/87185.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:31 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87185.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java虚拟机的深入研究</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87184.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:30:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87184.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87184.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87184.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87184.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87184.html</trackback:ping><description><![CDATA[
		<div align="left">
				<p class="title" align="center">Java虚拟机的深入研究</p>
				<p align="center">作者：刘学超 </p>
				<p>
						<b>1  Java技术与Java虚拟机</b>
				</p>
				<p class="normal">说起Java，人们首先想到的是Java编程语言，然而事实上，Java是一种技术，它由四方面组成: Java编程语言、Java类文件格式、Java虚拟机和Java应用程序接口(Java API)。它们的关系如下图所示：</p>
				<p align="center">
						<img height="397" alt="" src="http://gceclub.sun.com.cn/staticcontent/html/2004-04-09/jvm01.gif" width="529" border="0" />
				</p>
				<p align="center">图1  Java四个方面的关系</p>
				<p class="normal">运行期环境代表着Java平台，开发人员编写Java代码(.java文件)，然后将之编译成字节码(.class文件)。最后字节码被装入内存，一旦字节码进入虚拟机，它就会被解释器解释执行，或者是被即时代码发生器有选择的转换成机器码执行。从上图也可以看出Java平台由Java虚拟机和Java应用程序接口搭建，Java语言则是进入这个平台的通道，用Java语言编写并编译的程序可以运行在这个平台上。这个平台的结构如下图所示：</p>
				<p align="center">
						<img height="251" alt="" src="http://gceclub.sun.com.cn/staticcontent/html/2004-04-09/jvm02.gif" width="609" border="0" />
				</p>
				<p class="normal">在Java平台的结构中, 可以看出，Java虚拟机(JVM) 处在核心的位置，是程序与底层操作系统和硬件无关的关键。它的下方是移植接口，移植接口由两部分组成：适配器和Java操作系统, 其中依赖于平台的部分称为适配器；JVM 通过移植接口在具体的平台和操作系统上实现；在JVM 的上方是Java的基本类库和扩展类库以及它们的API， 利用Java API编写的应用程序(application) 和小程序(Java applet) 可以在任何Java平台上运行而无需考虑底层平台, 就是因为有Java虚拟机(JVM)实现了程序与操作系统的分离，从而实现了Java 的平台无关性。 </p>
				<p class="normal">那么到底什么是Java虚拟机(JVM)呢？通常我们谈论JVM时，我们的意思可能是： </p>
				<ol>
						<li>对JVM规范的的比较抽象的说明； 
</li>
						<li>对JVM的具体实现； 
</li>
						<li>在程序运行期间所生成的一个JVM实例。 </li>
				</ol>
				<p class="normal">对JVM规范的的抽象说明是一些概念的集合，它们已经在书《The Java Virtual Machine Specification》（《Java虚拟机规范》）中被详细地描述了；对JVM的具体实现要么是软件，要么是软件和硬件的组合，它已经被许多生产厂商所实现，并存在于多种平台之上；运行Java程序的任务由JVM的运行期实例单个承担。在本文中我们所讨论的Java虚拟机(JVM)主要针对第三种情况而言。它可以被看成一个想象中的机器，在实际的计算机上通过软件模拟来实现，有自己想象中的硬件，如处理器、堆栈、寄存器等，还有自己相应的指令系统。</p>
				<p class="normal">JVM在它的生存周期中有一个明确的任务，那就是运行Java程序，因此当Java程序启动的时候，就产生JVM的一个实例；当程序运行结束的时候，该实例也跟着消失了。下面我们从JVM的体系结构和它的运行过程这两个方面来对它进行比较深入的研究。 </p>
				<p>
						<b>2  Java虚拟机的体系结构</b>
				</p>
				<p class="normal">刚才已经提到，JVM可以由不同的厂商来实现。由于厂商的不同必然导致JVM在实现上的一些不同，然而JVM还是可以实现跨平台的特性，这就要归功于设计JVM时的体系结构了。</p>
				<p class="normal">我们知道，一个JVM实例的行为不光是它自己的事，还涉及到它的子系统、存储区域、数据类型和指令这些部分，它们描述了JVM的一个抽象的内部体系结构，其目的不光规定实现JVM时它内部的体系结构，更重要的是提供了一种方式，用于严格定义实现时的外部行为。每个JVM都有两种机制，一个是装载具有合适名称的类(类或是接口)，叫做类装载子系统；另外的一个负责执行包含在已装载的类或接口中的指令，叫做运行引擎。每个JVM又包括方法区、堆、Java栈、程序计数器和本地方法栈这五个部分，这几个部分和类装载机制与运行引擎机制一起组成的体系结构图为：</p>
				<p align="center">
						<img height="299" alt="" src="http://gceclub.sun.com.cn/staticcontent/html/2004-04-09/jvm03.gif" width="589" border="0" />
				</p>
				<p align="center">图3  JVM的体系结构</p>
				<p class="normal">JVM的每个实例都有一个它自己的方法域和一个堆，运行于JVM内的所有的线程都共享这些区域；当虚拟机装载类文件的时候，它解析其中的二进制数据所包含的类信息，并把它们放到方法域中；当程序运行的时候，JVM把程序初始化的所有对象置于堆上；而每个线程创建的时候，都会拥有自己的程序计数器和Java栈，其中程序计数器中的值指向下一条即将被执行的指令，线程的Java栈则存储为该线程调用Java方法的状态；本地方法调用的状态被存储在本地方法栈，该方法栈依赖于具体的实现。</p>
				<p class="normal">下面分别对这几个部分进行说明。</p>
				<p class="normal">执行引擎处于JVM的核心位置，在Java虚拟机规范中，它的行为是由指令集所决定的。尽管对于每条指令，规范很详细地说明了当JVM执行字节码遇到指令时，它的实现应该做什么，但对于怎么做却言之甚少。Java虚拟机支持大约248个字节码。每个字节码执行一种基本的CPU运算,例如,把一个整数加到寄存器,子程序转移等。Java指令集相当于Java程序的汇编语言。</p>
				<p class="normal">Java指令集中的指令包含一个单字节的操作符,用于指定要执行的操作,还有0个或多个操作数,提供操作所需的参数或数据。许多指令没有操作数,仅由一个单字节的操作符构成。</p>
				<pre>虚拟机的内层循环的执行过程如下: 
do{ 
取一个操作符字节; 
根据操作符的值执行一个动作; 
}while(程序未结束)
</pre>
				<p class="normal">由于指令系统的简单性,使得虚拟机执行的过程十分简单,从而有利于提高执行的效率。指令中操作数的数量和大小是由操作符决定的。如果操作数比一个字节大,那么它存储的顺序是高位字节优先。例如,一个16位的参数存放时占用两个字节,其值为: </p>
				<p class="normal">第一个字节*256+第二个字节字节码。 </p>
				<p class="normal">指令流一般只是字节对齐的。指令tableswitch和lookup是例外,在这两条指令内部要求强制的4字节边界对齐。 </p>
				<p class="normal">对于本地方法接口，实现JVM并不要求一定要有它的支持，甚至可以完全没有。Sun公司实现Java本地接口(JNI)是出于可移植性的考虑，当然我们也可以设计出其它的本地接口来代替Sun公司的JNI。但是这些设计与实现是比较复杂的事情，需要确保垃圾回收器不会将那些正在被本地方法调用的对象释放掉。 </p>
				<p class="normal">Java的堆是一个运行时数据区,类的实例(对象)从中分配空间，它的管理是由垃圾回收来负责的:不给程序员显式释放对象的能力。Java不规定具体使用的垃圾回收算法,可以根据系统的需求使用各种各样的算法。 </p>
				<p class="normal">Java方法区与传统语言中的编译后代码或是Unix进程中的正文段类似。它保存方法代码(编译后的java代码)和符号表。在当前的Java实现中,方法代码不包括在垃圾回收堆中,但计划在将来的版本中实现。每个类文件包含了一个Java类或一个Java界面的编译后的代码。可以说类文件是Java语言的执行代码文件。为了保证类文件的平台无关性,Java虚拟机规范中对类文件的格式也作了详细的说明。其具体细节请参考Sun公司的Java虚拟机规范。 </p>
				<p class="normal">Java虚拟机的寄存器用于保存机器的运行状态,与微处理器中的某些专用寄存器类似。Java虚拟机的寄存器有四种: </p>
				<ol>
						<li>pc: Java程序计数器； 
</li>
						<li>optop: 指向操作数栈顶端的指针； 
</li>
						<li>frame: 指向当前执行方法的执行环境的指针；。 
</li>
						<li>vars: 指向当前执行方法的局部变量区第一个变量的指针。 </li>
				</ol>
				<p class="normal">在上述体系结构图中，我们所说的是第一种，即程序计数器，每个线程一旦被创建就拥有了自己的程序计数器。当线程执行Java方法的时候，它包含该线程正在被执行的指令的地址。但是若线程执行的是一个本地的方法，那么程序计数器的值就不会被定义。 </p>
				<p class="normal">Java虚拟机的栈有三个区域:局部变量区、运行环境区、操作数区。</p>
				<p>
						<b>局部变量区</b>
				</p>
				<p class="normal">每个Java方法使用一个固定大小的局部变量集。它们按照与vars寄存器的字偏移量来寻址。局部变量都是32位的。长整数和双精度浮点数占据了两个局部变量的空间,却按照第一个局部变量的索引来寻址。(例如,一个具有索引n的局部变量,如果是一个双精度浮点数,那么它实际占据了索引n和n+1所代表的存储空间)虚拟机规范并不要求在局部变量中的64位的值是64位对齐的。虚拟机提供了把局部变量中的值装载到操作数栈的指令,也提供了把操作数栈中的值写入局部变量的指令。</p>
				<p>
						<b>运行环境区</b>
				</p>
				<p class="normal">在运行环境中包含的信息用于动态链接,正常的方法返回以及异常捕捉。 </p>
				<p class="normal">
						<b>动态链接</b>
				</p>
				<p class="normal">运行环境包括对指向当前类和当前方法的解释器符号表的指针,用于支持方法代码的动态链接。方法的class文件代码在引用要调用的方法和要访问的变量时使用符号。动态链接把符号形式的方法调用翻译成实际方法调用,装载必要的类以解释还没有定义的符号,并把变量访问翻译成与这些变量运行时的存储结构相应的偏移地址。动态链接方法和变量使得方法中使用的其它类的变化不会影响到本程序的代码。 </p>
				<p class="normal">
						<b>正常的方法返回</b>
				</p>
				<p class="normal">如果当前方法正常地结束了,在执行了一条具有正确类型的返回指令时,调用的方法会得到一个返回值。执行环境在正常返回的情况下用于恢复调用者的寄存器,并把调用者的程序计数器增加一个恰当的数值,以跳过已执行过的方法调用指令,然后在调用者的执行环境中继续执行下去。 </p>
				<p class="normal">
						<b>异常捕捉</b>
				</p>
				<p class="normal">异常情况在Java中被称作Error(错误)或Exception(异常),是Throwable类的子类,在程序中的原因是:①动态链接错,如无法找到所需的class文件。②运行时错,如对一个空指针的引用。程序使用了throw语句。 </p>
				<p class="normal">当异常发生时,Java虚拟机采取如下措施: </p>
				<ul>
						<li>检查与当前方法相联系的catch子句表。每个catch子句包含其有效指令范围,能够处理的异常类型,以及处理异常的代码块地址。 
</li>
						<li>与异常相匹配的catch子句应该符合下面的条件:造成异常的指令在其指令范围之内,发生的异常类型是其能处理的异常类型的子类型。如果找到了匹配的catch子句,那么系统转移到指定的异常处理块处执行;如果没有找到异常处理块,重复寻找匹配的catch子句的过程,直到当前方法的所有嵌套的catch子句都被检查过。 
</li>
						<li>由于虚拟机从第一个匹配的catch子句处继续执行,所以catch子句表中的顺序是很重要的。因为Java代码是结构化的,因此总可以把某个方法的所有的异常处理器都按序排列到一个表中,对任意可能的程序计数器的值,都可以用线性的顺序找到合适的异常处理块,以处理在该程序计数器值下发生的异常情况。 
</li>
						<li>如果找不到匹配的catch子句,那么当前方法得到一个"未截获异常"的结果并返回到当前方法的调用者,好像异常刚刚在其调用者中发生一样。如果在调用者中仍然没有找到相应的异常处理块,那么这种错误将被传播下去。如果错误被传播到最顶层,那么系统将调用一个缺省的异常处理块。 </li>
				</ul>
				<p>
						<b>操作数栈区</b>
				</p>
				<p class="normal">机器指令只从操作数栈中取操作数,对它们进行操作,并把结果返回到栈中。选择栈结构的原因是:在只有少量寄存器或非通用寄存器的机器(如Intel486)上,也能够高效地模拟虚拟机的行为。操作数栈是32位的。它用于给方法传递参数,并从方法接收结果,也用于支持操作的参数,并保存操作的结果。例如,iadd指令将两个整数相加。相加的两个整数应该是操作数栈顶的两个字。这两个字是由先前的指令压进堆栈的。这两个整数将从堆栈弹出、相加,并把结果压回到操作数栈中。 </p>
				<p class="normal">每个原始数据类型都有专门的指令对它们进行必须的操作。每个操作数在栈中需要一个存储位置,除了long和double型,它们需要两个位置。操作数只能被适用于其类型的操作符所操作。例如,压入两个int类型的数,如果把它们当作是一个long类型的数则是非法的。在Sun的虚拟机实现中,这个限制由字节码验证器强制实行。但是,有少数操作(操作符dupe和swap),用于对运行时数据区进行操作时是不考虑类型的。 </p>
				<p class="normal">本地方法栈，当一个线程调用本地方法时，它就不再受到虚拟机关于结构和安全限制方面的约束，它既可以访问虚拟机的运行期数据区，也可以使用本地处理器以及任何类型的栈。例如，本地栈是一个C语言的栈，那么当C程序调用C函数时，函数的参数以某种顺序被压入栈，结果则返回给调用函数。在实现Java虚拟机时，本地方法接口使用的是C语言的模型栈，那么它的本地方法栈的调度与使用则完全与C语言的栈相同。</p>
				<p>
						<b>3  Java虚拟机的运行过程</b>
				</p>
				<p class="normal">上面对虚拟机的各个部分进行了比较详细的说明，下面通过一个具体的例子来分析它的运行过程。</p>
				<p class="normal">虚拟机通过调用某个指定类的方法main启动，传递给main一个字符串数组参数，使指定的类被装载，同时链接该类所使用的其它的类型，并且初始化它们。例如对于程序：</p>
				<pre>class HelloApp 
{
	public static void main(String[] args) 
	{
		System.out.println("Hello World!"); 
		for (int i = 0; i &lt; args.length; i++ )
		{
			System.out.println(args[i]);
		}
	}
}
</pre>
				<p class="normal">编译后在命令行模式下键入： java HelloApp run virtual machine </p>
				<p class="normal">将通过调用HelloApp的方法main来启动java虚拟机，传递给main一个包含三个字符串"run"、"virtual"、"machine"的数组。现在我们略述虚拟机在执行HelloApp时可能采取的步骤。 </p>
				<p class="normal">开始试图执行类HelloApp的main方法，发现该类并没有被装载，也就是说虚拟机当前不包含该类的二进制代表，于是虚拟机使用ClassLoader试图寻找这样的二进制代表。如果这个进程失败，则抛出一个异常。类被装载后同时在main方法被调用之前，必须对类HelloApp与其它类型进行链接然后初始化。链接包含三个阶段：检验，准备和解析。检验检查被装载的主类的符号和语义，准备则创建类或接口的静态域以及把这些域初始化为标准的默认值，解析负责检查主类对其它类或接口的符号引用，在这一步它是可选的。类的初始化是对类中声明的静态初始化函数和静态域的初始化构造方法的执行。一个类在初始化之前它的父类必须被初始化。整个过程如下：</p>
				<p align="center">
						<img height="610" alt="" src="http://gceclub.sun.com.cn/staticcontent/html/2004-04-09/jvm04.gif" width="702" border="0" />
				</p>
				<p align="center">图4：虚拟机的运行过程</p>
				<p>
						<b>4  结束语</b>
				</p>
				<p class="normal">本文通过对JVM的体系结构的深入研究以及一个Java程序执行时虚拟机的运行过程的详细分析，意在剖析清楚Java虚拟机的机理。</p>
		</div>
<img src ="http://www.blogjava.net/renyangok/aggbug/87184.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:30 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87184.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入理解abstract class和interface</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87183.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:29:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87183.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87183.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87183.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87183.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87183.html</trackback:ping><description><![CDATA[
		<p>abstract class和interface是Java语言中对于抽象类定义进行支持的两种机制，正是由于这两种机制的存在，才赋予了Java强大的面向对象能力。abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性，甚至可以相互替换，因此很多开发者在进行抽象类定义时对于abstract class和interface的选择显得比较随意。其实，两者之间还是有很大的区别的，对于它们的选择甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。本文将对它们之间的区别进行一番剖析，试图给开发者提供一个在二者之间进行选择的依据。 </p>
		<p>理解抽象类 </p>
		<p>abstract class和interface在Java语言中都是用来进行抽象类（本文中的抽象类并非从abstract class翻译而来，它表示的是一个抽象体，而abstract class为Java语言中用于定义抽象类的一种方法，请读者注意区分）定义的，那么什么是抽象类，使用抽象类能为我们带来什么好处呢？ </p>
		<p>在面向对象的概念中，我们知道所有的对象都是通过类来描绘的，但是反过来却不是这样。并不是所有的类都是用来描绘对象的，如果一个类中没有包含足够的信息来描绘一个具体的对象，这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、设计中得出的抽象概念，是对一系列看上去不同，但是本质上相同的具体概念的抽象。比如：如果我们进行一个图形编辑软件的开发，就会发现问题领域存在着圆、三角形这样一些具体概念，它们是不同的，但是它们又都属于形状这样一个概念，形状这个概念在问题领域是不存在的，它就是一个抽象概念。正是因为抽象的概念在问题领域没有对应的具体概念，所以用以表征抽象概念的抽象类是不能够实例化的。 </p>
		<p>在面向对象领域，抽象类主要用来进行类型隐藏。我们可以构造出一个固定的一组行为的抽象描述，但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类，而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体，因此它可以是不允许修改的；同时，通过从这个抽象体派生，也可扩展此模块的行为功能。熟悉OCP的读者一定知道，为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed Principle)，抽象类是其中的关键所在。 </p>
		<p>
				<br />从语法定义层面看abstract class和interface </p>
		<p>在语法层面，Java语言对于abstract class和interface给出了不同的定义方式，下面以定义一个名为Demo的抽象类为例来说明这种不同。 </p>
		<p>使用abstract class的方式定义Demo抽象类的方式如下： </p>
		<p>abstract class Demo ｛ <br />abstract void method1(); <br />abstract void method2(); <br />… <br />｝ </p>
		<p>使用interface的方式定义Demo抽象类的方式如下： </p>
		<p>interface Demo { <br />void method1(); <br />void method2(); <br />… <br />} </p>
		<p>在abstract class方式中，Demo可以有自己的数据成员，也可以有非abstarct的成员方法，而在interface方式的实现中，Demo只能够有静态的不能被修改的数据成员（也就是必须是static final的，不过在interface中一般不定义数据成员），所有的成员方法都是abstract的。从某种意义上说，interface是一种特殊形式的abstract class。 </p>
		<p>从编程的角度来看，abstract class和interface都可以用来实现"design by contract"的思想。但是在具体的使用上面还是有一些区别的。 </p>
		<p>首先，abstract class在Java语言中表示的是一种继承关系，一个类只能使用一次继承关系。但是，一个类却可以实现多个interface。也许，这是Java语言的设计者在考虑Java对于多重继承的支持方面的一种折中考虑吧。 </p>
		<p>其次，在abstract class的定义中，我们可以赋予方法的默认行为。但是在interface的定义中，方法却不能拥有默认行为，为了绕过这个限制，必须使用委托，但是这会 增加一些复杂性，有时会造成很大的麻烦。 </p>
		<p>在抽象类中不能定义默认行为还存在另一个比较严重的问题，那就是可能会造成维护上的麻烦。因为如果后来想修改类的界面（一般通过abstract class或者interface来表示）以适应新的情况（比如，添加新的方法或者给已用的方法中添加新的参数）时，就会非常的麻烦，可能要花费很多的时间（对于派生类很多的情况，尤为如此）。但是如果界面是通过abstract class来实现的，那么可能就只需要修改定义在abstract class中的默认行为就可以了。 </p>
		<p>同样，如果不能在抽象类中定义默认行为，就会导致同样的方法实现出现在该抽象类的每一个派生类中，违反了"one rule，one place"原则，造成代码重复，同样不利于以后的维护。因此，在abstract class和interface间进行选择时要非常的小心。 </p>
		<p>
				<br />从设计理念层面看abstract class和interface </p>
		<p>上面主要从语法定义和编程的角度论述了abstract class和interface的区别，这些层面的区别是比较低层次的、非本质的。本小节将从另一个层面：abstract class和interface所反映出的设计理念，来分析一下二者的区别。作者认为，从这个层面进行分析才能理解二者概念的本质所在。 </p>
		<p>前面已经提到过，abstarct class在Java语言中体现了一种继承关系，要想使得继承关系合理，父类和派生类之间必须存在"is a"关系，即父类和派生类在概念本质上应该是相同的（参考文献〔3〕中有关于"is a"关系的大篇幅深入的论述，有兴趣的读者可以参考）。对于interface 来说则不然，并不要求interface的实现者和interface定义在概念本质上是一致的，仅仅是实现了interface定义的契约而已。为了使论述便于理解，下面将通过一个简单的实例进行说明。 </p>
		<p>考虑这样一个例子，假设在我们的问题领域中有一个关于Door的抽象概念，该Door具有执行两个动作open和close，此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型，定义方式分别如下所示： </p>
		<p>使用abstract class方式定义Door： </p>
		<p>abstract class Door { <br />abstract void open(); <br />abstract void close()； <br />} </p>
		<p>
				<br />使用interface方式定义Door： </p>
		<p>
				<br />interface Door { <br />void open(); <br />void close(); <br />} </p>
		<p>
				<br />其他具体的Door类型可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract class和interface没有大的区别。 </p>
		<p>如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢（在本例中，主要是为了展示abstract class和interface反映在设计理念上的区别，其他方面无关的问题都做了简化或者忽略）？下面将罗列出可能的解决方案，并从设计理念层面对这些不同的方案进行分析。 </p>
		<p>解决方案一： </p>
		<p>简单的在Door的定义中增加一个alarm方法，如下： </p>
		<p>abstract class Door { <br />abstract void open(); <br />abstract void close()； <br />abstract void alarm(); <br />} </p>
		<p>
				<br />或者 </p>
		<p>interface Door { <br />void open(); <br />void close(); <br />void alarm(); <br />} </p>
		<p>
				<br />那么具有报警功能的AlarmDoor的定义方式如下： </p>
		<p>class AlarmDoor extends Door { <br />void open() { … } <br />void close() { … } <br />void alarm() { … } <br />} </p>
		<p>
				<br />或者 </p>
		<p>class AlarmDoor implements Door ｛ <br />void open() { … } <br />void close() { … } <br />void alarm() { … } <br />｝ </p>
		<p>这种方法违反了面向对象设计中的一个核心原则ISP（Interface Segregation Priciple），在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变（比如：修改alarm方法的参数）而改变，反之依然。 </p>
		<p>解决方案二： </p>
		<p>既然open、close和alarm属于两个不同的概念，根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有：这两个概念都使用abstract class方式定义；两个概念都使用interface方式定义；一个概念使用abstract class方式定义，另一个概念使用interface方式定义。 </p>
		<p>显然，由于Java语言不支持多重继承，所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的，但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。 </p>
		<p>如果两个概念都使用interface方式来定义，那么就反映出两个问题：1、我们可能没有理解清楚问题领域，AlarmDoor在概念本质上到底是Door还是报警器？2、如果我们对于问题领域的理解没有问题，比如：我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的，那么我们在实现时就没有能够正确的揭示我们的设计意图，因为在这两个概念的定义上（均使用interface方式定义）反映不出上述含义。 </p>
		<p>如果我们对于问题领域的理解是：AlarmDoor在概念本质上是Door，同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢？前面已经说过，abstract class在Java语言中表示一种继承关系，而继承关系在本质上是"is a"关系。所以对于Door这个概念，我们应该使用abstarct class方式来定义。另外，AlarmDoor又具有报警功能，说明它又能够完成报警概念中定义的行为，所以报警概念可以通过interface方式定义。如下所示： </p>
		<p>abstract class Door { <br />abstract void open(); <br />abstract void close()； <br />} <br />interface Alarm { <br />void alarm(); <br />} <br />class AlarmDoor extends Door implements Alarm { <br />void open() { … } <br />void close() { … } <br />void alarm() { … } <br />} </p>
		<p>
				<br />这种实现方式基本上能够明确的反映出我们对于问题领域的理解，正确的揭示我们的设计意图。其实abstract class表示的是"is a"关系，interface表示的是"like a"关系，大家在选择时可以作为一个依据，当然这是建立在对问题领域的理解上的，比如：如果我们认为AlarmDoor在概念本质上是报警器，同时又具有Door的功能，那么上述的定义方式就要反过来了。 </p>
		<p> </p>
		<p>结论 </p>
		<p>abstract class和interface是Java语言中的两种定义抽象类的方式，它们之间有很大的相似性。但是对于它们的选择却又往往反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理，因为它们表现了概念间的不同的关系（虽然都能够实现需求的功能）。这其实也是语言的一种的惯用法。</p>
<img src ="http://www.blogjava.net/renyangok/aggbug/87183.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:29 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87183.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java线程总结</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87182.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:28:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87182.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87182.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87182.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87182.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87182.html</trackback:ping><description><![CDATA[版權申明,獲得授權轉載必須保留以下申明和鏈接:<br />作者的blog:(<a href="http://blog.matrix.org.cn/page/Kaizen" target="_new"><u><font color="#0000ff">http://blog.matrix.org.cn/page/Kaizen</font></u></a>)<br /><br />在论坛上面常常看到初学者对线程的无可奈何，所以总结出了下面一篇文章，希望对一些正在学习使用java线程的初学者有所帮助。<br /><br />首先要理解线程首先需要了解一些基本的东西，我们现在所使用的大多数操作系统都属于多任务，分时操作系统。正是由于这种操作系统的出现才有了多线程这个概念。我们使用的windows,linux就属于此列。什么是分时操作系统呢，通俗一点与就是可以同一时间执行多个程序的操作系统，在自己的电脑上面，你是不是一边听歌，一边聊天还一边看网页呢？但实际上，并不上cpu在同时执行这些程序，cpu只是将时间切割为时间片，然后将时间片分配给这些程序，获得时间片的程序开始执行，不等执行完毕，下个程序又获得时间片开始执行，这样多个程序轮流执行一段时间，由于现在cpu的高速计算能力，给人的感觉就像是多个程序在同时执行一样。<br />一般可以在同一时间内执行多个程序的操作系统都有进程的概念.一个进程就是一个执行中的程序,而每一个进程都有自己独立的一块内存空间,一组系统资源.在进程概念中,每一个进程的内部数据和状态都是完全独立的.因此可以想像创建并执行一个进程的系统开像是比较大的，所以线程出现了。在java中，程序通过流控制来执行程序流,程序中单个顺序的流控制称为线程,多线程则指的是在单个程序中可以同时运行多个不同的线程,执行不同的任务.多线程意味着一个程序的多行语句可以看上去几乎在同一时间内同时运行.（你可以将前面一句话的程序换成进程，进程是程序的一次执行过程,是系统运行程序的基本单位）<br /><br />线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序的流控制;但与进程不同的是,同类的多个线程是共享一块内存空间和一组系统资源,而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序执行时使用的堆栈.所以系统在产生一个线程,或者在各个线程之间切换时,负担要比进程小的多,正因如此,线程也被称为轻负荷进程(light-weight process).一个进程中可以包含多个线程.<br /><br />多任务是指在一个系统中可以同时运行多个程序,即有多个独立运行的任务,每个任务对应一个进程，同进程一样,一个线程也有从创建,运行到消亡的过程,称为线程的生命周期.用线程的状态(state)表明线程处在生命周期的哪个阶段.线程有创建,可运行,运行中,阻塞,死亡五中状态.通过线程的控制与调度可使线程在这几种状态间转化每个程序至少自动拥有一个线程,称为主线程.当程序加载到内存时,启动主线程.<br /><br />[线程的运行机制以及调度模型] <br />java中多线程就是一个类或一个程序执行或管理多个线程执行任务的能力，每个线程可以独立于其他线程而独立运行，当然也可以和其他线程协同运行，一个类控制着它的所有线程，可以决定哪个线程得到优先级，哪个线程可以访问其他类的资源，哪个线程开始执行，哪个保持休眠状态。<br />下面是线程的机制图：<br /><img onmouseover="javascript:imgShowTip(this);" style="DISPLAY: inline" onclick="javascript:imgClick(this);" alt="" src="http://java.chinaitlab.com/UploadFiles_8734/200604/20060404144316962.jpg" onload="javascript:imgLoad(this);" border="0" resized="0" /><br /><br />线程的状态表示线程正在进行的活动以及在此时间段内所能完成的任务.线程有创建,可运行,运行中,阻塞,死亡五中状态.一个具有生命的线程,总是处于这五种状态之一：<br /><b>1.创建状态</b><br />使用new运算符创建一个线程后,该线程仅仅是一个空对象,系统没有分配资源,称该线程处于创建状态(new thread)<br /><b>2.可运行状态</b><br />使用start()方法启动一个线程后,系统为该线程分配了除CPU外的所需资源,使该线程处于可运行状态(Runnable)<br /><b>3.运行中状态</b><br />Java运行系统通过调度选中一个Runnable的线程,使其占有CPU并转为运行中状态(Running).此时,系统真正执行线程的run()方法.<br /><b>4.阻塞状态</b><br />一个正在运行的线程因某种原因不能继续运行时,进入阻塞状态(Blocked)<br /><b>5.死亡状态</b><br />线程结束后是死亡状态(Dead)<br /><br />同一时刻如果有多个线程处于可运行状态,则他们需要排队等待CPU资源.此时每个线程自动获得一个线程的优先级(priority),优先级的高低反映线程的重要或紧急程度.可运行状态的线程按优先级排队,线程调度依据优先级基础上的"先到先服务"原则.<br />线程调度管理器负责线程排队和CPU在线程间的分配,并由线程调度算法进行调度.当线程调度管理器选种某个线程时,该线程获得CPU资源而进入运行状态.<br /><br />线程调度是先占式调度,即如果在当前线程执行过程中一个更高优先级的线程进入可运行状态,则这个线程立即被调度执行.先占式调度分为:独占式和分时方式.<br />独占方式下,当前执行线程将一直执行下去,直 到执行完毕或由于某种原因主动放弃CPU,或CPU被一个更高优先级的线程抢占<br />分时方式下,当前运行线程获得一个时间片,时间到时,即使没有执行完也要让出CPU,进入可运行状态,等待下一个时间片的调度.系统选中其他可运行状态的线程执行<br />分时方式的系统使每个线程工作若干步,实现多线程同时运行<br /><br />另外请注意下面的线程调度规则（如果有不理解，不急，往下看）：<br />①如果两个或是两个以上的线程都修改一个对象，那么把执行修改的方法定义为被同步的（Synchronized）,如果对象更新影响到只读方法，那么只度方法也应该定义为同步的<br />②如果一个线程必须等待一个对象状态发生变化，那么它应该在对象内部等待，而不是在外部等待，它可以调用一个被同步的方法，并让这个方法调用wait()<br />③每当一个方法改变某个对象的状态的时候，它应该调用notifyAll()方法，这给等待队列的线程提供机会来看一看执行环境是否已发生改变<br />④记住wait(),notify(),notifyAll()方法属于Object类，而不是Thread类，仔细检查看是否每次执行wait()方法都有相应的notify()或notifyAll()方法，且它们作用与相同的对象 在java中每个类都有一个主线程，要执行一个程序，那么这个类当中一定要有main方法，这个man方法也就是java class中的主线程。你可以自己创建线程，有两种方法，一是继承Thread类，或是实现Runnable接口。一般情况下，最好避免继承，因为java中是单根继承，如果你选用继承，那么你的类就失去了弹性，当然也不能全然否定继承Thread,该方法编写简单,可以直接操作线程,适用于单重继承情况。至于选用那一种，具体情况具体分析。<br /><br /><br /><b>eg.继承Thread</b><br /><pre class="overflow">public class MyThread_1 extends Thread<br />{<br />public void run()<br />{<br />//some code <br />}<br />}</pre><br /><br /><b>eg.实现Runnable接口</b><br /><pre class="overflow">public class MyThread_2 implements Runnable<br />{<br />public void run()<br />{<br />//some code <br />}<br />}</pre><br /><br /><br />当使用继承创建线程，这样启动线程：<br /><pre class="overflow">new MyThread_1().start()</pre><br /><br />当使用实现接口创建线程，这样启动线程：<br /><pre class="overflow">new Thread(new MyThread_2()).start()</pre><br /><br />注意，其实是创建一个线程实例，并以实现了Runnable接口的类为参数传入这个实例，当执行这个线程的时候，MyThread_2中run里面的代码将被执行。<br />下面是完成的例子：<br /><br /><pre class="overflow">public class MyThread implements Runnable<br />{ <br /><br />public void run()<br />{ <br />System.out.println("My Name is "+Thread.currentThread().getName()); <br />} <br />public static void main(String[] args)<br />{<br />new Thread(new MyThread()).start(); <br />}<br />} </pre><br /><br /><br />执行后将打印出：<br /><span style="COLOR: blue">My Name is Thread-0</span><br /><br />你也可以创建多个线程，像下面这样<br /><pre class="overflow">new Thread(new MyThread()).start();<br />new Thread(new MyThread()).start();<br />new Thread(new MyThread()).start();</pre><br /><br /><br />那么会打印出：<br /><span style="COLOR: blue">My Name is Thread-0<br />My Name is Thread-1<br />My Name is Thread-2</span><br /><br />看了上面的结果，你可能会认为线程的执行顺序是依次执行的，但是那只是一般情况，千万不要用以为是线程的执行机制；影响线程执行顺序的因素有几点：首先看看前面提到的优先级别<br /><br /><br /><pre class="overflow">public class MyThread implements Runnable<br />{ <br /><br />public void run()<br />{ <br />System.out.println("My Name is "+Thread.currentThread().getName()); <br />} <br />public static void main(String[] args)<br />{<br />Thread t1=new Thread(new MyThread());<br />Thread t2=new Thread(new MyThread());<br />Thread t3=new Thread(new MyThread());<br />t2.setPriority(Thread.MAX_PRIORITY);//赋予最高优先级<br />t1.start();<br />t2.start();<br />t3.start();<br />}<br />} </pre><br /><br />再看看结果：<br /><span style="COLOR: blue">My Name is Thread-1<br />My Name is Thread-0<br />My Name is Thread-2</span><br /><br /><br />线程的优先级分为10级，分别用1到10的整数代表，默认情况是5。上面的t2.setPriority(Thread.MAX_PRIORITY)等价与t2.setPriority(10）<br />然后是线程程序本身的设计，比如使用sleep,yield,join，wait等方法（详情请看JDKDocument)<br /><br /><pre class="overflow">public class MyThread implements Runnable<br />{ <br />public void run()<br />{ <br />try<br />{<br />int sleepTime=(int)(Math.random()*100);//产生随机数字，<br />Thread.currentThread().sleep(sleepTime);//让其休眠一定时间，时间又上面sleepTime决定<br />//public static void sleep(long millis)throw InterruptedException （API）<br />System.out.println(Thread.currentThread().getName()+" 睡了 "+sleepTime);<br />}catch(InterruptedException ie)//由于线程在休眠可能被中断，所以调用sleep方法的时候需要捕捉异常<br />{<br />ie.printStackTrace();<br />} <br />} <br />public static void main(String[] args)<br />{<br />Thread t1=new Thread(new MyThread());<br />Thread t2=new Thread(new MyThread());<br />Thread t3=new Thread(new MyThread());<br />t1.start();<br />t2.start();<br />t3.start();<br />}<br />}</pre><br /><br />执行后观察其输出：<br /><br /><span style="COLOR: blue">Thread-0 睡了 11<br />Thread-2 睡了 48<br />Thread-1 睡了 69</span><br /><br /><br /><br />上面的执行结果是随机的，再执行很可能出现不同的结果。由于上面我在run中添加了休眠语句，当线程休眠的时候就会让出cpu，cpu将会选择执行处于runnable状态中的其他线程，当然也可能出现这种情况，休眠的Thread立即进入了runnable状态，cpu再次执行它。<br />[线程组概念]<br />线程是可以被组织的，java中存在线程组的概念，每个线程都是一个线程组的成员,线程组把多个线程集成为一个对象,通过线程组可以同时对其中的多个线程进行操作,如启动一个线程组的所有线程等.Java的线程组由java.lang包中的Thread——Group类实现.<br />ThreadGroup类用来管理一组线程,包括:线程的数目,线程间的关系,线程正在执行的操作,以及线程将要启动或终止时间等.线程组还可以包含线程组.在Java的应用程序中,最高层的线程组是名位main的线程组,在main中还可以加入线程或线程组,在mian的子线程组中也可以加入线程和线程组,形成线程组和线程之间的树状继承关系。像上面创建的线程都是属于main这个线程组的。<br />借用上面的例子，main里面可以这样写：<br /><br /><pre class="overflow">public static void main(String[] args)<br />{<br />/***************************************<br />ThreadGroup(String name) <br />ThreadGroup(ThreadGroup parent, String name) <br />***********************************/<br />ThreadGroup group1=new ThreadGroup("group1");<br />ThreadGroup group2=new ThreadGroup(group1,"group2");<br />Thread t1=new Thread(group2,new MyThread());<br />Thread t2=new Thread(group2,new MyThread());<br />Thread t3=new Thread(group2,new MyThread());<br />t1.start();<br />t2.start();<br />t3.start();<br />}</pre><br /><br /><br />线程组的嵌套，t1,t2,t3被加入group2,group2加入group1。<br />另外一个比较多就是关于线程同步方面的，试想这样一种情况，你有一笔存款在银行，你在一家银行为你的账户存款，而你的妻子在另一家银行从这个账户提款，现在你有1000块在你的账户里面。你存入了1000，但是由于另一方也在对这笔存款进行操作，人家开始执行的时候只看到账户里面原来的1000元，当你的妻子提款1000元后，你妻子所在的银行就认为你的账户里面没有钱了，而你所在的银行却认为你还有2000元。<br />看看下面的例子：<br /><br /><pre class="overflow">class BlankSaving //储蓄账户<br />{<br />private static int money=10000;<br />public void add(int i)<br />{<br />money=money+i;<br />System.out.println("Husband 向银行存入了 [￥"+i+"]");<br />}<br />public void get(int i)<br />{<br />money=money-i;<br />System.out.println("Wife 向银行取走了 [￥"+i+"]");<br />if(money&lt;0)<br />System.out.println("余额不足！"); <br />}<br />public int showMoney()<br />{<br />return money;<br />}<br />} <br /><br /><br />class Operater implements Runnable<br />{<br />String name;<br />BlankSaving bs;<br />public Operater(BlankSaving b,String s)<br />{<br />name=s;<br />bs=b;<br /><br /><br /><br />}<br />public static void oper(String name,BlankSaving bs)<br />{<br /><br /><br /><br />if(name.equals("husband"))<br />{<br />try<br />{<br />for(int i=0;i&lt;10;i++)<br />{<br />Thread.currentThread().sleep((int)(Math.random()*300));<br />bs.add(1000);<br />}<br />}catch(InterruptedException e){}<br />}else<br />{<br />try<br />{<br /><br /><br /><br />for(int i=0;i&lt;10;i++)<br />{<br />Thread.currentThread().sleep((int)(Math.random()*300));<br />bs.get(1000);<br />}<br />}catch(InterruptedException e){}<br />}<br />}<br />public void run()<br />{<br />oper(name,bs);<br />} <br />}<br />public class BankTest <br />{<br />public static void main(String[] args)throws InterruptedException<br />{<br />BlankSaving bs=new BlankSaving();<br />Operater o1=new Operater(bs,"husband");<br />Operater o2=new Operater(bs,"wife");<br />Thread t1=new Thread(o1);<br />Thread t2=new Thread(o2);<br />t1.start();<br />t2.start();<br />Thread.currentThread().sleep(500);<br />}<br /><br /><br /><br />}</pre><br /><br /><br />下面是其中一次的执行结果：<br /><br /><br /><br /><span style="COLOR: blue">---------first--------------<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Husband 向银行存入了 [￥1000]</span><br /><br />看到了吗，这可不是正确的需求，在husband还没有结束操作的时候，wife就插了进来，这样很可能导致意外的结果。解决办法很简单，就是将对数据进行操作方法声明为synchronized,当方法被该关键字声明后，也就意味着，如果这个数据被加锁，只有一个对象得到这个数据的锁的时候该对象才能对这个数据进行操作。也就是当你存款的时候，这笔账户在其他地方是不能进行操作的，只有你存款完毕，银行管理人员将账户解锁，其他人才能对这个账户进行操作。<br />修改public static void oper(String name,BlankSaving bs)为public static void oper(String name,BlankSaving bs)，再看看结果:<br /><br /><span style="COLOR: blue">Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]</span><br /><br /><br /><br />当丈夫完成操作后，妻子才开始执行操作，这样的话，对共享对象的操作就不会有问题了。<br />[wait and notify]<br />你可以利用这两个方法很好的控制线程的执行流程，当线程调用wait方法后，线程将被挂起，直到被另一线程唤醒（notify）或则是如果wait方法指定有时间得话，在没有被唤醒的情况下，指定时间时间过后也将自动被唤醒。但是要注意一定，被唤醒并不是指马上执行，而是从组塞状态变为可运行状态，其是否运行还要看cpu的调度。<br />事例代码：<br /><br /><pre class="overflow">class MyThread_1 extends Thread<br />{<br />Object lock;<br />public MyThread_1(Object o)<br />{<br />lock=o;<br />}<br />public void run()<br />{<br />try<br />{<br />synchronized(lock)<br />{<br />System.out.println("Enter Thread_1 and wait");<br />lock.wait();<br />System.out.println("be notified");<br />}<br />}catch(InterruptedException e){}<br />}<br />}<br />class MyThread_2 extends Thread<br />{<br />Object lock;<br />public MyThread_2(Object o)<br />{<br />lock=o;<br />}<br />public void run()<br />{<br />synchronized(lock)<br />{<br />System.out.println("Enter Thread_2 and notify");<br />lock.notify();<br />}<br />}<br />}<br />public class MyThread<br />{ <br />public static void main(String[] args)<br />{<br />int[] in=new int[0];//notice<br />MyThread_1 t1=new MyThread_1(in);<br />MyThread_2 t2=new MyThread_2(in);<br />t1.start();<br />t2.start();<br />}<br />}</pre><br /><br /><br /><br />执行结果如下：<br /><span style="COLOR: blue">Enter Thread_1 and wait<br />Enter Thread_2 and notify<br />Thread_1 be notified</span><br /><br />可能你注意到了在使用wait and notify方法得时候我使用了synchronized块来包装这两个方法，这是由于调用这两个方法的时候线程必须获得锁，也就是上面代码中的lock[]，如果你不用synchronized包装这两个方法的得话，又或则锁不一是同一把，比如在MyThread_2中synchronized(lock)改为synchronized(this),那么执行这个程序的时候将会抛出java.lang.IllegalMonitorStateException执行期异常。另外wait and notify方法是Object中的，并不在Thread这个类中。最后你可能注意到了这点：int[] in=new int[0];为什么不是创建new Object而是一个0长度的数组，那是因为在java中创建一个0长度的数组来充当锁更加高效。<br /><br />Thread作为java中一重要组成部分，当然还有很多地方需要更深刻的认识，上面只是对Thread的一些常识和易错问题做了一个简要的总结，若要真正的掌握java的线程，还需要自己多做总结 <img src ="http://www.blogjava.net/renyangok/aggbug/87182.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:28 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87182.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>计算机编码大全</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87181.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:27:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87181.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87181.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87181.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87181.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87181.html</trackback:ping><description><![CDATA[从基础的开始<br /><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     Octal  Decimal Hex<br />0          0      0       0<br />1          1      1       1<br />10         2      2       2<br />11         3      3       3<br />100        4      4       4<br />101        5      5       5<br />110        6      6       6<br />111        7      7       7<br />1000       10     8       8<br />1001       11     9       9<br />1010       12     10      A<br />1011       13     11      B<br />1100       14     12      C<br />1101       15     13      D<br />1110       16     14      E<br />1111       17     15      F 
<p>接着是上层建筑字符：</p><p>  字符是各种文字和符号的总称，包括各国家文字、标点符号、图形符号、数字等。字符集是多个字符的集合，字符集种类较多，每个字符集包含的字符个数不同，常见字符集名称：ASCII字符集、GB2312字符集、BIG5字符集、 GB 18030字符集、Unicode字符集等。计算机要准确的处理各种字符集文字，需要进行字符编码，以便计算机能够识别和存储各种文字。</p><p><font style="BACKGROUND-COLOR: #44bb5c" color="#121012">ASCII 字符集</font><br />  ASCII（American Standard Code for Information Interchange，美国信息互换标准代码）是基于罗马字母表的一套电脑编码系统，它主要用于显示现代英语和其他西欧语言。它是现今最通用的单字节编码系统，并等同于国际标准ISO 646。</p><p>包含内容：</p><p>控制字符：回车键、退格、换行键等。</p><p>可显示字符：英文大小写字符、阿拉伯数字和西文符号</p><p>ASCII扩展字符集扩展：表格符号、计算符号、希腊字母和特殊的拉丁符号。 </p><p>　　第0～32号及第127号(共34个)是控制字符或通讯专用字符，如控制符：LF（换行）、CR（回车）、FF（换页）、DEL（删除）、BEL（振铃）等；通讯专用字符：SOH（文头）、EOT（文尾）、ACK（确认）等； </p><p>　　第33～126号(共94个)是字符，其中第48～57号为0～9十个阿拉伯数字；65～90号为26个大写英文字母，97～122号为26个小写英文字母，其余为一些标点符号、运算符号等。  </p><p>　　注意：在计算机的存储单元中，一个ASCII码值占一个字节(8个二进制位)，其最高位(b7)用作奇偶校验位。所谓奇偶校验，是指在代码传送过程中用来检验是否出现错误的一种方法，一般分奇校验和偶校验两种。奇校验规定：正确的代码一个字节中1的个数必须是奇数，若非奇数，则在最高位b7添1；偶校验规定：正确的代码一个字节中1的个数必须是偶数，若非偶数，则在最高位b7添1。</p><p>DEC   HEX  CHAR  CODE  C 程序（转义）  <br />0     00 　 NUL （’\0’）  <br />1     01 　 SOH 　  <br />2     02 　 STX 　  <br />3     03 　 ETX 　  <br />4     04 　 EOT 　  <br />5     05 　 ENQ 　  <br />6     06 　 ACK 　  <br />7     07 　 BEL （’\a’）  <br />8     08 　 BS （’\b’）  <br />9     09 　 HT （’\t’）  <br />10    0A 　 LF （’\n’）  <br />11    0B 　 VT （’\v’）  <br />12    0C 　 FF （’\f’）  <br />13    0D 　 CR （’\r’）  <br />14    0E 　 SO 　  <br />15    0F 　 SI 　  <br />16    10 　 DLE 　  <br />17    11 　 DC1 　  <br />18    12 　 DC2 　  <br />19    13 　 DC1 　  <br />20    14 　 DC4 　  <br />21    15 　 NAK 　  <br />22    16 　 SYN 　  <br />23    17 　 ETB 　  <br />24    18 　 CAN 　  <br />25    19 　 EM 　  <br />26    1A 　 SUB 　  <br />27    1B 　 ESC 　  <br />28    1C 　 FS 　  <br />29    1D 　 GS 　  <br />30    1E 　 RS 　  <br />31    1F 　 US 　  <br />32    20 (space，空格) 　 　  <br />33    21    ! 　 　  <br />34    22    " 　 　  <br />35    23    # 　 　  <br />36    24    $ 　 　  <br />37    25    % 　 　  <br />38    26    &amp; 　 　  <br />39    27    ’ 　 　  <br />40    28    ( 　 　  <br />41    29    ) 　 　  <br />42    2A    * 　 　  <br />43    2B    + 　 　  <br />44    2C    , 　 　  <br />45    2D    - 　 　  <br />46    2E    . 　 　  <br />47    2F    / 　 　  <br />48    30    0 　 　  <br />49    31    1 　 　  <br />50    32    2 　 　  <br />51    33    3 　 　  <br />52    34    4 　 　  <br />53    35    5 　 　  <br />54    36    6 　 　  <br />55    37    7 　 　  <br />56    38    8 　 　  <br />57    39    9 　 　  <br />58    3A    : 　 　  <br />59    3B    ; 　 　  <br />60    3C    &lt; 　 　  <br />61    3D    = 　 　  <br />62    3E    &gt; 　 　  <br />63    3F    ? 　 　  <br />64    40    @ 　 　  <br />65    41    A 　 　  <br />66    42    B 　 　  <br />67    43    C 　 　  <br />68    44    D 　 　  <br />69    45    E 　 　  <br />70    46    F 　 　  <br />71    47    G 　 　  <br />72    48    H 　 　  <br />73    49    I 　 　  <br />74    4A    J 　 　  <br />75    4B    K 　 　  <br />76    4C    L 　 　  <br />77    4D    M 　 　  <br />78    4E    N 　 　  <br />79    4F    O 　 　  <br />80    50    P 　 　  <br />81    51    Q 　 　  <br />82    52    R 　 　  <br />83    53    S 　 　  <br />84    54    T 　 　  <br />85    55    U 　 　  <br />86    56    V 　 　  <br />87    57    W 　 　  <br />88    58    X 　 　  <br />89    59    Y 　 　  <br />90    5A    Z 　 　  <br />91    5B    [ 　 　  <br />92    5C    \ 　 （’\\’）  <br />93    5D    ] 　 　  <br />94    5E    ^ 　 　  <br />95    5F    _ 　 　  <br />96    60    ` 　 　  <br />97    61    a 　 　  <br />98    62    b 　 　  <br />99    63    c 　 　  <br />100   64    d 　 　  <br />101   65    e 　 　  <br />102   66    f 　 　  <br />103   67    g 　 　  <br />104   68    h 　 　  <br />105   69    i 　 　  <br />106   6A    j 　 　  <br />107   6B    k 　 　  <br />108   6C    l 　 　  <br />109   6D    m 　 　  <br />110   6E    n 　 　  <br />111   6F    o 　 　  <br />112   70    p 　 　  <br />113   71    q 　 　  <br />114   72    r 　 　  <br />115   73    s 　 　  <br />116   74    t 　 　  <br />117   75    u 　 　  <br />118   76    v 　 　  <br />119   77    w 　 　  <br />120   78    x 　 　  <br />121   79    y 　 　  <br />122   7A    z 　 　  <br />123   7B    { 　 　  <br />124   7C    | 　 　  <br />125   7D    } 　 　  <br />126   7E    ~ 　 　  <br />127   7F     　 DEL </p><p><br /><font style="BACKGROUND-COLOR: #48dd22" color="#25302a">GB2312 字符集</font></p><p>  GB2312又称为GB2312-80字符集，全称为《信息交换用汉字编码字符集·基本集》，由原中国国家标准总局发布，1981年5月1日实施，是中国国家标准的简体中文字符集。它所收录的汉字已经覆盖99.75%的使用频率，基本满足了汉字的计算机处理需要。在中国大陆和新加坡获广泛使用。<br /> <br />  GB2312收录简化汉字及一般符号、序号、数字、拉丁字母、日文假名、希腊字母、俄文字母、汉语拼音符号、汉语注音字母，共 7445 个图形字符。其中包括6763个汉字，其中一级汉字3755个，二级汉字3008个；包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。 </p><p>  GB2312中对所收汉字进行了“分区”处理，每区含有94个汉字/符号。这种表示方式也称为区位码。</p><p>它是用双字节表示的，两个字节中前面的字节为第一字节，后面的字节为第二字节。习惯上称第一字节为“高字节” ，而称第二字节为“低字节”。“高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0)，“低位字节”使用了0xA1-0xFE(把01-94加上0xA0)。</p><p>  以GB2312字符集的第一个汉字“啊”字为例，它的区号16，位号01，则区位码是1601，在大多数计算机程序中，高字节和低字节分别加0xA0得到程序的汉字处理编码0xB0A1。计算公式是：0xB0=0xA0+16, 0xA1=0xA0+1。</p><p><font style="BACKGROUND-COLOR: #2b4dd5" color="#ffffff">GBK字符集</font><br />  GBK字符集是GB2312的扩展(K)，GBK1.0收录了21886个符号，它分为汉字区和图形符号区，汉字区包括21003个字符。GBK字符集主要扩展了繁体中文字的支持。</p><p><br /><font style="BACKGROUND-COLOR: #dddd22" color="#ff0000">BIG5 字符集</font></p><p>  BIG5又称大五码或五大码，1984年由台湾财团法人信息工业策进会和五间软件公司宏碁 (Acer)、神通 (MiTAC)、佳佳、零壹 (Zero One)、大众 (FIC)创立，故称大五码。Big5码的产生，是因为当时台湾不同厂商各自推出不同的编码，如倚天码、IBM PS55、王安码等，彼此不能兼容；另一方面，台湾政府当时尚未推出官方的汉字编码，而中国大陆的GB2312编码亦未有收录繁体中文字。</p><p>  Big5字符集共收录13,053个中文字，该字符集在中国台湾使用。耐人寻味的是该字符集重复地收录了两个相同的字：“兀”(0xA461及0xC94A)、“嗀”(0xDCD1及0xDDFC)。</p><p>  Big5码使用了双字节储存方法，以两个字节来编码一个字。第一个字节称为“高位字节”，第二个字节称为“低位字节”。高位字节的编码范围0xA1-0xF9，低位字节的编码范围0x40-0x7E及0xA1-0xFE。</p><p>  尽管Big5码内包含一万多个字符，但是没有考虑社会上流通的人名、地名用字、方言用字、化学及生物科等用字，没有包含日文平假名及片假字母。</p><p>例如台湾视“着”为“著”的异体字，故没有收录“着”字。康熙字典中的一些部首用字(如“亠”、“疒”、“辵”、“癶”等)、常见的人名用字(如“堃”、“煊”、“栢”、“喆”等) 也没有收录到Big5之中。</p><p><br /><font style="BACKGROUND-COLOR: #5577aa" color="#ffffff">GB18030 字符集</font></p><p>GB18030的全称是GB18030-2000《信息交换用汉字编码字符集基本集的扩充》，是我国政府于2000年3月17日发布的新的汉字编码国家标准，2001年8月31日后在中国市场上发布的软件必须符合本标准。GB 18030字符集标准的出台经过广泛参与和论证，来自国内外知名信息技术行业的公司，信息产业部和原国家质量技术监督局联合实施。</p><p>GB 18030字符集标准解决汉字、日文假名、朝鲜语和中国少数民族文字组成的大字符集计算机编码问题。该标准的字符总编码空间超过150万个编码位，收录了27484个汉字，覆盖中文、日文、朝鲜语和中国少数民族文字。满足中国大陆、香港、台湾、日本和韩国等东亚地区信息交换多文种、大字量、多用途、统一编码格式的要求。并且与Unicode 3.0版本兼容，填补Unicode扩展字符字汇“统一汉字扩展A”的内容。并且与以前的国家字符编码标准（GB2312，GB13000.1）兼容。</p><p>编码方法：<br />GB 18030标准采用单字节、双字节和四字节三种方式对字符编码。单字节部分使用0×00至0×7F码(对应于ASCII码的相应码)。双字节部分，首字节码从0×81至0×FE，尾字节码位分别是0×40至0×7E和0×80至0×FE。四字节部分采用GB/T 11383未采用的0×30到0×39作为对双字节编码扩充的后缀，这样扩充的四字节编码，其范围为0×81308130到0×FE39FE39。其中第一、三个字节编码码位均为0×81至0×FE，第二、四个字节编码码位均为0×30至0×39。</p><p>按照程序员的称呼，GB2312、GBK到GB18030都属于双字节字符集 (DBCS)。</p><p>接着是国际通用的unicode字符集</p><p><font style="BACKGROUND-COLOR: #005f77" color="#ffffff">Unicode字符集（简称为UCS）</font></p><p>1．名称的由来</p><p>Unicode字符集编码是（Universal Multiple-Octet Coded Character Set） 通用多八位编码字符集的简称，支持世界上超过650种语言的国际字符集。Unicode允许在同一服务器上混合使用不同语言组的不同语言。它是由一个名为 Unicode 学术学会(Unicode Consortium)的机构制订的字符编码系统，支持现今世界各种不同语言的书面文本的交换、处理及显示。该编码于1990年开始研发，1994年正式公布，最新版本是2005年3月31日的Unicode 4.1.0。Unicode是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码，以满足跨语言、跨平台进行文本转换、处理的要求。</p><p>2．编码方法</p><p>Unicode 标准始终使用十六进制数字，而且在书写时在前面加上前缀“U+”，例如字母“A”的编码为 004116 。所以“A”的编码书写为“U+0041”。</p><p>3．UTF-8 编码<br />UTF-8是Unicode的其中一个使用方式。 UTF是 Unicode Translation Format，即把Unicode转做某种格式的意思。</p><p>UTF-8便于不同的计算机之间使用网络传输不同语言和编码的文字，使得双字节的Unicode能够在现存的处理单字节的系统上正确传输。</p><p>UTF-8使用可变长度字节来储存 Unicode字符，例如ASCII字母继续使用1字节储存，重音文字、希腊字母或西里尔字母等使用2字节来储存，而常用的汉字就要使用3字节。辅助平面字符则使用4字节。</p><p>4．UTF-16 和 UTF-32 编码<br />UTF-32、UTF-16 和 UTF-8 是 Unicode 标准的编码字符集的字符编码方案，UTF-16 使用一个或两个未分配的 16 位代码单元的序列对 Unicode 代码点进行编码；UTF-32 即将每一个 Unicode 代码点表示为相同值的 32 位整数</p><p>通过一个问题了解unicode编码<br /> <br />问题：使用Windows记事本的“另存为”，可以在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）。但这些标记是基于什么标准呢？</p><p>答案：</p><p>ANSI字符集定义：ASCII字符集，以及由此派生并兼容的字符集，如：GB2312，正式的名称为MBCS(Multi-Byte Chactacter System，多字节字符系统)，通常也称为ANSI字符集。 </p><p>UNICODE 与 UTF8、UTF16 </p><p>  由于每种语言都制定了自己的字符集，导致最后存在的各种字符集实在太多，在国际交流中要经常转换字符集非常不便。因此，产生了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 </p><p>当一个软件打开一个文本时，它要做的第一件事是决定这个文本究竟是使用哪种字符集的哪种编码保存的。软件一般采用三种方式来决定文本的字符集和编码： <br />检测文件头标识，提示用户选择，根据一定的规则猜测 <br />最标准的途径是检测文本最开头的几个字节，开头字节 Charset/encoding,如下表： <br />EF BB BF      UTF-8 <br />FE FF           UTF-16/UCS-2, little endian <br />FF FE           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.  </p><p><br />1、big endian和little endian<br />big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时，究竟是将6C写在前面，还是将49写在前面？如果将6C写在前面，就是big endian。还是将49写在前面，就是little endian。<br />“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开，由此曾发生过六次叛乱，其中一个皇帝送了命，另一个丢了王位。<br />我们一般将endian翻译成“字节序”，将big endian和little endian称作“大尾”和“小尾”。</p><p>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的字节，就可以将下两个字节作为一个双字节编码，而不用管低字节的高位是什么。</p><p>3、Unicode、UCS和UTF(UCS Transformation Format)<br />前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容（更准确地说，是与ISO-8859-1兼容），与GB码不兼容。例如“汉”字的Unicode编码是6C49，而GB码是BABA。</p><p>  UCS规定了怎么用多个字节表示各种文字。而怎样传输这些编码，是由UTF(UCS Transformation Format)规范规定的！常见的UTF规范包括UTF-8、UTF-7、UTF-16。</p><p>4、UTF的字节序和BOM<br />UTF-8以字节为编码单元，没有字节序的问题。UTF-16以两个字节为编码单元，在解释一个UTF-16文本前，首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E，“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”，那么这是“奎”还是“乙”？<br />Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的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来标记文本文件的编码方式的。</p><p><br />  写到这里对编码有了大致的了解了，就可以理解网上一些文章的话了，比如有一篇很流行的文章《URL编码与SQL注射》里面有一段是这么说的：</p><p>其实url编码就是一个字符ascii码的十六进制。不过稍微有些变动，需要在前面加上“%”。比如“\”，它的ascii码是92，92的十六进制是5c，所以“\”的url编码就是%5c。那么汉字的url编码呢？很简单，看例子：“胡”的ascii码是-17670，十六进制是BAFA，url编码是“%BA%FA”。呵呵，知道怎么转换的了吧。</p><p><br />   这得从ASCII说起，扩展的ASCII字符集采用8bit255个字符显然不够用，于是各个国家纷纷制定了自己的文字编码规范，其中中文的文字编码规范叫做“GB2312-80”（就是GB2312)，它是和ASCII兼容的一种编码规范，其实就是用扩展ASCII没有真正标准化这一点，把一个中文字符用两个扩展ASCII字符来表示。文中说的的中文ASCII码实际上就是简体中文的编码2312GB！它把ASCII又扩充了一个字节，由于高位的第一位是0，所以会出现负数的形式，url编码就是将汉字的这个GB2312编码转化成UTF-8的编码并且每8位即一个字节前面加上%符号表示。</p><p>那为何UTF-8是进行网络的规范传输编码呢？</p><p>在Unicode里，所有的字符被一视同仁。汉字不再使用“两个扩展ASCII”，而是使用“1个Unicode”，注意，现在的汉字是“一个字符”了，于是，拆字、统计字数这些问题也就自然而然的解决了。但是，这个世界不是理想的，不可能在一夜之间所有的系统都使用Unicode来处理字符，所以Unicode在诞生之日，就必须考虑一个严峻的问题：和ASCII字符集之间的不兼容问题。 </p><p>我们知道，ASCII字符是单个字节的，比如“A”的ASCII是65。而Unicode是双字节的，比如“A”的Unicode是0065，这就造成了一个非常大的问题：以前处理ASCII的那套机制不能被用来处理Unicode了 </p><p>另一个更加严重的问题是，C语言使用'\0'作为字符串结尾，而Unicode里恰恰有很多字符都有一个字节为0，这样一来，C语言的字符串函数将无法正常处理Unicode，除非把世界上所有用C写的程序以及他们所用的函数库全部换掉 </p><p>于是，比Unicode更伟大的东东诞生了，之所以说它更伟大是因为它让Unicode不再存在于纸上，而是真实的存在于我们大家的电脑中。那就是：UTF </p><p>UTF= UCS Transformation Format UCS转换格式，它是将Unicode编码规则和计算机的实际编码对应起来的一个规则。现在流行的UTF有2种：UTF-8和UTF-16 </p><p>其中UTF-16和上面提到的Unicode本身的编码规范是一致的，这里不多说了。而UTF-8不同，它定义了一种“区间规则”，这种规则可以和ASCII编码保持最大程度的兼容，这样做的好处是压缩了字符在西欧一些国家的内存消耗，减少了不必要的资源浪费，这在实际应用中是非常有必要的。 </p><p>UTF-8有点类似于Haffman编码，它将Unicode编码为：<br />00000000-0000007F的字符，用单个字节来表示； </p><p>00000080-000007FF的字符用两个字节表示  （中文的编码范围）</p><p>00000800-0000FFFF的字符用3字节表示 </p><p>因为目前为止Unicode-16规范没有指定FFFF以上的字符，所以UTF-8最多是使用3个字节来表示一个字符。但理论上来说，UTF-8最多需要用6字节表示一个字符。 </p><p>在UTF-8里，英文字符仍然跟ASCII编码一样，因此原先的函数库可以继续使用。而中文的编码范围是在0080-07FF之间，因此是2个字节表示（但这两个字节和GB编码的两个字节是不同的）。</p><p><br />看看编码之多：ANSI,AscII,GB2312,GBK,BIG5,GB18030,Unicode,UCS（就是unicode）Utf-8,utf-16,utf-32 整整10种编码～，算是够复杂了<br />可是这还仅仅是个开始，应用方面变化无穷，不过现在看到这些东西起码再不会头大了！呼呼～</p><p><br />哦，漏了一个加密的base64编码。</p><p>什么是Base64？ </p><p>按照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.） </p><p>为什么要使用Base64？</p><p>在设计这个编码的时候，我想设计人员最主要考虑了3个问题： <br />1.是否加密？ <br />2.加密算法复杂程度和效率 <br />3.如何处理传输？ </p><p>    加密是肯定的，但是加密的目的不是让用户发送非常安全的Email。这种加密方式主要就是“防君子不防小人”。即达到一眼望去完全看不出内容即可。 <br />基于这个目的加密算法的复杂程度和效率也就不能太大和太低。和上一个理由类似，MIME协议等用于发送Email的协议解决的是如何收发Email，而并不是如何安全的收发Email。因此算法的复杂程度要小，效率要高，否则因为发送Email而大量占用资源，路就有点走歪了。 </p><p>    但是，如果是基于以上两点，那么我们使用最简单的恺撒法即可，为什么Base64看起来要比恺撒法复杂呢？这是因为在Email的传送过程中，由于历史原因，Email只被允许传送ASCII字符，即一个8位字节的低7位。因此，如果您发送了一封带有非ASCII字符（即字节的最高位是1）的Email通过有“历史问题”的网关时就可能会出现问题。网关可能会把最高位置为0！很明显，问题就这样产生了！因此，为了能够正常的传送Email，这个问题就必须考虑！所以，单单靠改变字母的位置的恺撒之类的方案也就不行了。关于这一点可以参考RFC2046。 <br />基于以上的一些主要原因产生了Base64编码。</p><p>鉴于算法比较让人头大，想看的人自然会有看到的办法拉，俺是头大得很，就不放上来了。</p><img src ="http://www.blogjava.net/renyangok/aggbug/87181.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:27 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87181.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正则表达式基础知识 &amp; 其在Java中的应用</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87180.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:26:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87180.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87180.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87180.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87180.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87180.html</trackback:ping><description><![CDATA[
		<p>
				<strong>
						<font size="5">一、正则表达式基础知识：（此文讲的是符合perl的正则表达式匹配方法，与jdk1.4上的不一样，但讲的很清晰，可作为基础知识讲解看）<br /></font>
				</strong>
		</p>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">如果你曾经用过Perl或任何其他内建正则表达式支持的语言，你一定知道用正则表达式处理文本和匹配模式是多么简单。如果你不熟悉这个术语，那么“正则表达式”（Regular Expression）就是一个字符构成的串，它定义了一个用来搜索匹配字符串的模式。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">许多语言，包括Perl、PHP、Python、JavaScript和JScript，都支持用正则表达式处理文本，一些文本编辑器用正则表达式实现高级“搜索-替换”功能。那么Java又怎样呢？本文写作时，一个包含了用正则表达式进行文本处理的Java规范需求（Specification Request）已经得到认可，你可以期待在JDK的下一版本中看到它。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">然而，如果现在就需要使用正则表达式，又该怎么办呢？你可以从Apache.org下载源代码开放的Jakarta-ORO库。本文接下来的内容先简要地介绍正则表达式的入门知识，然后以Jakarta-ORO API为例介绍如何使用正则表达式。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<b>
												<font size="4">一、正则表达式基础知识</font>
										</b>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">我们先从简单的开始。假设你要搜索一个包含字符“cat”的字符串，搜索用的正则表达式就是“cat”。如果搜索对大小写不敏感，单词“catalog”、“Catherine”、“sophisticated”都可以匹配。也就是说： </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_a.jpg" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<b>1.1 句点符号</b>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">假设你在玩英文拼字游戏，想要找出三个字母的单词，而且这些单词必须以“t”字母开头，以“n”字母结束。另外，假设有一本英文字典，你可以用正则表达式搜索它的全部内容。要构造出这个正则表达式，你可以使用一个通配符——句点符号“.”。这样，完整的表达式就是“t.n”，它匹配“tan”、“ten”、“tin”和“ton”，还匹配“t#n”、“tpn”甚至“t n”，还有其他许多无意义的组合。这是因为句点符号匹配所有字符，包括空格、Tab字符甚至换行符： </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_b.jpg" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<b>1.2 方括号符号</b>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">为了解决句点符号匹配范围过于广泛这一问题，你可以在方括号（“[]”）里面指定看来有意义的字符。此时，只有方括号里面指定的字符才参与匹配。也就是说，正则表达式“t[aeio]n”只匹配“tan”、“Ten”、“tin”和“ton”。但“Toon”不匹配，因为在方括号之内你只能匹配单个字符： </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_c.jpg" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<b>1.3 “或”符号</b>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">如果除了上面匹配的所有单词之外，你还想要匹配“toon”，那么，你可以使用“|”操作符。“|”操作符的基本意义就是“或”运算。要匹配“toon”，使用“t(a|e|i|o|oo)n”正则表达式。这里不能使用方扩号，因为方括号只允许匹配单个字符；这里必须使用圆括号“()”。圆括号还可以用来分组，具体请参见后面介绍。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4_d.jpg" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<b>1.4 表示匹配次数的符号</b>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">表一显示了表示匹配次数的符号，这些符号用来确定紧靠该符号左边的符号出现的次数： </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">
												<img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4n.jpg" border="0" />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">假设我们要在文本文件中搜索美国的社会安全号码。这个号码的格式是999-99-9999。用来匹配它的正则表达式如图一所示。在正则表达式中，连字符（“-”）有着特殊的意义，它表示一个范围，比如从0到9。因此，匹配社会安全号码中的连字符号时，它的前面要加上一个转义字符“\”。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">
												<img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4a.gif" border="0" />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">图一：匹配所有123-12-1234形式的社会安全号码</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">假设进行搜索的时候，你希望连字符号可以出现，也可以不出现——即，999-99-9999和999999999都属于正确的格式。这时，你可以在连字符号后面加上“？”数量限定符号，如图二所示： </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">
												<img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4b.gif" border="0" />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">图二：匹配所有123-12-1234和123121234形式的社会安全号码</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">下面我们再来看另外一个例子。美国汽车牌照的一种格式是四个数字加上二个字母。它的正则表达式前面是数字部分“[0-9]{4}”，再加上字母部分“[A-Z]{2}”。图三显示了完整的正则表达式。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">
												<img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4c.gif" border="0" />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">图三：匹配典型的美国汽车牌照号码，如8836KV</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">1.5 “否”符号 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">“^”符号称为“否”符号。如果用在方括号内，“^”表示不想要匹配的字符。例如，图四的正则表达式匹配所有单词，但以“X”字母开头的单词除外。 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">
												<img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4d.gif" border="0" />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">图四：匹配所有单词，但“X”开头的除外</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">1.6 圆括号和空白符号 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">假设要从格式为“June 26, 1951”的生日日期中提取出月份部分，用来匹配该日期的正则表达式可以如图五所示： </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">
												<img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4e.gif" border="0" />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">图五：匹配所有Moth DD,YYYY格式的日期</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">新出现的“\s”符号是空白符号，匹配所有的空白字符，包括Tab字符。如果字符串正确匹配，接下来如何提取出月份部分呢？只需在月份周围加上一个圆括号创建一个组，然后用ORO API（本文后面详细讨论）提取出它的值。修改后的正则表达式如图六所示： </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">
												<img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4f.gif" border="0" />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">图六：匹配所有Month DD,YYYY格式的日期，定义月份值为第一个组</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<b>1.7 其它符号</b>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">为简便起见，你可以使用一些为常见正则表达式创建的快捷符号。如表二所示： </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">表二：常用符号 </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">
												<img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4o.jpg" border="0" />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">例如，在前面社会安全号码的例子中，所有出现“[0-9]”的地方我们都可以使用“\d”。修改后的正则表达式如图七所示： </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="center">
												<img src="http://www.ccw.com.cn/htm/app/aprog/01_7_31_4g.gif" border="0" />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<p align="left">图七：匹配所有123-12-1234格式的社会安全号码<br /><!--StartFragment --></p>
								</td>
						</tr>
						<tr valign="baseline">
								<th align="left">
								</th>
						</tr>
				</tbody>
		</table>
		<font size="5">
				<strong>－－－－－－－－－－－－－－－－－－－－－－－－－<br />二、正则表达式在java中</strong>应用</font> （java编程思想第三版P565页有讲解）<a name="1"><span class="atitle"><font face="Arial"><br /><font size="4">简介：</font></font></span></a><p>java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包。</p><p>它包括两个类： <b>Pattern</b>和 <b>Matcher</b></p><table cellspacing="0" cellpadding="5" border="1"><tbody><tr><td><b>Pattern</b></td><td>一个Pattern是一个正则表达式经编译后的表现模式。</td></tr><tr><td><b>Matcher</b></td><td>一个Matcher对象是一个状态机器，它依据Pattern对象做为匹配模式对字符串展开匹配检查。</td></tr></tbody></table><p>首先一个Pattern实例订制了一个所用语法与PERL的类似的正则表达式经编译后的模式，然后一个Matcher实例在这个给定的Pattern实例的模式控制下进行字符串的匹配工作。</p><p>以下我们就分别来看看这两个类：</p><br /><br /><p><a name="2"><span class="atitle"><font face="Arial" size="4">Pattern类: </font></span></a></p><p>Pattern的方法如下：</p><table cellspacing="0" cellpadding="5" width="100%" border="1"><tbody><tr><td align="right">static Pattern</td><td><b>compile</b>(String regex) <br />将给定的正则表达式编译并赋予给Pattern类 </td></tr><tr><td align="right">static Pattern</td><td><b>compile</b>(String regex, int flags) <br />同上，但增加flag参数的指定，可选的flag参数包括：CASE INSENSITIVE,MULTILINE,DOTALL,UNICODE CASE， CANON EQ </td></tr><tr><td align="right">int</td><td><b>flags</b>() <br />返回当前Pattern的匹配flag参数. </td></tr><tr><td align="right">Matcher</td><td><b>matcher</b>(CharSequence input) <br />生成一个给定命名的Matcher对象 </td></tr><tr><td align="right">static boolean</td><td><b>matches</b>(String regex, CharSequence input) <br />编译给定的正则表达式并且对输入的字串以该正则表达式为模开展匹配,该方法适合于该正则表达式只会使用一次的情况，也就是只进行一次匹配工作，因为这种情况下并不需要生成一个Matcher实例。 </td></tr><tr><td align="right">String</td><td><b>pattern</b>() <br />返回该Patter对象所编译的正则表达式。 </td></tr><tr><td align="right">String[]</td><td><b>split</b>(CharSequence input) <br />将目标字符串按照Pattern里所包含的正则表达式为模进行分割。 </td></tr><tr><td align="right">String[]</td><td><b>split</b>(CharSequence input, int limit) <br />作用同上，增加参数limit目的在于要指定分割的段数，如将limi设为2，那么目标字符串将根据正则表达式分为割为两段。 </td></tr></tbody></table><p>一个正则表达式，也就是一串有特定意义的字符，必须首先要编译成为一个Pattern类的实例，这个Pattern对象将会使用 <b>matcher()</b>方法来生成一个Matcher实例，接着便可以使用该 Matcher实例以编译的正则表达式为基础对目标字符串进行匹配工作，多个Matcher是可以共用一个Pattern对象的。 </p><p>现在我们先来看一个简单的例子，再通过分析它来了解怎样生成一个Pattern对象并且编译一个正则表达式，最后根据这个正则表达式将目标字符串进行分割：</p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">import java.util.regex.*;<br />
public class Replacement{
      public static void main(String[] args) throws Exception {
        // 生成一个Pattern,同时编译一个正则表达式
        Pattern p = Pattern.compile("[/]+");
        //用Pattern的split()方法把字符串按"/"分割
        String[] result = p.split(
"Kevin has seen《LEON》seveal times,because it is a good film."
+"/ 凯文已经看过《这个杀手不太冷》几次了，因为它是一部"
+"好电影。/名词:凯文。");
        for (int i=0; i&lt;result.length; i++)
            System.out.println(result[i]);
      }
}
</font></code></pre></td></tr></tbody></table><br /><p>输出结果为：</p><pre>Kevin has seen《LEON》seveal times,because it is a good film.
凯文已经看过《这个杀手不太冷》几次了，因为它是一部好电影。
名词:凯文。
</pre><p>很明显，该程序将字符串按"/"进行了分段，我们以下再使用 <b>split</b>(CharSequence input, int limit)方法来指定分段的段数，程序改动为： <br /><code>tring[] result = p.split("Kevin has seen《LEON》seveal times,because it is a good film./ 凯文已经看过《这个杀手不太冷》几次了，因为它是一部好电影。/名词:凯文。"，2);</code></p><p>这里面的参数"2"表明将目标语句分为两段。</p><p>输出结果则为：</p><pre>Kevin has seen《LEON》seveal times,because it is a good film.
凯文已经看过《这个杀手不太冷》几次了，因为它是一部好电影。/名词:凯文。</pre><br /><p>由上面的例子，我们可以比较出java.util.regex包在构造Pattern对象以及编译指定的正则表达式的实现手法与我们在上一篇中所介绍的Jakarta-ORO 包在完成同样工作时的差别，Jakarta-ORO 包要先构造一个PatternCompiler类对象接着生成一个Pattern对象，再将正则表达式用该PatternCompiler类的compile()方法来将所需的正则表达式编译赋予Pattern类：</p><p>PatternCompiler orocom=new Perl5Compiler();</p><p>Pattern pattern=orocom.compile("REGULAR EXPRESSIONS");</p><p>PatternMatcher matcher=new Perl5Matcher();</p><p>但是在java.util.regex包里，我们仅需生成一个Pattern类，直接使用它的compile()方法就可以达到同样的效果: <code>Pattern p = Pattern.compile("[/]+");</code></p><p>因此似乎java.util.regex的构造法比Jakarta-ORO更为简洁并容易理解。</p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-regp/part2/index.html#main"><b><font color="#996699">回页首</font><font color="#000080"></font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="3"><span class="atitle"><font face="Arial" size="4">Matcher类:</font></span></a></p><p>Matcher方法如下：</p><table cellspacing="0" cellpadding="5" width="100%" border="1"><tbody><tr><td align="right">Matcher</td><td><b>appendReplacement</b>(StringBuffer sb, String replacement) <br />将当前匹配子串替换为指定字符串，并且将替换后的子串以及其之前到上次匹配子串之后的字符串段添加到一个StringBuffer对象里。 </td></tr><tr><td align="right">StringBuffer</td><td><b>appendTail</b>(StringBuffer sb) <br />将最后一次匹配工作后剩余的字符串添加到一个StringBuffer对象里。 </td></tr><tr><td align="right">int</td><td><b>end</b>() <br />返回当前匹配的子串的最后一个字符在原目标字符串中的索引位置 。 </td></tr><tr><td align="right">int</td><td><b>end</b>(int group) <br />返回与匹配模式里指定的组相匹配的子串最后一个字符的位置。 </td></tr><tr><td align="right">boolean</td><td><b>find</b>() <br />尝试在目标字符串里查找下一个匹配子串。 </td></tr><tr><td align="right">boolean</td><td><b>find</b>(int start) <br />重设Matcher对象，并且尝试在目标字符串里从指定的位置开始查找下一个匹配的子串。 </td></tr><tr><td align="right">String</td><td><b>group</b>() <br />返回当前查找而获得的与组匹配的所有子串内容 </td></tr><tr><td align="right">String</td><td><b>group</b>(int group) <br />返回当前查找而获得的与指定的组匹配的子串内容 </td></tr><tr><td align="right">int</td><td><b>groupCount</b>() <br />返回当前查找所获得的匹配组的数量。 </td></tr><tr><td align="right">boolean</td><td><b>lookingAt</b>() <br />检测目标字符串是否以匹配的子串起始。 </td></tr><tr><td align="right">boolean</td><td><b>matches</b>() <br />尝试对整个目标字符展开匹配检测，也就是只有整个目标字符串完全匹配时才返回真值。 </td></tr><tr><td align="right">Pattern</td><td><b>pattern</b>() <br />返回该Matcher对象的现有匹配模式，也就是对应的Pattern 对象。 </td></tr><tr><td align="right">String</td><td><b>replaceAll</b>(String replacement) <br />将目标字符串里与既有模式相匹配的子串全部替换为指定的字符串。 </td></tr><tr><td align="right">String</td><td><b>replaceFirst</b>(String replacement) <br />将目标字符串里第一个与既有模式相匹配的子串替换为指定的字符串。 </td></tr><tr><td align="right">Matcher</td><td><b>reset</b>() <br />重设该Matcher对象。 </td></tr><tr><td align="right">Matcher</td><td><b>reset</b>(CharSequence input) <br />重设该Matcher对象并且指定一个新的目标字符串。 </td></tr><tr><td align="right">int</td><td><b>start</b>() <br />返回当前查找所获子串的开始字符在原目标字符串中的位置。 </td></tr><tr><td align="right">int</td><td><b>start</b>(int group) <br />返回当前查找所获得的和指定组匹配的子串的第一个字符在原目标字符串中的位置。 </td></tr></tbody></table><p>（光看方法的解释是不是很不好理解？不要急，待会结合例子就比较容易明白了）</p><p>一个Matcher实例是被用来对目标字符串进行基于既有模式（也就是一个给定的Pattern所编译的正则表达式）进行匹配查找的，所有往Matcher的输入都是通过CharSequence接口提供的，这样做的目的在于可以支持对从多元化的数据源所提供的数据进行匹配工作。</p><p>我们分别来看看各方法的使用：</p><p>★matches()/lookingAt ()/find()： <br />一个Matcher对象是由一个Pattern对象调用其matcher()方法而生成的，一旦该Matcher对象生成,它就可以进行三种不同的匹配查找操作： </p><ol><li>matches()方法尝试对整个目标字符展开匹配检测，也就是只有整个目标字符串完全匹配时才返回真值。 
</li><li>lookingAt ()方法将检测目标字符串是否以匹配的子串起始。 
</li><li>find()方法尝试在目标字符串里查找下一个匹配子串。 </li></ol><p>以上三个方法都将返回一个布尔值来表明成功与否。</p><p>★replaceAll ()/appendReplacement()/appendTail()： <br />Matcher类同时提供了四个将匹配子串替换成指定字符串的方法： </p><ol><li>replaceAll() 
</li><li>replaceFirst() 
</li><li>appendReplacement() 
</li><li>appendTail() </li></ol><p>replaceAll()与replaceFirst()的用法都比较简单，请看上面方法的解释。我们主要重点了解一下appendReplacement()和appendTail()方法。</p><p>appendReplacement(StringBuffer sb, String replacement) 将当前匹配子串替换为指定字符串，并且将替换后的子串以及其之前到上次匹配子串之后的字符串段添加到一个StringBuffer对象里，而appendTail(StringBuffer sb) 方法则将最后一次匹配工作后剩余的字符串添加到一个StringBuffer对象里。</p><p>例如，有字符串fatcatfatcatfat,假设既有正则表达式模式为"cat"，第一次匹配后调用appendReplacement(sb,"dog"),那么这时StringBuffer sb的内容为fatdog，也就是fatcat中的cat被替换为dog并且与匹配子串前的内容加到sb里，而第二次匹配后调用appendReplacement(sb,"dog")，那么sb的内容就变为fatdogfatdog，如果最后再调用一次appendTail（sb）,那么sb最终的内容将是fatdogfatdogfat。</p><p>还是有点模糊？那么我们来看个简单的程序：</p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">//该例将把句子里的"Kelvin"改为"Kevin"
import java.util.regex.*;
public class MatcherTest{
    public static void main(String[] args) 
                         throws Exception {
        //生成Pattern对象并且编译一个简单的正则表达式"Kelvin"
        Pattern p = Pattern.compile("Kevin");
        //用Pattern类的matcher()方法生成一个Matcher对象
        Matcher m = p.matcher("Kelvin Li and Kelvin Chan are both working <br />in Kelvin Chen's KelvinSoftShop company");
        StringBuffer sb = new StringBuffer();
        int i=0;
        //使用find()方法查找第一个匹配的对象
        boolean result = m.find();
        //使用循环将句子里所有的kelvin找出并替换再将内容加到sb里
        while(result) {
            i++;
            m.appendReplacement(sb, "Kevin");
            System.out.println("第"+i+"次匹配后sb的内容是："+sb);
            //继续查找下一个匹配对象
            result = m.find();
        }
        //最后调用appendTail()方法将最后一次匹配后的剩余字符串加到sb里；
        m.appendTail(sb);
        System.out.println("调用m.appendTail(sb)后sb的最终内容是:"+ sb.toString());
    }
}</font></code></pre></td></tr></tbody></table><br /><p>最终输出结果为： <br />第1次匹配后sb的内容是：Kevin <br />第2次匹配后sb的内容是：Kevin Li and Kevin <br />第3次匹配后sb的内容是：Kevin Li and Kevin Chan are both working in Kevin <br />第4次匹配后sb的内容是：Kevin Li and Kevin Chan are both working in Kevin Chen's Kevin <br />调用m.appendTail(sb)后sb的最终内容是：Kevin Li and Kevin Chan are both working in Kevin Chen's KevinSoftShop company. </p><p>看了上面这个例程是否对appendReplacement()，appendTail()两个方法的使用更清楚呢，如果还是不太肯定最好自己动手写几行代码测试一下。</p><p>★group()/group(int group)/groupCount()： <br />该系列方法与我们在上篇介绍的Jakarta-ORO中的MatchResult .group()方法类似(有关Jakarta-ORO请参考上篇的内容)，都是要返回与组匹配的子串内容，下面代码将很好解释其用法： </p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">import java.util.regex.*;

public class GroupTest{
    public static void main(String[] args) 
                         throws Exception {
        Pattern p = Pattern.compile("(ca)(t)");        
        Matcher m = p.matcher("one cat,two cats in the yard");
        StringBuffer sb = new StringBuffer();
        boolean result = m.find();
        System.out.println("该次查找获得匹配组的数量为："+m.groupCount());
        for(int i=1;i&lt;=m.groupCount();i++){
         System.out.println("第"+i+"组的子串内容为： "+m.group(i));
        }
    }
}</font></code></pre></td></tr></tbody></table><br /><p>输出为： <br />该次查找获得匹配组的数量为：2 <br />第1组的子串内容为：ca <br />第2组的子串内容为：t </p><p>Matcher对象的其他方法因比较好理解且由于篇幅有限，请读者自己编程验证。</p><br /><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br /><img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td></tr></tbody></table><table class="no-print" cellspacing="0" cellpadding="0" align="right"><tbody><tr align="right"><td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td valign="center"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br /></td><td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/l-regp/part2/index.html#main"><b><font color="#996699">回页首</font><font color="#000080"></font></b></a></td></tr></tbody></table></td></tr></tbody></table><br /><br /><p><a name="4"><span class="atitle"><font face="Arial" size="4">一个检验Email地址的小程序：</font></span></a></p><p>最后我们来看一个检验Email地址的例程，该程序是用来检验一个输入的EMAIL地址里所包含的字符是否合法，虽然这不是一个完整的EMAIL地址检验程序，它不能检验所有可能出现的情况，但在必要时您可以在其基础上增加所需功能。</p><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre><code class="section"><font face="Lucida Console">import java.util.regex.*;
public class Email {
   public static void main(String[] args) throws Exception {
      String input = args[0];
      //检测输入的EMAIL地址是否以 非法符号"."或"@"作为起始字符      
      Pattern p = Pattern.compile("^\\.|^\\@");
      Matcher m = p.matcher(input);
      if (m.find()){
        System.err.println("EMAIL地址不能以'.'或'@'作为起始字符");
      }
      //检测是否以"www."为起始
      p = Pattern.compile("^www\\.");
      m = p.matcher(input);
      if (m.find()) {
        System.out.println("EMAIL地址不能以'www.'起始");
      }
      //检测是否包含非法字符
      p = Pattern.compile("[^A-Za-z0-9\\.\\@_\\-~#]+");
      m = p.matcher(input);
      StringBuffer sb = new StringBuffer();
      boolean result = m.find();
      boolean deletedIllegalChars = false;
      while(result) {
         //如果找到了非法字符那么就设下标记
         deletedIllegalChars = true;
         //如果里面包含非法字符如冒号双引号等，那么就把他们消去，加到SB里面
         m.appendReplacement(sb, "");
         result = m.find();
      }
      m.appendTail(sb);
      input = sb.toString();
      if (deletedIllegalChars) {
          System.out.println("输入的EMAIL地址里包含有冒号、逗号等非法字符，请修改");
          System.out.println("您现在的输入为: "+args[0]);
          System.out.println("修改后合法的地址应类似: "+input);
     }
   }
}</font></code></pre></td></tr></tbody></table><br /><p>例如，我们在命令行输入：java Email www.kevin@163.net</p><p>那么输出结果将会是：EMAIL地址不能以'www.'起始</p><p>如果输入的EMAIL为@kevin@163.net</p><p>则输出为：EMAIL地址不能以'.'或'@'作为起始字符</p><p>当输入为：cgjmail#$%@163.net</p><p>那么输出就是：</p><pre>输入的EMAIL地址里包含有冒号、逗号等非法字符，请修改
您现在的输入为: cgjmail#$%@163.net
修改后合法的地址应类似: cgjmail@163.net</pre><img src ="http://www.blogjava.net/renyangok/aggbug/87180.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:26 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87180.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>全世界所有程序员都会犯的错误-蔡学镛</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87179.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:25:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87179.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87179.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87179.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87179.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87179.html</trackback:ping><description><![CDATA[
		<div>当年，国际巨星成龙的「龙种」曝光，众人指责他对不起娇妻林凤娇，逼得他出面召开记者会，向世人自白他犯了「全世界所有男人都会犯的错误」。从来没犯过这种错误的我，也因此常常认为自己不是个男人。</div>
		<div>虽然没犯过「全世界所有男人都会犯的错误」，但是我倒是曾经犯了「全世界所有程序员都会犯的错误」。不管使用何种语言，全世界所有程序员都一定犯过这种错误，那就是：太依赖编译器，却不知道编译器做了哪些事。</div>
		<div>一般来说，越高阶的程序语言，会提供越多语法上的便利，以方便程序撰写，这就俗称为syntactic sugar，我称其为「语法上的甜头」。虽说是甜头，但是如果你未能了解该语法的实质内涵，很可能会未尝甜头，却吃尽苦头。</div>
		<div>不久前，我收到一个电子邮件，读者列出下面的Java程序，向我求救。看过这个程序之后，我确定这又是一个「全世界所有程序员都会犯的错误」。</div>
		<div>// 程序1<br />class Singleton {<br />private static Singleton obj = new Singleton();<br />public static int counter1;<br />public static int counter2 = 0;<br />private Singleton() {<br />counter1++;<br />counter2++;<br />}<br />public static Singleton getInstance() {<br />return obj;<br />}<br />}</div>
		<div>// 程序2<br />public class MyMain {<br />public static void main(String[] args) {<br />Singleton obj = Singleton.getInstance();<br />System.out.println("obj.counter1=="+obj.counter1);<br />System.out.println("obj.counter2=="+obj.counter2);<br />}<br />}</div>
		<div>执行结果是：<br />obj.counter1==1<br />obj.counter2==0</div>
		<div>你有没有被此结果吓一跳？乍看程序代码，你很可能会认为counter1和counter2的值一定会相等，但执行结果显然不是如此。其实，程序1被编译后的程序应该等同于下面的程序3：</div>
		<div>// 程序3<br />class Singleton {<br />private static Singleton obj;<br />public static int counter1;<br />public static int counter2;<br />static { // 这就是class constructor<br />// 在进入此class constructor之前，class已经被JVM<br />// 配置好内存，所有的static field都会被先设定为0，<br />// 所以此时counter1和counter2都已经是0，且singleton为null<br />obj = new Singleton(); // 问题皆由此行程序产生<br />// counter1不会在此被设定为0<br />counter2 = 0; // counter2再被设定一次0（其实是多此一举）<br />}<br />private Singleton() { // 这是instance constructor<br />counter1++;<br />counter2++;<br />}<br />public static Singleton getInstance() {<br />return obj;<br />}<br />}</div>
		<div>这是因为：当class具有static field，且直接在宣告处透过「=...」的方式设定其值时，编译器会自动将这些叙述依序搬到class constructor内。同样地，当class具有instance field，且直接在宣告处透过「=...」的方式设定其值时，编译器会自动将这些叙述依序搬到instance constructor内。</div>
		<div>此程序在class constructor内，还未将static field初始化时（这时候，counter1和counter2都是0），就呼叫instance constructor，而instance constructor竟然还会去更动static field的值，使得counter1和counter2都变成1。然后instance constructor执行完，回到class constructor，再把counter2的值设为0（但是<br />counter1维持不变）。最后的结果：counter1等于1，counter2等于0。</div>
		<div>欲改正程序1，方法有三：</div>
		<div>-方法一：将singleton field的宣告调到counter1与counter2 field之后。<br />这是最好的作法。<br />-方法二：将counter2=0的宣告中，「=0」的部分删除。这种作法只有在希望<br />-方法三：将初始化的动作搬到class constructors内，自行撰写，而不依赖<br />编译器产生。这是最保险的作法。</div>
		<div>如何避免犯下「全世界所有程序员都会犯的错误」，我给各位Java程序员<br />的建议是：<br />-熟读Java Language Specification<br />-在有疑问时，使用J2SDK所提供的javap来反组译Java Bytecode，直接观察<br />编译后的结果。</div>
		<div>下面是我用javap来反组译程序1的示范：</div>
		<div>C:\&gt;javap -c -classpath . Singleton</div>
		<div>Compiled from MyMain.java<br />class Singleton extends java.lang.Object {<br />public static int counter1;<br />public static int counter2;<br />public static Singleton getInstance();<br />static {};<br />}</div>
		<div>Method Singleton()<br />0 aload_0<br />1 invokespecial #1 &lt;Method java.lang.Object()&gt;<br />4 getstatic #2 &lt;Field int counter1&gt;<br />7 iconst_1<br />8 iadd<br />9 putstatic #2 &lt;Field int counter1&gt;<br />12 getstatic #3 &lt;Field int counter2&gt;<br />15 iconst_1<br />16 iadd<br />17 putstatic #3 &lt;Field int counter2&gt;<br />20 return</div>
		<div>Method Singleton getInstance()<br />0 getstatic #4 &lt;Field Singleton obj&gt;<br />3 areturn</div>
		<div>Method static {}<br />0 new #5 &lt;Class Singleton&gt;<br />3 dup<br />4 invokespecial #6 &lt;Method Singleton()&gt;<br />7 putstatic #4 &lt;Field Singleton obj&gt;<br />10 iconst_0<br />11 putstatic #3 &lt;Field int counter2&gt;<br />14 return</div>
		<div>其实Java的syntactic sugar并不算多，C#的syntactic sugar才真的是无所不在，<br />也因此C#的初学者更容易犯了「全世界所有程序员都会犯的错误」。许多C#的书都会一边介绍C#语法，一边介绍编译之后MSIL（.NET的中间语言，类似Java的Bytecode）的结果，然而Java的书却鲜少这么做。</div>
		<div>虽说是「全世界所有程序员都会犯的错误」，但是这不代表你犯了此错误之后，仍可以同爱借钱的曹启泰一般地「抬头挺胸、理直气壮」。只要有心，其实这一类的错误仍是可以避免的。<br /></div>
<img src ="http://www.blogjava.net/renyangok/aggbug/87179.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:25 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87179.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>问题研究——字符集编码</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87178.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:23:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87178.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87178.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87178.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87178.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87178.html</trackback:ping><description><![CDATA[
		<h1 style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal; TEXT-ALIGN: center" align="center">问题研究</h1>
		<p class="MsoBodyTextIndent" style="MARGIN-BOTTOM: 24pt; TEXT-INDENT: 21pt; TEXT-ALIGN: center" align="center">--字符集编码</p>
		<p class="1">1. 概述</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 22pt">本文主要包括以下几个方面：编码基本知识，java，系统软件，url，工具软件等。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 22pt">在下面的描述中，将以"中文"两个字为例，经查表可以知道其GB2312编码是"<u>d6d0 cec4</u>"，Unicode编码为"<u>4e2d 6587</u>"，UTF编码就是"<u>e4b8ad e69687</u>"。注意，这两个字没有iso8859-1编码，但可以用iso8859-1编码来"表示"。</p>
		<p class="1">2. 编码基本知识</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">最早的编码是iso8859-1，和ascii编码相似。但为了方便表示各种各样的语言，逐渐出现了很多标准编码，重要的有如下几个。</p>
		<p class="2">2.1. iso8859-1</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">属于单字节编码，最多能表示的字符范围是0-255，应用于英文系列。比如，字母'a'的编码为0x61=97。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">很明显，iso8859-1编码表示的字符范围很窄，无法表示中文字符。但是，由于是单字节编码，和计算机最基础的表示单位一致，所以很多时候，仍旧使用iso8859-1编码来表示。而且在很多协议上，默认使用该编码。比如，虽然"中文"两个字不存在iso8859-1编码，以gb2312编码为例，应该是"<u>d6d0 cec4</u>"两个字符，使用iso8859-1编码的时候则将它拆开为4个字节来表示："<u>d6 d0 ce c4</u>"（事实上，在进行存储的时候，也是以字节为单位处理的）。而如果是UTF编码，则是6个字节"<u>e4 b8 ad e6 96 87</u>"。很明显，这种表示方法还需要以另一种编码为基础。</p>
		<p class="2">2.2. GB2312/GBK</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">这就是汉子的国标码，专门用来表示汉字，是双字节编码，而英文字母和iso8859-1一致（兼容iso8859-1编码）。其中gbk编码能够用来同时表示繁体字和简体字，而gb2312只能表示简体字，gbk是兼容gb2312编码的。</p>
		<p class="2">2.3. unicode</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">这是最统一的编码，可以用来表示所有语言的字符，而且是定长双字节（也有四字节的）编码，包括英文字母在内。所以可以说它是不兼容iso8859-1编码的，也不兼容任何编码。不过，相对于iso8859-1编码来说，uniocode编码只是在前面增加了一个0字节，比如字母'a'为"<u>00 61</u>"。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 22pt">需要说明的是，定长编码便于计算机处理（注意GB2312/GBK不是定长编码），而unicode又可以用来表示所有字符，所以在很多软件内部是使用unicode编码来处理的，比如java。</p>
		<p class="2">2.4. UTF</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">考虑到unicode编码不兼容iso8859-1编码，而且容易占用更多的空间：因为对于英文字母，unicode也需要两个字节来表示。所以unicode不便于传输和存储。因此而产生了utf编码，utf编码兼容iso8859-1编码，同时也可以用来表示所有语言的字符，不过，utf编码是不定长编码，每一个字符的长度从1-6个字节不等。另外，utf编码自带简单的校验功能。一般来讲，英文字母都是用一个字节表示，而汉字使用三个字节。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">注意，虽然说utf是为了使用更少的空间而使用的，但那只是相对于unicode编码来说，如果已经知道是汉字，则使用GB2312/GBK无疑是最节省的。不过另一方面，值得说明的是，虽然utf编码对汉字使用3个字节，但即使对于汉字网页，utf编码也会比unicode编码节省，因为网页中包含了很多的英文字符。</p>
		<p class="1">3. java对字符的处理</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">在java应用软件中，会有多处涉及到字符集编码，有些地方需要进行正确的设置，有些地方需要进行一定程度的处理。</p>
		<p class="2">3.1. getBytes(charset)</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">这是java字符串处理的一个标准函数，其作用是将字符串所表示的字符按照charset编码，并以字节方式表示。注意字符串在java内存中总是按unicode编码存储的。比如"中文"，正常情况下（即没有错误的时候）存储为"<u>4e2d 6587</u>"，如果charset为"gbk"，则被编码为"<u>d6d0 cec4</u>"，然后返回字节"<u>d6 d0 ce c4</u>"。如果charset为"utf8"则最后是"<u>e4 b8 ad e6 96 87</u>"。如果是"iso8859-1"，则由于无法编码，最后返回 "<u>3f 3f</u>"（两个问号）。</p>
		<p class="2">3.2. new String(charset)</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">这是java字符串处理的另一个标准函数，和上一个函数的作用相反，将字节数组按照charset编码进行组合识别，最后转换为unicode存储。参考上述getBytes的例子，"gbk" 和"utf8"都可以得出正确的结果"<u>4e2d 6587</u>"，但iso8859-1最后变成了"<u>003f 003f</u>"（两个问号）。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 22pt">因为utf8可以用来表示/编码所有字符，所以new String( str.getBytes( "utf8" ), "utf8" ) === str，即完全可逆。</p>
		<p class="2">3.3. setCharacterEncoding()</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">该函数用来设置http请求或者相应的编码。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">对于request，是指提交内容的编码，指定后可以通过getParameter()则直接获得正确的字符串，如果不指定，则默认使用iso8859-1编码，需要进一步处理。参见下述"表单输入"。值得注意的是在执行setCharacterEncoding()之前，不能执行任何getParameter()。java doc上说明：This method must be called prior to reading request parameters or reading input using getReader()。而且，该指定只对POST方法有效，对GET方法无效。分析原因，应该是在执行第一个getParameter()的时候，java将会按照编码分析所有的提交内容，而后续的getParameter()不再进行分析，所以setCharacterEncoding()无效。而对于GET方法提交表单是，提交的内容在URL中，一开始就已经按照编码分析所有的提交内容，setCharacterEncoding()自然就无效。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">对于response，则是指定输出内容的编码，同时，该设置会传递给浏览器，告诉浏览器输出内容所采用的编码。</p>
		<p class="2">3.4. 处理过程</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">下面分析两个有代表性的例子，说明java对编码有关问题的处理方法。</p>
		<p class="3">3.4.1. 表单输入</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 22pt">User input<u>  *(gbk:d6d0 cec4)  </u>browser<u>  *(gbk:d6d0 cec4)  </u>web server<u>  iso8859-1(00d6 00d 000ce 00c4)  </u>class，需要在class中进行处理：getbytes("iso8859-1")为<u>d6 d0 ce c4</u>，new String("gbk")为<u>d6d0 cec4</u>，内存中以unicode编码则为<u>4e2d 6587</u>。</p>
		<p class="MsoBodyTextIndent" style="MARGIN-LEFT: 43pt; TEXT-INDENT: -21pt; tab-stops: list 43.0pt">
				<span lang="EN-US" style="FONT-SIZE: 11pt; FONT-FAMILY: Wingdings">l</span> 用户输入的编码方式和页面指定的编码有关，也和用户的操作系统有关，所以是不确定的，上例以gbk为例。</p>
		<p class="MsoBodyTextIndent" style="MARGIN-LEFT: 43pt; TEXT-INDENT: -21pt; tab-stops: list 43.0pt">
				<span lang="EN-US" style="FONT-SIZE: 11pt; FONT-FAMILY: Wingdings">l</span> 从browser到web server，可以在表单中指定提交内容时使用的字符集，否则会使用页面指定的编码。而如果在url中直接用?的方式输入参数，则其编码往往是操作系统本身的编码，因为这时和页面无关。上述仍旧以gbk编码为例。</p>
		<p class="MsoBodyTextIndent" style="MARGIN-LEFT: 43pt; TEXT-INDENT: -21pt; tab-stops: list 43.0pt">
				<span lang="EN-US" style="FONT-SIZE: 11pt; FONT-FAMILY: Wingdings">l</span> Web server接收到的是字节流，默认时（getParameter）会以iso8859-1编码处理之，结果是不正确的，所以需要进行处理。但如果预先设置了编码（通过request. setCharacterEncoding ()），则能够直接获取到正确的结果。</p>
		<p class="MsoBodyTextIndent" style="MARGIN-LEFT: 43pt; TEXT-INDENT: -21pt; tab-stops: list 43.0pt">
				<span lang="EN-US" style="FONT-SIZE: 11pt; FONT-FAMILY: Wingdings">l</span> 在页面中指定编码是个好习惯，否则可能失去控制，无法指定正确的编码。</p>
		<p class="3">3.4.2. 文件编译</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 22pt">假设文件是gbk编码保存的，而编译有两种编码选择：gbk或者iso8859-1，前者是中文windows的默认编码，后者是linux的默认编码，当然也可以在编译时指定编码。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 22pt">Jsp<u>  *(gbk:d6d0 cec4)  </u>java file<u>  *(gbk:d6d0 cec4)  </u>compiler read<u>  uincode(gbk: 4e2d 6587; iso8859-1: 00d6 00d 000ce 00c4)  </u>compiler write<u>  utf(gbk: e4b8ad e69687; iso8859-1: *)  </u>compiled file<u>  unicode(gbk: 4e2d 6587; iso8859-1: 00d6 00d 000ce 00c4)  </u>class。所以用gbk编码保存，而用iso8859-1编译的结果是不正确的。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 22pt">class<u>  unicode(4e2d 6587)  </u>system.out / jsp.out<u>  gbk(d6d0 cec4)  </u>os console / browser。</p>
		<p class="MsoBodyTextIndent" style="MARGIN-LEFT: 43pt; TEXT-INDENT: -21pt; tab-stops: list 43.0pt">
				<span lang="EN-US" style="FONT-SIZE: 11pt; FONT-FAMILY: Wingdings">l</span> 文件可以以多种编码方式保存，中文windows下，默认为ansi/gbk。</p>
		<p class="MsoBodyTextIndent" style="MARGIN-LEFT: 43pt; TEXT-INDENT: -21pt; tab-stops: list 43.0pt">
				<span lang="EN-US" style="FONT-SIZE: 11pt; FONT-FAMILY: Wingdings">l</span> 编译器读取文件时，需要得到文件的编码，如果未指定，则使用系统默认编码。一般class文件，是以系统默认编码保存的，所以编译不会出问题，但对于jsp文件，如果在中文windows下编辑保存，而部署在英文linux下运行/编译，则会出现问题。所以需要在jsp文件中用pageEncoding指定编码。</p>
		<p class="MsoBodyTextIndent" style="MARGIN-LEFT: 43pt; TEXT-INDENT: -21pt; tab-stops: list 43.0pt">
				<span lang="EN-US" style="FONT-SIZE: 11pt; FONT-FAMILY: Wingdings">l</span> Java编译的时候会转换成统一的unicode编码处理，最后保存的时候再转换为utf编码。</p>
		<p class="MsoBodyTextIndent" style="MARGIN-LEFT: 43pt; TEXT-INDENT: -21pt; tab-stops: list 43.0pt">
				<span lang="EN-US" style="FONT-SIZE: 11pt; FONT-FAMILY: Wingdings">l</span> 当系统输出字符的时候，会按指定编码输出，对于中文windows下，System.out将使用gbk编码，而对于response（浏览器），则使用jsp文件头指定的contentType，或者可以直接为response指定编码。同时，会告诉browser网页的编码。如果未指定，则会使用iso8859-1编码。对于中文，应该为browser指定输出字符串的编码。</p>
		<p class="MsoBodyTextIndent" style="MARGIN-LEFT: 43pt; TEXT-INDENT: -21pt; tab-stops: list 43.0pt">
				<span lang="EN-US" style="FONT-SIZE: 11pt; FONT-FAMILY: Wingdings">l</span> browser显示网页的时候，首先使用response中指定的编码（jsp文件头指定的contentType最终也反映在response上），如果未指定，则会使用网页中meta项指定中的contentType。</p>
		<p class="2">3.5. 几处设置</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">对于web应用程序，和编码有关的设置或者函数如下。</p>
		<p class="3">3.5.1. jsp编译</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">指定文件的存储编码，很明显，该设置应该置于文件的开头。例如：&lt;%@page pageEncoding="GBK"%&gt;。另外，对于一般class文件，可以在编译的时候指定编码。</p>
		<p class="3">3.5.2. jsp输出</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">指定文件输出到browser是使用的编码，该设置也应该置于文件的开头。例如：&lt;%@ page contentType="text/html; charset= GBK" %&gt;。该设置和response.setCharacterEncoding("GBK")等效。</p>
		<p class="3">3.5.3. meta设置</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">指定网页使用的编码，该设置对静态网页尤其有作用。因为静态网页无法采用jsp的设置，而且也无法执行response.setCharacterEncoding()。例如：&lt;<?xml:namespace prefix = st1 /?><st1:place w:st="on">META http-equiv="Content-Type" content="text/html; charset=GBK" /&gt;</st1:place></p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">如果同时采用了jsp输出和meta设置两种编码指定方式，则jsp指定的优先。因为jsp指定的直接体现在response中。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">需要注意的是，apache有一个设置可以给无编码指定的网页指定编码，该指定等同于jsp的编码指定方式，所以会覆盖静态网页中的meta指定。所以有人建议关闭该设置。</p>
		<p class="3">3.5.4. form设置</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">当浏览器提交表单的时候，可以指定相应的编码。例如：&lt;form accept-charset= "gb2312"&gt;。一般不必不使用该设置，浏览器会直接使用网页的编码。</p>
		<p class="1">4. 系统软件</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">下面讨论几个相关的系统软件。</p>
		<p class="2">4.1. mysql数据库</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 22pt">很明显，要支持多语言，应该将数据库的编码设置成utf或者unicode，而utf更适合与存储。但是，如果中文数据中包含的英文字母很少，其实unicode更为适合。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 22pt">数据库的编码可以通过mysql的配置文件设置，例如default-character-set=utf8。还可以在数据库链接URL中设置，例如： useUnicode=true&amp;characterEncoding=UTF-8。注意这两者应该保持一致，在新的sql版本里，在数据库链接URL里可以不进行设置，但也不能是错误的设置。</p>
		<p class="2">4.2. apache</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">appache和编码有关的配置在httpd.conf中，例如AddDefaultCharset UTF-8。如前所述，该功能会将所有静态页面的编码设置为UTF-8，最好关闭该功能。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">另外，apache还有单独的模块来处理网页响应头，其中也可能对编码进行设置。</p>
		<p class="2">4.3. linux默认编码</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">这里所说的linux默认编码，是指运行时的环境变量。两个重要的环境变量是LC_ALL和LANG，默认编码会影响到java URLEncode的行为，下面有描述。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">建议都设置为"zh_CN.UTF-8"。</p>
		<p class="2">4.4. 其它</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">为了支持中文文件名，linux在加载磁盘时应该指定字符集，例如：mount /dev/hda5 /mnt/hda5/ -t ntfs -o iocharset=gb2312。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">另外，如前所述，使用GET方法提交的信息不支持request.setCharacterEncoding()，但可以通过tomcat的配置文件指定字符集，在tomcat的server.xml文件中，形如：&lt;Connector ... URIEncoding="GBK"/&gt;。这种方法将统一设置所有请求，而不能针对具体页面进行设置，也不一定和browser使用的编码相同，所以有时候并不是所期望的。</p>
		<p class="1">5. URL地址</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">URL地址中含有中文字符是很麻烦的，前面描述过使用GET方法提交表单的情况，使用GET方法时，参数就是包含在URL中。</p>
		<p class="2">5.1. URL编码</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">对于URL中的一些特殊字符，浏览器会自动进行编码。这些字符除了"/?&amp;"等外，还包括unicode字符，比如汉子。这时的编码比较特殊。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">IE有一个选项"总是使用UTF-8发送URL"，当该选项有效时，IE将会对特殊字符进行UTF-8编码，同时进行URL编码。如果改选项无效，则使用默认编码"GBK"，并且不进行URL编码。但是，对于URL后面的参数，则总是不进行编码，相当于UTF-8选项无效。比如"中文.html?a=中文"，当UTF-8选项有效时，将发送链接"%<u>e4%b8%ad%e6%96%87.html?a=\x4e\x2d\x65\x87</u>"；而UTF-8选项无效时，将发送链接"<u>\x4e\x2d\x65\x87.html?a=\x4e\x2d\x65\x87</u>"。注意后者前面的"中文"两个字只有4个字节，而前者却有18个字节，这主要时URL编码的原因。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 22pt">当web server（tomcat）接收到该链接时，将会进行URL解码，即去掉"%"，同时按照ISO8859-1编码（上面已经描述，可以使用URLEncoding来设置成其它编码）识别。上述例子的结果分别是"<u>\ue4\ub8\uad\ue6\u96\u87.html?a=\u4e\u2d\u65\u87</u>"和"<u>\u4e\u2d\u65\u87.html?a=\u4e\u2d\u65\u87</u>"，注意前者前面的"中文"两个字恢复成了6个字符。这里用"\u"，表示是unicode。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 22pt">所以，由于客户端设置的不同，相同的链接，在服务器上得到了不同结果。这个问题不少人都遇到，却没有很好的解决办法。所以有的网站会建议用户尝试关闭UTF-8选项。不过，下面会描述一个更好的处理办法。</p>
		<p class="2">5.2. rewrite</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">熟悉的人都知道，apache有一个功能强大的rewrite模块，这里不描述其功能。需要说明的是该模块会自动将URL解码（去除%），即完成上述web server（tomcat）的部分功能。有相关文档介绍说可以使用[NE]参数来关闭该功能，但我试验并未成功，可能是因为版本（我使用的是apache <st1:chsdate w:st="on" isrocdate="False" islunardate="False" day="30" month="12" year="1899">2.0.54）问题。另外，当参数中含有"?&amp; "等符号的时候，该功能将导致系统得不到正常结果。</st1:chsdate></p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">rewrite本身似乎完全是采用字节处理的方式，而不考虑字符串的编码，所以不会带来编码问题。</p>
		<p class="2">5.3. URLEncode.encode()</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">这是Java本身提供对的URL编码函数，完成的工作和上述UTF-8选项有效时浏览器所做的工作相似。值得说明的是，java已经不赞成不指定编码来使用该方法（deprecated）。应该在使用的时候增加编码指定。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">当不指定编码的时候，该方法使用系统默认编码，这会导致软件运行结果得不确定。比如对于"中文"，当系统默认编码为"gb2312"时，结果是"%<u>4e%2d%65%87</u>"，而默认编码为"UTF-8"，结果却是"%<u>e4%b8%ad%e6%96%87</u>"，后续程序将难以处理。另外，这儿说的系统默认编码是由运行tomcat时的环境变量LC_ALL和LANG等决定的，曾经出现过tomcat重启后就出现乱码的问题，最后才郁闷的发现是因为修改修改了这两个环境变量。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">建议统一指定为"UTF-8"编码，可能需要修改相应的程序。</p>
		<p class="2">5.4. 一个解决方案</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">上面说起过，因为浏览器设置的不同，对于同一个链接，web server收到的是不同内容，而软件系统有无法知道这中间的区别，所以这一协议目前还存在缺陷。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">针对具体问题，不应该侥幸认为所有客户的IE设置都是UTF-8有效的，也不应该粗暴的建议用户修改IE设置，要知道，用户不可能去记住每一个web server的设置。所以，接下来的解决办法就只能是让自己的程序多一点智能：根据内容来分析编码是否UTF-8。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">比较幸运的是UTF-8编码相当有规律，所以可以通过分析传输过来的链接内容，来判断是否是正确的UTF-8字符，如果是，则以UTF-8处理之，如果不是，则使用客户默认编码（比如"GBK"），下面是一个判断是否UTF-8的例子，如果你了解相应规律，就容易理解。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">public static boolean isValidUtf8(byte[] b,int aMaxCount){</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">       int lLen=b.length,lCharCount=0;</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">       for(int i=0;i&lt;lLen &amp;&amp; lCharCount&lt;aMaxCount;++lCharCount){</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">              byte lByte=b[i++];//to fast operation, ++ now, ready for the following for(;;)</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">              if(lByte&gt;=0) continue;//&gt;=0 is normal ascii</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">              if(lByte&lt;(byte)0xc0 || lByte&gt;(byte)0xfd) return false;</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">              int lCount=lByte&gt;(byte)0xfc?5:lByte&gt;(byte)0xf8?4</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">                     :lByte&gt;(byte)0xf0?3:lByte&gt;(byte)0xe0?2:1;</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">              if(i+lCount&gt;lLen) return false;</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">              for(int j=0;j&lt;lCount;++j,++i) if(b[i]&gt;=(byte)0xc0) return false;</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">       }</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">       return true;</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">}</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">相应地，一个使用上述方法的例子如下：</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">public static String getUrlParam(String aStr,String aDefaultCharset)</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">throws UnsupportedEncodingException{</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">       if(aStr==null) return null;</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">       byte[] lBytes=aStr.getBytes("ISO-8859-1");</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">       return new String(lBytes,StringUtil.isValidUtf8(lBytes)?"utf8":aDefaultCharset);</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">}</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">不过，该方法也存在缺陷，如下两方面：</p>
		<p class="MsoBodyTextIndent" style="MARGIN-LEFT: 42pt; TEXT-INDENT: -21pt; tab-stops: list 42.0pt">
				<span lang="EN-US" style="FONT-FAMILY: Wingdings">l</span> 没有包括对用户默认编码的识别，这可以根据请求信息的语言来判断，但不一定正确，因为我们有时候也会输入一些韩文，或者其他文字。</p>
		<p class="MsoBodyTextIndent" style="MARGIN-LEFT: 42pt; TEXT-INDENT: -21pt; tab-stops: list 42.0pt">
				<span lang="EN-US" style="FONT-FAMILY: Wingdings">l</span> 可能会错误判断UTF-8字符，一个例子是"学习"两个字，其GBK编码是" <u>\xd1\xa7\xcf\xb0</u>"，如果使用上述isValidUtf8方法判断，将返回true。可以考虑使用更严格的判断方法，不过估计效果不大。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">有一个例子可以证明google也遇到了上述问题，而且也采用了和上述相似的处理方法，比如，如果在地址栏中输入"<a href="http://www.google.com/search?hl=zh-CN&amp;newwindow=1&amp;q=学习">http://www.google.com/search?hl=zh-CN&amp;newwindow=1&amp;q=学习</a>"，google将无法正确识别，而其他汉字一般能够正常识别。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">最后，应该补充说明一下，如果不使用rewrite规则，或者通过表单提交数据，其实并不一定会遇到上述问题，因为这时可以在提交数据时指定希望的编码。另外，中文文件名确实会带来问题，应该谨慎使用。</p>
		<p class="1">6. 其它</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">下面描述一些和编码有关的其他问题。</p>
		<p class="2">6.1. SecureCRT</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">除了浏览器和控制台与编码有关外，一些客户端也很有关系。比如在使用SecureCRT连接linux时，应该让SecureCRT的显示编码（不同的session，可以有不同的编码设置）和linux的编码环境变量保持一致。否则看到的一些帮助信息，就可能是乱码。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">另外，mysql有自己的编码设置，也应该保持和SecureCRT的显示编码一致。否则通过SecureCRT执行sql语句的时候，可能无法处理中文字符，查询结果也会出现乱码。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">对于Utf-8文件，很多编辑器（比如记事本）会在文件开头增加三个不可见的标志字节，如果作为mysql的输入文件，则必须要去掉这三个字符。（用linux的vi保存可以去掉这三个字符）。一个有趣的现象是，在中文windows下，创建一个新txt文件，用记事本打开，输入"连通"两个字，保存，再打开，你会发现两个字没了，只留下一个小黑点。</p>
		<p class="2">6.2. 过滤器</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">如果需要统一设置编码，则通过filter进行设置是个不错的选择。在filter class中，可以统一为需要的请求或者回应设置编码。参加上述setCharacterEncoding()。这个类apache已经给出了可以直接使用的例子SetCharacterEncodingFilter。</p>
		<p class="2">6.3. POST和GET</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">很明显，以POST提交信息时，URL有更好的可读性，而且可以方便的使用setCharacterEncoding()来处理字符集问题。但GET方法形成的URL能够更容易表达网页的实际内容，也能够用于收藏。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">从统一的角度考虑问题，建议采用GET方法，这要求在程序中获得参数是进行特殊处理，而无法使用setCharacterEncoding()的便利，如果不考虑rewrite，就不存在IE的UTF-8问题，可以考虑通过设置URIEncoding来方便获取URL中的参数。</p>
		<p class="2">6.4. 简繁体编码转换</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">GBK同时包含简体和繁体编码，也就是说同一个字，由于编码不同，在GBK编码下属于两个字。有时候，为了正确取得完整的结果，应该将繁体和简体进行统一。可以考虑将UTF、GBK中的所有繁体字，转换为相应的简体字，BIG5编码的数据，也应该转化成相应的简体字。当然，仍旧以UTF编码存储。</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt">例如，对于"语言 語言"，用UTF表示为"<u>\x</u><u>E8\xAF\xAD\xE8\xA8\x80 \xE8\xAA\x9E\xE8\xA8\x80</u>"，进行简繁体编码转换后应该是两个相同的 "<u>\x</u><u>E8\xAF\xAD\xE8\xA8\x80&gt;</u>"。</p>
		<p class="MsoBodyTextIndent"> </p>
		<p class="MsoBodyTextIndent" style="WORD-BREAK: break-all; TEXT-INDENT: 21pt; TEXT-ALIGN: right" align="right">
				<a href="http://china.eceel.com/">Eceel东西在线</a> 刘科垠</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt; TEXT-ALIGN: right" align="right">2006-3-8</p>
		<p class="MsoBodyTextIndent" style="TEXT-INDENT: 21pt; TEXT-ALIGN: right" align="right">转自：<a href="http://china.eceel.com/article/study_for_character_encoding_java.htm">http://china.eceel.com/article/study_for_character_encoding_java.htm</a></p>
<img src ="http://www.blogjava.net/renyangok/aggbug/87178.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:23 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87178.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>程序员趣味读物：谈谈Unicode编码</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87176.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:22:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87176.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87176.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87176.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87176.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87176.html</trackback:ping><description><![CDATA[
		<p>这是一篇程序员写给程序员的趣味读物。所谓趣味是指可以比较轻松地了解一些原来不清楚的概念，增进知识，类似于打RPG游戏的升级。整理这篇文章的动机是两个问题：</p>
		<p>　　问题一： </p>
		<p>　　使用Windows记事本的“另存为”，可以在GBK、Unicode、Unicode big endian和UTF-8这几种编码方式间相互转换。同样是txt文件，Windows是怎样识别编码方式的呢？</p>
		<p>　　我很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节，分别是FF、FE（Unicode）,FE、FF（Unicode big endian）,EF、BB、BF（UTF-8）。但这些标记是基于什么标准呢？</p>
		<p>　　问题二： </p>
		<p>　　最近在网上看到一个ConvertUTF.c，实现了UTF-32、UTF-16和UTF-8这三种编码方式的相互转换。对于Unicode(UCS2)、GBK、UTF-8这些编码方式，我原来就了解。但这个程序让我有些糊涂，想不起来UTF-16和UCS2有什么关系。</p>
		<p>　　查了查相关资料，总算将这些问题弄清楚了，顺带也了解了一些Unicode的细节。写成一篇文章，送给有过类似疑问的朋友。本文在写作时尽量做到通俗易懂，但要求读者知道什么是字节，什么是十六进制。</p>
		<p>0、big endian和little endian</p>
		<p>　　big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时，究竟是将6C写在前面，还是将49写在前面？如果将6C写在前面，就是big endian。还是将49写在前面，就是little endian。</p>
		<p>　　“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开，由此曾发生过六次叛乱，其中一个皇帝送了命，另一个丢了王位。</p>
		<p>　　我们一般将endian翻译成“字节序”，将big endian和little endian称作“大尾”和“小尾”。</p>
		<p>1、字符编码、内码，顺带介绍汉字编码</p>
		<p>　　字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码，为了处理汉字，程序员设计了用于简体中文的GB2312和用于繁体中文的big5。</p>
		<p>　　GB2312(1980年)一共收录了7445个字符，包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7，低字节从A1-FE，占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。</p>
		<p>　　GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号，它分为汉字区和图形符号区。汉字区包括21003个字符。2000年的GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字，同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。现在的PC平台必须支持GB18030，对嵌入式产品暂不作要求。所以手机、MP3一般只支持GB2312。</p>
		<p>　　从ASCII、GB2312、GBK到GB18030，这些编码方法是向下兼容的，即同一个字符在这些方案中总是有相同的编码，后面的标准支持更多的字符。在这些编码中，英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼，GB2312、GBK到GB18030都属于双字节字符集 (DBCS)。</p>
		<p>　　有的中文Windows的缺省内码还是GBK，可以通过GB18030升级包升级到GB18030。不过GB18030相对GBK增加的字符，普通人是很难用到的，通常我们还是用GBK指代中文Windows内码。</p>
		<p>　　这里还有一些细节：</p>
		<p>　　GB2312的原文还是区位码，从区位码到内码，需要在高字节和低字节上分别加上A0。</p>
		<p>　　在DBCS中，GB内码的存储格式始终是big endian，即高位在前。</p>
		<p>　　GB2312的两个字节的最高位都是1。但符合这个条件的码位只有128*128=16384个。所以GBK和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析：在读取DBCS字符流时，只要遇到高位为1的字节，就可以将下两个字节作为一个双字节编码，而不用管低字节的高位是什么。</p>
		<p>2、Unicode、UCS和UTF</p>
		<p>　　前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容（更准确地说，是与ISO-8859-1兼容），与GB码不兼容。例如“汉”字的Unicode编码是6C49，而GB码是BABA。</p>
		<p>　　Unicode也是一种字符编码方法，不过它是由国际组织设计，可以容纳全世界所有语言文字的编码方案。Unicode的学名是"Universal Multiple-Octet Coded Character Set"，简称为UCS。UCS可以看作是"Unicode Character Set"的缩写。</p>
		<p>　　根据维基百科全书(<a href="http://zh.wikipedia.org/wiki/">http://zh.wikipedia.org/wiki/</a>)的记载：历史上存在两个试图独立设计Unicode的组织，即国际标准化组织（ISO）和一个软件制造商的协会（unicode.org）。ISO开发了ISO 10646项目，Unicode协会开发了Unicode项目。</p>
		<p>　　在1991年前后，双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果，并为创立一个单一编码表而协同工作。从Unicode2.0开始，Unicode项目采用了与ISO 10646-1相同的字库和字码。</p>
		<p>　　目前两个项目仍都存在，并独立地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0。ISO的最新标准是10646-3:2003。</p>
		<p>　　UCS规定了怎么用多个字节表示各种文字。怎样传输这些编码，是由UTF(UCS Transformation Format)规范规定的，常见的UTF规范包括UTF-8、UTF-7、UTF-16。</p>
		<p>　　IETF的RFC2781和RFC3629以RFC的一贯风格，清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记不得IETF是Internet Engineering Task Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。</p>
		<p>3、UCS-2、UCS-4、BMP</p>
		<p>　　UCS有两种格式：UCS-2和UCS-4。顾名思义，UCS-2就是用两个字节编码，UCS-4就是用4个字节（实际上只用了31位，最高位必须为0）编码。下面让我们做一些简单的数学游戏：</p>
		<p>　　UCS-2有2^16=65536个码位，UCS-4有2^31=2147483648个码位。</p>
		<p>　　UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个plane。每个plane根据第3个字节分为256行 (rows)，每行包含256个cells。当然同一行的cells只是最后一个字节不同，其余都相同。</p>
		<p>　　group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中，高两个字节为0的码位被称作BMP。</p>
		<p>　　将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节，就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。</p>
		<p>4、UTF编码</p>
		<p>　　UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下：</p>
		<p>UCS-2编码(16进制) UTF-8 字节流(二进制) <br />0000 - 007F 0xxxxxxx <br />0080 - 07FF 110xxxxx 10xxxxxx <br />0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx </p>
		<p>
				<br />　　例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间，所以肯定要用3字节模板了：1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是：0110 110001 001001， 用这个比特流依次代替模板中的x，得到：11100110 10110001 10001001，即E6 B1 89。</p>
		<p>　　读者可以用记事本测试一下我们的编码是否正确。</p>
		<p>　　UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码，UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码，定义了一个算法。不过由于实际使用的UCS2，或者UCS4的BMP必然小于0x10000，所以就目前而言，可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案，UTF-16却要用于实际的传输，所以就不得不考虑字节序的问题。</p>
		<p>5、UTF的字节序和BOM</p>
		<p>　　UTF-8以字节为编码单元，没有字节序的问题。UTF-16以两个字节为编码单元，在解释一个UTF-16文本前，首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E，“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”，那么这是“奎”还是“乙”？</p>
		<p>　　Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表，而是Byte Order Mark。BOM是一个有点小聪明的想法：</p>
		<p>　　在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符，它的编码是FEFF。而FFFE在UCS中是不存在的字符，所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前，先传输字符"ZERO WIDTH NO-BREAK SPACE"。</p>
		<p>　　这样如果接收者收到FEFF，就表明这个字节流是Big-Endian的；如果收到FFFE，就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。</p>
		<p>　　UTF-8不需要BOM来表明字节顺序，但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF（读者可以用我们前面介绍的编码方法验证一下）。所以如果接收者收到以EF BB BF开头的字节流，就知道这是UTF-8编码了。</p>
		<p>　　Windows就是使用BOM来标记文本文件的编码方式的。</p>
		<p>6、进一步的参考资料</p>
		<p>　　本文主要参考的资料是 "Short overview of ISO-IEC 10646 and Unicode" (<a href="http://www.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html">http://www.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html</a>)。</p>
		<p>　　我还找了两篇看上去不错的资料，不过因为我开始的疑问都找到了答案，所以就没有看：</p>
		<p>"Understanding Unicode A general introduction to the Unicode Standard" (<a href="http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-Chapter04a">http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-Chapter04a</a>) <br />"Character set encoding basics Understanding character set encodings and legacy encodings" (<a href="http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-Chapter03">http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-Chapter03</a>) </p>
		<p>　　我写过UTF-8、UCS-2、GBK相互转换的软件包，包括使用Windows API和不使用Windows API的版本。以后有时间的话，我会整理一下放到我的个人主页上(<a href="http://fmddlmyy.home4u.china.com/">http://fmddlmyy.home4u.china.com</a>)。</p>
		<p>　　我是想清楚所有问题后才开始写这篇文章的，原以为一会儿就能写好。没想到考虑措辞和查证细节花费了很长时间，竟然从下午1:30写到9:00。希望有读者能从中受益。<br /></p>
<img src ="http://www.blogjava.net/renyangok/aggbug/87176.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:22 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87176.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java解析XML</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87175.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:21:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87175.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87175.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87175.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87175.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87175.html</trackback:ping><description><![CDATA[
		<p>链接都已失效，可以在网上搜索。<a href="http://blog.dnjs.net/post/95.html"><font color="#4176e1" size="3"><strong><br /><br />高效解析XML</strong></font></a></p>
		<p>
				<a href="http://blog.dnjs.net/post/96.html">
						<font color="#4176e1" size="3">
								<strong>详细解析XML与J2EE组合</strong>
						</font>
				</a>
		</p>
		<p>
				<a href="http://blog.dnjs.net/post/97.html">
						<strong>
								<font size="3">初学者入门 JAVA 的 XML 编程实例解析</font>
						</strong>
				</a>
		</p>
		<p>
				<a href="http://blog.dnjs.net/post/99.html">
						<strong>
								<font size="3">Java中四种XML解析技术之不完全测试</font>
						</strong>
				</a>
		</p>
		<p>
				<a href="http://blog.dnjs.net/post/98.html">
						<font color="#4176e1" size="3">
								<strong>XML认证教程，第 7 部分</strong>
						</font>
				</a>
		</p>
		<p>
				<strong>
						<a href="http://blog.dnjs.net/post/96.html">
								<font color="#095c83" size="3">详细解析XML与J2EE组合技术的精髓</font>
						</a>
				</strong>
		</p>
		<p>
				<strong>
						<a href="http://tech.163.com/06/0524/09/2HSKJ0FU0009159T.html">
								<font color="#095c83" size="3">Java高手解析XML配置文件的读取操作</font>
						</a>
						<br />
						<br />使用dom4j解析xml    <a href="http://www-128.ibm.com/developerworks/cn/xml/x-dom4j.html">http://www-128.ibm.com/developerworks/cn/xml/x-dom4j.html</a><br /><br /><font color="#660000">初学者入门 JAVA 的 XML 编程实例解析 <a href="http://tech.ccidnet.com/art/1077/20050307/219781_1.html">http://tech.ccidnet.com/art/1077/20050307/219781_1.html</a></font></strong>
		</p>
<img src ="http://www.blogjava.net/renyangok/aggbug/87175.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:21 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87175.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>学习日期, 日期格式, 日期的解析和日期的计算 </title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87174.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:20:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87174.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87174.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87174.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87174.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87174.html</trackback:ping><description><![CDATA[Calendar与Date、long的转换：<br />  Calendar ca = Calendar.getInstance();<br />  Date d = ca.getTime();<br />  long l = ca.getTimeInMillis();<br />  ca.setTime(d);<br />  ca.setTimeInMillis(l);<br /><br />Date和long间的转换：<br />  Date d = new Date();<br />  long l = d.getTime();<br />  d.setTime(l);<br />  d = new Date(l);<br />————————————————————————————————<br />  当前年月实际的总天数：   <br />  Calendar   cal   =   new   GregorianCalendar();   <br />    <br />  int   year_days     =   cal.getActualMaximum(Calendar.DAY_OF_YEAR   );   <br />  int   month_days   =   cal.getActualMaximum(Calendar.DAY_OF_MONTH);   <br />   <br />  可能出现的最大天数：   <br />    <br />  int   month_days   =   cal.getMaximum(Calendar.DAY_OF_MONTH);   <br />  //   这种方式不随当前日期的影响，如果取   2   月份，总是的到   29   <br />    <br />    <br />  在给   Calendar   指定月份时要注意：     <br />    <br />  Java   中的月份，0   -   表示1月份，   .....     11   -   表示12月份，不要搞错了哟   保险的方式是，使用常量：Calendar.JANUARY   ...   <br /><br />————————————————————————————————<br /><p><br />学习日期, 日期格式, 日期的解析和日期的计算 </p><p>Java 语言的 Calendar，GregorianCalendar (日历),Date(日期), 和DateFormat(日期格式)组成了Java标准的一个基本但是非常重要的部分. 日期是商业逻辑计算一个关键的部分. 所有的开发者都应该能够计算未来的日期, 定制日期的显示格式, 并将文本数据解析成日期对象。学习日期, 日期格式, 日期的解析和日期的计算。 </p><p>我们将讨论下面的类: </p><p>1、 具体类(和抽象类相对)java.util.Date </p><p>2、 抽象类java.text.DateFormat 和它的一个具体子类,java.text.SimpleDateFormat </p><p>3、 抽象类java.util.Calendar 和它的一个具体子类,java.util.GregorianCalendar </p><p>具体类可以被实例化, 但是抽象类却不能. 你首先必须实现抽象类的一个具体子类.</p><p>1.   java.util.Date及其格式化<br />Date 类从Java 开发包(JDK) 1.0 就开始进化, 当时它只包含了几个取得或者设置一个日期数据的各个部分的方法, 比如说月, 日, 和年. 这些方法现在遭到了批评并且已经被转移到了Calendar类里去了, 我们将在本文中进一步讨论它. 这种改进旨在更好的处理日期数据的国际化格式. 就象在JDK 1.1中一样, Date 类实际上只是一个包裹类, 它包含的是一个长整型数据, 表示的是从GMT(格林尼治标准时间)1970年, 1 月 1日00:00:00这一刻之前或者是之后经历的毫秒数. </p><p>1.1. 创建java.util.Date<br />Java统计从1970年1月1日起的毫秒的数量表示日期。也就是说，例如，1970年1月2日，是在1月1日后的86，400，000毫秒。同样的，1969年12 月31日是在1970年1月1日前86，400，000毫秒。Java的Date类使用long类型纪录这些毫秒值.因为long是有符号整数，所以日期可以在1970年1月1日之前，也可以在这之后。Long类型表示的最大正值和最大负值可以轻松的表示290，000，000年的时间，这适合大多数人的时间要求。</p><p>让我们看一个使用系统的当前日期和时间创建一个日期对象并返回一个长整数的简单例子. 这个时间通常被称为Java 虚拟机(JVM)主机环境的系统时间. <br />import java.util.Date; </p><p>public class DateExample1 { </p><p>public static void main(String[] args) { </p><p>// Get the system date/time </p><p>Date date = new Date(); </p><p>// 打印出具体的年，月，日，小时，分钟，秒钟以及时区</p><p>System.out.println(date.getTime()); </p><p>}   </p><p>} </p><p>在星期六, 2001年9月29日, 下午大约是6:50的样子, 上面的例子在系统输出设备上显示的结果是 1001803809710. 在这个例子中,值得注意的是我们使用了Date 构造函数创建一个日期对象, 这个构造函数没有接受任何参数. 而这个构造函数在内部使用了 System.currentTimeMillis() 方法来从系统获取日期. </p><p>//1年前日期</p><p>  java.util.Date myDate=new java.util.Date(); </p><p>  long myTime=(myDate.getTime()/1000)-60*60*24*365;</p><p>  myDate.setTime(myTime*1000);</p><p>  String mDate=formatter.format(myDate);</p><p>//明天日期</p><p>  myDate=new java.util.Date(); </p><p>  myTime=(myDate.getTime()/1000)+60*60*24;</p><p>  myDate.setTime(myTime*1000);</p><p>  mDate=formatter.format(myDate);</p><p>//两个时间之间的天数</p><p>  SimpleDateFormat myFormatter = new SimpleDateFormat("yyyy-MM-dd");</p><p>  java.util.Date date= myFormatter.parse("2003-05-1"); </p><p>  java.util.Date mydate= myFormatter.parse("1899-12-30");</p><p>  long day=(date.getTime()-mydate.getTime())/(24*60*60*1000);</p><p>//加半小时</p><p>SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");</p><p>java.util.Date date1 = format.parse("2002-02-28 23:16:00");</p><p>long Time=(date1.getTime()/1000)+60*30;</p><p>date1.setTime(Time*1000);</p><p>String mydate1=formatter.format(date1);</p><p>//年月周求日期</p><p>SimpleDateFormat formatter2 = new SimpleDateFormat("yyyy-MM F E");</p><p>java.util.Date date2= formatter2.parse("2003-05 5 星期五"); </p><p>SimpleDateFormat formatter3 = new SimpleDateFormat("yyyy-MM-dd");</p><p>String mydate2=formatter3.format(date2);</p><p>//求是星期几</p><p>mydate= myFormatter.parse("2001-1-1");</p><p>SimpleDateFormat formatter4 = new SimpleDateFormat("E");</p><p>String mydate3=formatter4.format(mydate);</p><p> </p><p> </p><p><br />1.2. Date格式化<br />能以一种用户明白的格式来显示这个日期呢? 在这里类java.text.SimpleDateFormat 和它的抽象基类 java.text.DateFormat。那么, 现在我们已经知道了如何获取从1970年1月1日开始经历的毫秒数了. 我们如何才format 就派得上用场了. </p><p>// 我们能不能用下面的代码构件出 2001/8/8 8:8<br />  import java.io.*;<br />  import java.util.*;</p><p>  public class WhatIsDate<br />  {<br />    public static void main(String[] args) {<br />        Date date = new Date(2001, 8, 8, 8, 8, 8);<br />        System.out.println(date);<br />    }<br />  }</p><p><br />Java 的编译器竟然报如下信息 (Sun JDK1.3, Windows 2000 中文下)</p><p>注意：<br />WhatIsDate.java 使用或覆盖一个不鼓励使用的API。<br />注意：<br />使用-deprecation重新编译，以得到详细信息。！</p><p><br />那么 Date 对象究竟是为了满足哪个需求呢？看来它不是用来实现基于年/月/日小时:分钟 的时间表述。我们查看 Java 的文档，我们看到有 getTime() 方法，它返回的竟然是一个 long 值。</p><p>文档进一步又告诉我们这个值代表了当前系统的时间离1970/1/1 0:0 的毫秒差，而且是在 GMT 时区下(也被称为 EPOC)。如果我们指定的时间是在此之前的，那它将返回一个负数值。</p><p>这个发现让我们对 Date 对象有了一个全新的认识-Date 存放的是与 EPOC 的偏差值。换而言之我们也可通过 long 类型来表示时间？对了，这个猜想是得到了 Java 的支持：</p><p>  // 第二种获得当前时间的方法<br />  long dateInMilliSeconds = System.currentTimeMillis();<br />  // 这时候打印出的只是一串数字而已<br />  System.out.println(dateInMilliSeconds);</p><p><br />对程序执行效率敏感的程序员可以发现这个方法只是生成一个 Java 的原始类型 (primitive type) long, 不需要实例化一个对象。因此如果我们对时间的处理只是在内部进行时，可以用 long 来代替 Date 对象。</p><p>最典型的应用就是在一段代码开始和结束时,分别获得系统当前的时间,然后计算出代码执行所需的时间(微秒级)。</p><p>  long start = System.currentTimeMillis();<br />  // 代码段<br />  System.out.println("需要 "+(System.currentTimeMillis()-start)+" 微秒");</p><p><br />那么当我们要把这个 long 值已更为友好的表现形式显示处理的时候，我们可以用它来构造 Date 对象：</p><p>Date date = new Date(dateInMilliSeconds);</p><p>System.out.println(date);</p><p><br />我们看到了在 Java 中对时间最为基本的表示，有通过对EPOC 的偏差值进行处理。Date 对象是对它的一个对象的封装。我们同时也看到了，在现时世界中我们对时间的描述通常是通过"某年某月某日某时某分"来定义的。Date 的显示(实际上是 toString() 方法)描述了这些信息，但 Java 并不建议我们用这种方式直接来构件 Date 对象。因此我们需要找出哪个对象可以实现这个需求。这就是我们下面就要讲述的 Calendar 对象的功能。</p><p>在我们进一步研究 Calendar 之前，请记住 Date 只是一个对 long 值(基于 GMT 时区)的对象封装。它所表现出来的年/月/日小时:分钟时区的时间表述，只是它的 toString() 方法所提供的。千万不要为这个假象所迷惑。</p><p>假如我们希望定制日期数据的格式, 比方星期六-9月-29日-2001年. 下面的例子展示了如何完成这个工作: </p><p>import java.text.SimpleDateFormat; </p><p>import java.util.Date; </p><p>public class DateExample2 { </p><p>public static void main(String[] args) { </p><p>SimpleDateFormat bartDateFormat = new SimpleDateFormat("EEEE-MMMM-dd-yyyy"); Date date = new Date(); </p><p>System.out.println(bartDateFormat.format(date)); </p><p>}</p><p>}</p><p><br />只要通过向SimpleDateFormat 的构造函数传递格式字符串"EEE-MMMM-dd-yyyy", 我们就能够指明自己想要的格式. 你应该可以看见, 格式字符串中的ASCII 字符告诉格式化函数下面显示日期数据的哪一个部分. EEEE是星期, MMMM是月, dd是日, yyyy是年. 字符的个数决定了日期是如何格式化的.传递"EE-MM-dd-yy"会显示 Sat-09-29-01. 请察看Sun 公司的Web 站点获取日期格式化选项的完整的指示. </p><p>1.3. 文本数据解析成日期对象 <br />假设我们有一个文本字符串包含了一个格式化了的日期对象, 而我们希望解析这个字符串并从文本日期数据创建一个日期对象. 我们将再次以格式化字符串"MM-dd-yyyy" 调用 SimpleDateFormat类, 但是这一次, 我们使用格式化解析而不是生成一个文本日期数据. 我们的例子, 显示在下面, 将解析文本字符串 "9-29-2001"并创建一个值为001736000000 的日期对象. </p><p>通过parse()方法，DateFormat能够以一个字符串创立一个Date对象。这个方法能抛出ParseException异常，所以你必须使用适当的异常处理技术。</p><p>例子程序: </p><p>import java.text.SimpleDateFormat; </p><p>import java.util.Date; </p><p>public class DateExample3 { </p><p>public static void main(String[] args) { </p><p>// Create a date formatter that can parse dates of </p><p>// the form MM-dd-yyyy. </p><p>SimpleDateFormat bartDateFormat = new SimpleDateFormat("MM-dd-yyyy"); </p><p>// Create a string containing a text date to be parsed. </p><p>String dateStringToParse = "9-29-2001"; </p><p>try { </p><p>// Parse the text version of the date. </p><p>// We have to perform the parse method in a </p><p>// try-catch construct in case dateStringToParse </p><p>// does not contain a date in the format we are expecting. </p><p>Date date = bartDateFormat.parse(dateStringToParse); </p><p>// Now send the parsed date as a long value </p><p>// to the system output. </p><p>System.out.println(date.getTime()); </p><p>}catch (Exception ex) { </p><p>System.out.println(ex.getMessage()); </p><p>}</p><p>} </p><p>} </p><p><br />1.4. 使用标准的日期格式化过程 <br />既然我们已经可以生成和解析定制的日期格式了, 让我们来看一看如何使用内建的格式化过程. 方法 DateFormat.getDateTimeInstance() 让我们得以用几种不同的方法获得标准的日期格式化过程. 在下面的例子中, 我们获取了四个内建的日期格式化过程. 它们包括一个短的, 中等的, 长的, 和完整的日期格式. </p><p>import java.text.DateFormat; </p><p>import java.util.Date; </p><p>public class DateExample4 { </p><p>public static void main(String[] args) { </p><p>Date date = new Date(); </p><p>DateFormat shortDateFormat = DateFormat.getDateTimeInstance( </p><p>DateFormat.SHORT, DateFormat.SHORT); </p><p>DateFormat mediumDateFormat = DateFormat.getDateTimeInstance( </p><p>DateFormat.MEDIUM, DateFormat.MEDIUM); </p><p>DateFormat longDateFormat = DateFormat.getDateTimeInstance( </p><p>DateFormat.LONG, DateFormat.LONG); </p><p>DateFormat fullDateFormat = DateFormat.getDateTimeInstance( </p><p>DateFormat.FULL, DateFormat.FULL); </p><p>System.out.println(shortDateFormat.format(date)); System.out.println(mediumDateFormat.format(date)); System.out.println(longDateFormat.format(date)); System.out.println(fullDateFormat.format(date)); </p><p>} </p><p>} </p><p>注意我们在对 getDateTimeInstance的每次调用中都传递了两个值. 第一个参数是日期风格, 而第二个参数是时间风格. 它们都是基本数据类型int(整型). 考虑到可读性, 我们使用了DateFormat 类提供的常量: SHORT, MEDIUM, LONG, 和 FULL. 要知道获取时间和日期格式化过程的更多的方法和选项, 请看Sun 公司Web 站点上的解释. </p><p>运行我们的例子程序的时候, 它将向标准输出设备输出下面的内容: <br />9/29/01 8:44 PM <br />Sep 29, 2001 8:44:45 PM <br />September 29, 2001 8:44:45 PM EDT <br />Saturday, September 29, 2001 8:44:45 PM EDT </p><p>2.   Calendar 日历类 <br />首先请记住 Calendar 只是一个抽象类, 也就是说你无法直接获得它的一个实例，换而言之你可以提供一个自己开发的 Calendar 对象。</p><p>那究竟什么是一个 Calendar 呢？中文的翻译就是日历，那我们立刻可以想到我们生活中有阳(公)历、阴(农)历之分。它们的区别在哪呢？</p><p>比如有：</p><p>月份的定义 - 阳`(公)历 一年12 个月，每个月的天数各不同；阴(农)历，每个月固定28天,每周的第一天 - 阳(公)历星期日是第一天；阴(农)历，星期一是第一天</p><p>实际上，在历史上有着许多种纪元的方法。它们的差异实在太大了，比如说一个人的生日是"八月八日" 那么一种可能是阳(公)历的八月八日，但也可以是阴 (农)历的日期。所以为了计时的统一，必需指定一个日历的选择。那现在最为普及和通用的日历就是 "Gregorian Calendar"。也就是我们在讲述年份时常用 "公元几几年"。Calendar 抽象类定义了足够的方法，让我们能够表述日历的规则。Java 本身提供了对 "Gregorian Calendar" 规则的实现。我们从 Calendar.getInstance() 中所获得的实例就是一个 "GreogrianCalendar" 对象(与您通过 new GregorianCalendar() 获得的结果一致)。</p><p>下面的代码可以证明这一点：</p><p>  import java.io.*;<br />  import java.util.*;</p><p>  public class WhatIsCalendar<br />  {<br />    public static void main(String[] args) {<br />        Calendar calendar = Calendar.getInstance();<br />        if (calendar instanceof GregorianCalendar)<br />          System.out.println("It is an instance of GregorianCalendar");<br />    }<br />  }</p><p> </p><p><br />Calendar 在 Java 中是一个抽象类(Abstract Class)，GregorianCalendar 是它的一个具体实现。</p><p>Calendar 与 Date 的转换非常简单：</p><p>  Calendar calendar = Calendar.getInstance();<br />  // 从一个 Calendar 对象中获取 Date 对象<br />  Date date = calendar.getTime();<br />  // 将 Date 对象反应到一个 Calendar 对象中，<br />  // Calendar/GregorianCalendar 没有构造函数可以接受 Date 对象<br />  // 所以我们必需先获得一个实例，然后设置 Date 对象<br />  calendar.setTime(date);</p><p> </p><p> </p><p>Calendar 对象在使用时，有一些值得注意的事项：</p><p>1. Calendar 的 set() 方法</p><p>set(int field, int value) - 是用来设置"年/月/日/小时/分钟/秒/微秒"等值</p><p>field 的定义在 Calendar 中</p><p>set (int year, int month, int day, int hour, int minute, int second) 但没有set (int year, int month, int day, int hour, int minute, int second, int millisecond) 前面 set(int,int,int,int,int,int) 方法不会自动将 MilliSecond 清为 0。</p><p>另外，月份的起始值为０而不是１，所以要设置八月时，我们用７而不是8。</p><p>calendar.set(Calendar.MONTH, 7);</p><p>我们通常需要在程序逻辑中set(Calendar.MILLISECOND, 0)，否则可能会出现下面的情况：<br />//ObjectOutputStream和ObjectOutputStream是对象存储的类<br />//Calendar.MILLISECOND如不设为0则会是个与当前系统时间有关的数</p><p>  import java.io.*;<br />  import java.util.*;</p><p>  public class WhatIsCalendarWrite<br />  {<br />    public static void main(String[] args) throws Exception{<br />        ObjectOutputStream out =<br />          new ObjectOutputStream(<br />            new FileOutputStream("calendar.out"));<br />        Calendar cal1 = Calendar.getInstance();<br />        cal1.set(2000, 7, 1, 0, 0, 0);<br />        out.writeObject(cal1);<br />        Calendar cal2 = Calendar.getInstance();<br />        cal2.set(2000, 7, 1, 0, 0, 0);<br />        cal2.set(Calendar.MILLISECOND, 0);<br />        out.writeObject(cal2);<br />        out.close();<br />    }<br />  }</p><p><br />我们将 Calendar 保存到文件中</p><p>  import java.io.*;<br />  import java.util.*;</p><p>  public class WhatIsCalendarRead<br />  {<br />    public static void main(String[] args) throws Exception{<br />        ObjectInputStream in =<br />          new ObjectInputStream(<br />            new FileInputStream("calendar.out"));<br />        Calendar cal2 = (Calendar)in.readObject();<br />        Calendar cal1 = Calendar.getInstance();<br />        cal1.set(2000, 7, 1, 0, 0, 0);<br />        if (cal1.equals(cal2))<br />          System.out.println("Equals");<br />        else<br />          System.out.println("NotEqual");<br />        System.out.println("Old calendar "+cal2.getTime().getTime());<br />        System.out.println("New calendar "+cal1.getTime().getTime());<br />        cal1.set(Calendar.MILLISECOND, 0);<br />        cal2 = (Calendar)in.readObject();<br />        if (cal1.equals(cal2))<br />          System.out.println("Equals");<br />        else<br />          System.out.println("NotEqual");<br />        System.out.println("Processed Old calendar "+cal2.getTime().getTime());<br />        System.out.println("Processed New calendar "+cal1.getTime().getTime());<br />    }<br />  }</p><p><br />然后再另外一个程序中取回来（模拟对数据库的存储），但是执行的结果是：</p><p>NotEqual<br />Old calendar 965113200422 &lt;------------ 最后三位的MilliSecond与当前时间有关<br />New calendar 965113200059 &lt;-----------/<br />Equals<br />Processed Old calendar 965113200000<br />Processed New calendar 965113200000</p><p> </p><p>另外我们要注意的一点是，Calendar 为了性能原因对 set() 方法采取延缓计算的方法。在 JavaDoc 中有下面的例子来说明这个问题：</p><p>Calendar cal1 = Calendar.getInstance();<br />  cal1.set(2000, 7, 31, 0, 0 , 0); //2000-8-31<br />  cal1.set(Calendar.MONTH, Calendar.SEPTEMBER); //应该是 2000-9-31，也就是 2000-10-1<br />  cal1.set(Calendar.DAY_OF_MONTH, 30); //如果 Calendar 转化到 2000-10-1，那么现在的结果就该是 2000-10-30<br />  System.out.println(cal1.getTime()); //输出的是2000-9-30，说明 Calendar 不是马上就刷新其内部的记录</p><p><br />在 Calendar 的方法中，get() 和 add() 会让 Calendar 立刻刷新。Set() 的这个特性会给我们的开发带来一些意想不到的结果。我们后面会看到这个问题。</p><p>2. Calendar 对象的容错性，Lenient 设置</p><p>我们知道特定的月份有不同的日期，当一个用户给出错误的日期时，Calendar 如何处理的呢？</p><p>  import java.io.*;<br />  import java.util.*;</p><p>  public class WhatIsCalendar<br />  {<br />    public static void main(String[] args) throws Exception{<br />        Calendar cal1 = Calendar.getInstance();<br />        cal1.set(2000, 1, 32, 0, 0, 0);<br />        System.out.println(cal1.getTime());<br />        cal1.setLenient(false);<br />        cal1.set(2000, 1, 32, 0, 0, 0);<br />        System.out.println(cal1.getTime());<br />    }<br />  }</p><p><br />它的执行结果是：</p><p>  Tue Feb 01 00:00:00 PST 2000<br />  Exception in thread "main" java.lang.IllegalArgumentException<br />    at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:1368)<br />    at java.util.Calendar.updateTime(Calendar.java:1508)<br />    at java.util.Calendar.getTimeInMillis(Calendar.java:890)<br />    at java.util.Calendar.getTime(Calendar.java:871)<br />    at WhatIsCalendar.main(WhatIsCalendar.java:12)</p><p><br />当我们设置该 Calendar 为 Lenient false 时，它会依据特定的月份检查出错误的赋值。</p><p>3. 不稳定的 Calendar</p><p>我们知道 Calendar 是可以被 serialize 的，但是我们要注意下面的问题</p><p>  import java.io.*;<br />  import java.util.*;</p><p>  public class UnstableCalendar implements Serializable<br />  {</p><p>    public static void main(String[] args) throws Exception{<br />        Calendar cal1 = Calendar.getInstance();<br />        cal1.set(2000, 7, 1, 0, 0 , 0);<br />        cal1.set(Calendar.MILLISECOND, 0);<br />        ObjectOutputStream out =<br />          new ObjectOutputStream(<br />          new FileOutputStream("newCalendar.out"));<br />        out.writeObject(cal1);<br />        out.close();<br />        ObjectInputStream in =<br />          new ObjectInputStream(<br />          new FileInputStream("newCalendar.out"));<br />        Calendar cal2 = (Calendar)in.readObject();<br />        cal2.set(Calendar.MILLISECOND, 0);<br />        System.out.println(cal2.getTime());<br />    }<br />  }</p><p> </p><p><br />运行的结果竟然是: Thu Jan 01 00:00:00 PST 1970</p><p>它被复原到 EPOC 的起始点，我们称该 Calendar 是处于不稳定状态。这个问题的根本原因是 Java 在 serialize GregorianCalendar 时没有保存所有的信息，所以当它被恢复到内存中，又缺少足够的信息时，Calendar 会被恢复到 EPOCH 的起始值。Calendar 对象由两部分构成：字段和相对于 EPOC 的微秒时间差。字段信息是由微秒时间差计算出的，而 set() 方法不会强制 Calendar 重新计算字段。这样字段值就不对了。</p><p>下面的代码可以解决这个问题：</p><p>  import java.io.*;<br />  import java.util.*;</p><p>  public class StableCalendar implements Serializable<br />  {<br />    public static void main(String[] args) throws Exception{<br />        Calendar cal1 = Calendar.getInstance();<br />        cal1.set(2000, 7, 1, 0, 0 , 0);<br />        cal1.set(Calendar.MILLISECOND, 0);<br />        ObjectOutputStream out =<br />          new ObjectOutputStream(<br />          new FileOutputStream("newCalendar.out"));<br />        out.writeObject(cal1);<br />        out.close();<br />        ObjectInputStream in =<br />          new ObjectInputStream(<br />          new FileInputStream("newCalendar.out"));<br />        Calendar cal2 = (Calendar)in.readObject();<br />        cal2.get(Calendar.MILLISECOND); //先调用 get()，强制 Calendar 刷新<br />        cal2.set(Calendar.MILLISECOND, 0); //再设值<br />        System.out.println(cal2.getTime());<br />    }<br />  }</p><p><br />运行的结果是: Tue Aug 01 00:00:00 PDT 2000,这个问题主要会影响到在 EJB 编程中，参数对象中包含 Calendar 时。经过 Serialize/Deserialize 后，直接操作 Calendar 会产生不稳定的情况。</p><p>4. add() 与 roll() 的区别</p><p>add() 的功能非常强大，add 可以对 Calendar 的字段进行计算。如果需要减去值，那么使用负数值就可以了，如 add(field, -value)。</p><p>add() 有两条规则：</p><p>当被修改的字段超出它可以的范围时，那么比它大的字段会自动修正。如：</p><p>Calendar cal1 = Calendar.getInstance();</p><p>cal1.set(2000, 7, 31, 0, 0 , 0); //2000-8-31</p><p>cal1.add(Calendar.MONTH, 1); //2000-9-31 =&gt; 2000-10-1，对吗？System.out.println(cal1.getTime()); //结果是 2000-9-30</p><p><br />另一个规则是，如果比它小的字段是不可变的（由 Calendar 的实现类决定），那么该小字段会修正到变化最小的值。</p><p>以上面的例子，9-31 就会变成 9-30，因为变化最小。</p><p>Roll() 的规则只有一条：当被修改的字段超出它可以的范围时，那么比它大的字段不会被修正。如：</p><p>Calendar cal1 = Calendar.getInstance();<br />cal1.set(1999, 5, 6, 0, 0, 0); //1999-6-6, 周日<br />cal1.roll(Calendar.WEEK_OF_MONTH, -1); //1999-6-1, 周二<br />cal1.set(1999, 5, 6, 0, 0, 0); //1999-6-6, 周日<br />cal1.add(Calendar.WEEK_OF_MONTH, -1); //1999-5-30, 周日<br />WEEK_OF_MONTH 比 MONTH 字段小，所以 roll 不能修正 MONTH 字段。</p><p><br />我们现在已经能够格式化并创建一个日期对象了, 但是我们如何才能设置和获取日期数据的特定部分呢, 比如说小时, 日, 或者分钟? 我们又如何在日期的这些部分加上或者减去值呢? 答案是使用Calendar 类. 就如我们前面提到的那样, Calendar 类中的方法替代了Date 类中被人唾骂的方法. </p><p>假设你想要设置, 获取, 和操纵一个日期对象的各个部分, 比方一个月的一天或者是一个星期的一天. 为了演示这个过程, 我们将使用具体的子类 java.util.GregorianCalendar. 考虑下面的例子, 它计算得到下面的第十个星期五是13号. </p><p>import java.util.GregorianCalendar; </p><p>import java.util.Date; </p><p>import java.text.DateFormat; </p><p>public class DateExample5 { </p><p>public static void main(String[] args) { </p><p>DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL); </p><p>// Create our Gregorian Calendar. </p><p>GregorianCalendar cal = new GregorianCalendar(); </p><p>// Set the date and time of our calendar </p><p>// to the system&amp;s date and time </p><p>cal.setTime(new Date()); </p><p>System.out.println("System Date: " + dateFormat.format(cal.getTime())); // Set the day of week to FRIDAY </p><p>cal.set(GregorianCalendar.DAY_OF_WEEK, GregorianCalendar.FRIDAY); System.out.println("After Setting Day of Week to Friday: " + dateFormat.format(cal.getTime())); </p><p>int friday13Counter = 0; </p><p>while (friday13Counter &lt;= 10) { </p><p>// Go to the next Friday by adding 7 days. cal.add(GregorianCalendar.DAY_OF_MONTH, 7); </p><p>// If the day of month is 13 we have </p><p>// another Friday the 13th. </p><p>if (cal.get(GregorianCalendar.DAY_OF_MONTH) == 13) { </p><p>friday13Counter++; System.out.println(dateFormat.format(cal.getTime())); </p><p>} </p><p>} </p><p>} </p><p>} </p><p>在这个例子中我们作了有趣的函数调用: </p><p>cal.set(GregorianCalendar.DAY_OF_WEEK, GregorianCalendar.FRIDAY); </p><p>和:cal.add(GregorianCalendar.DAY_OF_MONTH, 7); </p><p>set 方法能够让我们通过简单的设置星期中的哪一天这个域来将我们的时间调整为星期五. 注意到这里我们使用了常量 DAY_OF_WEEK 和 FRIDAY 来增强代码的可读性. add 方法让我们能够在日期上加上数值. 润年的所有复杂的计算都由这个方法自动处理. </p><p>我们这个例子的输出结果是: </p><p>System Date: Saturday, September 29, 2001 </p><p>当我们将它设置成星期五以后就成了: Friday, September 28, 2001 </p><p>Friday, September 13, 2002 </p><p>Friday, December 13, 2002 </p><p>Friday, June 13, 2003 </p><p>Friday, February 13, 2004 </p><p>Friday, August 13, 2004 </p><p>Friday, May 13, 2005 </p><p>Friday, January 13, 2006 </p><p>Friday, October 13, 2006 </p><p>Friday, April 13, 2007 </p><p>Friday, July 13, 2007 </p><p>Friday, June 13, 2008 </p><p>Calendar类的基础即有变量域的观念。每个类元素都是域，并且这些域在Calendar类中表现为静态变量。这些变量域，可以通过get/set类方法来获得或者设置域值。</p><p>// 获得默认的Calendar实例，给它设置时间<br />Calendarcal = Calendar.getInstance();<br />intyear = cal.get(Calendar.YEAR);<br />cal.set(Calendar.MONTH,Calendar.NOVEMBER); <br />Calendar类的add和roll方法提供在日期之间转换的能力。每个方法都由一个参数变量和一个参数值来修改，通过这个可为正数或负数的参数值来修改它。仅仅不同的是，add方法可以向高阶的变量域溢出。例如，如果从九月三号向后倒退三天，将得到：</p><p>Calendar cal = Calendar.getInstance();</p><p>cal.add(Calendar.DATE,-3);</p><p>// 值为: 星期六八月 31 23:43:19 EDT 2002 </p><p>然而使用roll方法向后回滚三天得出：</p><p>Calendar cal = Calendar.getInstance();</p><p>cal.roll(Calendar.DATE,-3);</p><p>// 值为: 星期一九月 30 23:43:47 EDT 2002 <br />这就是为什么通常主要使用add方法的原因。</p><p>还有一个隐藏在最通用的Calendar的子类中的功能性方法--isLeapYear（判断是否为闰年）方法。</p><p>Calendar cal = Calendar.getInstance();</p><p>booleanleapYear = ( (GregorianCalendar)cal ).isLeapYear(2002);</p><p>// 这个值是false </p><p>尽管它是一个实例方法，isLeapYear方法的行为表现像静态方法，需要提供年份的参数传值给日历。</p><p>其实求几天几月几年前/后的方法，应该用Calendar类比较好的（比Date）。</p><p>Calendar cal = Calendar.getInstance();</p><p>cal.setTime(date);</p><p>cal.add(Calendar.MONTH,1);</p><p>cal.add(Calendar.YEAR,2000);</p><p>date = cal.getTime();</p><p>通过接管日期修改的功能，java.util.Calendar类看上去更像是Data类的复杂版本。但是它还提供额外的功能，更不用说它的国际化支持，使得它值得拥有学习的难度曲线。</p><p>3.     使用GregorianCalendar类<br />创建一个代表任意日期的一个途径使用GregorianCalendar类的构造函数，它包含在java.util包中：</p><p>GregorianCalendar(int year, int month, int date) <br />注意月份的表示，一月是0，二月是1，以此类推，是12月是11。因为大多数人习惯于使用单词而不是使用数字来表示月份，这样程序也许更易读，父类 Calendar使用常量来表示月份：JANUARY, FEBRUARY,等等。所以，创建Wilbur 和 Orville制造第一架动力飞机的日期（December 17, 1903），你可以使用：</p><p>GregorianCalendar firstFlight = new GregorianCalendar(1903, Calendar.DECEMBER, 17); </p><p>出于清楚的考虑，你应该使用前面的形式。但是，你也应该学习怎样阅读下面的短格式。下面的例子同样表示December 17,1903（记住，在短格式中，11表示December）</p><p>    GregorianCalendar firstFlight = new GregorianCalendar(1903, 11, 17);   在上一节中，你学习了转换Date对象到字符串。这里，你可以做同样的事情；但是首先，你需要将GregorianCalendar对象转换到Date。要做到这一点，你可以使用getTime()方法，从它得父类 Calendar继承而来。GetTime()方法返回GregorianCalendar相应的Date对象。你能够创建 GregorianCalendar对象，转换到Date对象，得到和输出相应的字符串这样一个过程。下面是例子： </p><p>import java.util.*;</p><p>import java.text.*;</p><p>public class Flight {</p><p>  public static void main(String[] args) {</p><p>GregorianCalendar firstFlight = new GregorianCalendar(1903, Calendar.DECEMBER, 17);   </p><p>Date d = firstFlight.getTime();</p><p>DateFormat df = DateFormat.getDateInstance();</p><p>String s = df.format(d);</p><p>System.out.println("First flight was " + s);</p><p>}</p><p>有时候创建一个代表当前时刻的GregorianCalendar类的实例是很有用的。你可以简单的使用没有参数的GregorianCalendar构造函数，象这样：</p><p>GregorianCalendar thisday = new GregorianCalendar();<br />一个输出今天日期的例子程序，使用GregorianCalendar对象：</p><p>import java.util.*;<br />import java.text.*;<br />class Today {<br />  public static void main(String[] args) {<br />GregorianCalendar thisday = new GregorianCalendar(); <br />Date d = thisday.getTime();<br />DateFormat df = DateFormat.getDateInstance();<br />String s = df.format(d);<br />    System.out.println("Today is " + s);<br />  }<br />}<br />注意到，Date()构造函数和GregorianCalendar()构造函数很类似：都创建一个对象，条件简单，代表今天。<br />GregorianCalendar 类提供处理日期的方法。一个有用的方法是add().使用add()方法，你能够增加象年，月数，天数到日期对象中。要使用add()方法，你必须提供要增加的字段，要增加的数量。一些有用的字段是DATE, MONTH, YEAR, 和 WEEK_OF_YEAR。下面的程序使用add()方法计算未来80天的一个日期。在Jules的&lt;环球80天&gt;是一个重要的数字，使用这个程序可以计算Phileas Fogg从出发的那一天1872 年10月2日后80天的日期：</p><p>import java.util.*;<br />import java.text.*;<br />public class World {<br />  public static void main(String[] args) {<br />GregorianCalendar worldTour = new GregorianCalendar(1872, Calendar.OCTOBER, 2);<br />    worldTour.add(GregorianCalendar.DATE, 80);<br />Date d = worldTour.getTime();<br />DateFormat df = DateFormat.getDateInstance();<br />String s = df.format(d);<br />System.out.println("80 day trip will end " + s);<br />  }<br />}<br />add ()一个重要的副作用是它改变了原来的日期。有时候，拥有原始日期和修改后的日期很重要。不幸的是，你不能简单的创建一个 GregorianCalendar对象，设置它和原来的相等（equal）。原因是两个变量指向同一个Date()对象地址。如果Date对象改变，两个变量就指向改变后的日期对象。代替这种做法，应该创建一个新对象。下面的程序示范了这种做法：import java.util.*;</p><p>import java.text.*;</p><p>public class ThreeDates {</p><p>  public static void main(String[] args) {</p><p>GregorianCalendar gc1 = new GregorianCalendar(2000, Calendar.JANUARY, 1);</p><p>GregorianCalendar gc2 = gc1;</p><p>GregorianCalendar gc3 = new GregorianCalendar(2000, Calendar.JANUARY, 1);<br />    //Three dates all equal to January 1, 2000</p><p>gc1.add(Calendar.YEAR, 1);</p><p>//gc1 and gc2 are changed     </p><p>DateFormat df = DateFormat.getDateInstance();</p><p>Date d1 = gc1.getTime();</p><p>Date d2 = gc2.getTime();</p><p>Date d3 = gc3.getTime();</p><p>String s1 = df.format(d1);</p><p>String s2 = df.format(d2);</p><p>String s3 = df.format(d3);</p><p>System.out.println("gc1 is " + s1);</p><p>System.out.println("gc2 is " + s2);</p><p>System.out.println("gc3 is " + s3);</p><p>  }</p><p>}</p><p>      程序运行后，gc1和gc2被变成2001年（因为两个对象指向同一个Date，而Date已经被改变了）。对象gc3指向一个单独的Date，它没有被改变。<br />package com.minght.sys.util;</p><p>/**<br />* &lt;p&gt;Title: 开源,开放&lt;/p&gt;<br />* &lt;p&gt;Description: opeansource&lt;/p&gt;<br />* &lt;p&gt;Copyright: Copyright (c) 2004&lt;/p&gt;<br />* &lt;p&gt;Company: ?海棠&lt;/p&gt;<br />* @author HaiTang Ming<br />* @version 1.0<br />*/</p><p>import java.util.*;<br />import java.math.BigDecimal;<br />import java.math.BigInteger;<br />import java.sql.Timestamp;<br />import java.text.*;</p><p><br />public class timeUtil {</p><p>/**<br />  * 将Date类型日期转化成String类型"任意"格式<br />  * java.sql.Date,java.sql.Timestamp类型是java.util.Date类型的子类<br />  * @param date Date<br />  * @param format String<br />  *           "2003-01-01"格式<br />  *           "yyyy年M月d日"<br />  *           "yyyy-MM-dd HH:mm:ss"格式<br />  * @return String<br />  */<br />public static String dateToString(java.util.Date date,String format) {</p><p>    if (date==null || format==null) {<br />      return null;<br />    }</p><p>    SimpleDateFormat sdf = new SimpleDateFormat(format);<br />    String str = sdf.format(date);<br />    return str;<br />}</p><p>/**<br />  * 将String类型日期转化成java.utl.Date类型"2003-01-01"格式<br />  * @param str String 要格式化的字符串<br />  * @param format String<br />  * @return Date<br />  */<br />public static java.util.Date stringToUtilDate(String str,String format) {</p><p>    if (str==null||format==null) {<br />      return null;<br />    }</p><p>    SimpleDateFormat sdf = new SimpleDateFormat(format);</p><p>    java.util.Date date = null;<br />    try<br />    {<br />      date = sdf.parse(str);<br />    }<br />    catch(Exception e)<br />    {<br />    }<br />    return date;<br />}</p><p><br />/**<br />  * 将String类型日期转化成java.sql.Date类型"2003-01-01"格式<br />  * @param str String<br />  * @param format String<br />  * @return Date<br />  */<br />public static java.sql.Date stringToSqlDate(String str,String format) {</p><p>    if (str==null||format==null) {<br />      return null;<br />    }</p><p>    SimpleDateFormat sdf = new SimpleDateFormat(format);</p><p>    java.util.Date date = null;<br />    try<br />    {<br />      date = sdf.parse(str);<br />    }<br />    catch(Exception e)<br />    {<br />      return null;<br />    }<br />    return new java.sql.Date(date.getTime());<br />}</p><p>/**<br />  * 将String类型日期转化成java.sql.Date类型"2003-01-01"格式<br />  * @param str String<br />  * @param format String<br />  * @return Timestamp<br />  */<br />public static java.sql.Timestamp stringToTimestamp(String str,String format) {</p><p>    if (str==null||format==null) {<br />      return null;<br />    }</p><p>    SimpleDateFormat sdf = new SimpleDateFormat(format);</p><p>    java.util.Date date = null;<br />    try<br />    {<br />      date = sdf.parse(str);<br />    }<br />    catch(Exception e)<br />    {<br />      return null;<br />    }<br />    return new java.sql.Timestamp(date.getTime());<br />}</p><p><br />/**<br />  * 将java.util.Date日期转化成java.sql.Date类型<br />  * @param Date<br />  * @return 格式化后的java.sql.Date<br />  */<br />public static java.sql.Date toSqlDate(Date date) {</p><p>    if (date==null) {<br />      return null;<br />    }</p><p>  return new java.sql.Date(date.getTime());<br />}<br />/**<br />  * 将字符串转化为时间格式 string to string<br />  * @param str String<br />  * @param format String<br />  * @return String<br />  */<br />public static String toDateString(String str,String oldformat,String newformat){</p><p>    return dateToString(stringToUtilDate(str,oldformat),newformat);</p><p>}</p><p>/**<br />  * 将日历转化为日期<br />  * @param calendar Calendar<br />  * @return Date<br />  */<br />public static java.util.Date converToDate(java.util.Calendar calendar){<br />  return Calendar.getInstance().getTime();<br />}</p><p>/**<br />  * 将日期转化为日历<br />  * @param date Date<br />  * @return Calendar<br />  */<br />public static java.util.Calendar converToCalendar(java.util.Date date){<br />  Calendar calendar = Calendar.getInstance();<br />  calendar.setTime(date);<br />  return calendar;<br />}</p><p>/**<br />  * 求得从某天开始，过了几年几月几日几时几分几秒后，日期是多少<br />  * 几年几月几日几时几分几秒可以为负数<br />  * @param date Date<br />  * @param year int<br />  * @param month int<br />  * @param day int<br />  * @param hour int<br />  * @param min int<br />  * @param sec int<br />  * @return Date<br />  */<br />public static java.util.Date modifyDate(java.util.Date date,int year ,int month,int day,int hour,int min,int sec){<br />  Calendar cal = Calendar.getInstance();<br />  cal.setTime(date);<br />  cal.add(Calendar.YEAR,year);<br />  cal.add(Calendar.MONTH,month);<br />  cal.add(Calendar.DATE,day);<br />  cal.add(Calendar.HOUR,hour);<br />  cal.add(Calendar.MINUTE,min);<br />  cal.add(Calendar.SECOND,sec);</p><p>  return cal.getTime();</p><p>}</p><p><br />/**<br />  * 取得当前日期时间<br />  * 1:year<br />  * 2:month<br />  * 3:day<br />  */<br />public static int getCurTime(int i) {<br />  if (i == 1) {<br />    return java.util.Calendar.getInstance().get(Calendar.YEAR);<br />  }<br />  else if (i == 2) {<br />    return java.util.Calendar.getInstance().get(Calendar.MONTH) + 1;<br />  }<br />  else if (i == 3) {<br />    return java.util.Calendar.getInstance().get(Calendar.DATE);<br />  }<br />  return 0;</p><p>}</p><p>public static void main(String[] args){<br />  System.out.println(dateToString(modifyDate(Calendar.getInstance().getTime(),-1,-1,-1,-1,-1,-1),"yyyy-MM-dd HH:mm:ss"));</p><p>}</p><p>} <br /><br />加一：为了保证跨年的周属于同一周，java API规定跨年的周都是新的一年的第一周，例如：<br /> public static void main(String[] args){<br />  Calendar c = Calendar.getInstance();<br />  c.set(2005, 11, 31);<br />  System.out.println(DateUtil.formatDate(c.getTime()));//2006.12.31<br />  System.out.println(c.get(Calendar.WEEK_OF_MONTH));//06年12月的最后一周<br />  System.out.println(c.get(Calendar.WEEK_OF_YEAR));//07年的第一周<br /> }</p><img src ="http://www.blogjava.net/renyangok/aggbug/87174.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:20 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87174.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>javaIO学习笔记</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87173.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:19:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87173.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87173.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87173.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87173.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87173.html</trackback:ping><description><![CDATA[
		<p>第一篇、<a href="http://www.blueidea.com/bbs/newsdetail.asp?id=996916">http://www.blueidea.com/bbs/newsdetail.asp?id=996916</a>（里面有很多例子）<br /><br />第二篇、彻底明白Java的IO系统(文摘）---JAVA之精髓IO流<br />一． Input和Output<br />1. stream代表的是任何有能力产出数据的数据源，或是任何有能力接收数据的接收源。在Java的IO中，所有的stream（包括Input和Out stream）都包括两种类型：<br /><strong>1.1 以字节为导向的stream</strong><br />以字节为导向的stream，表示以字节为单位从stream中读取或往stream中写入信息。以字节为导向的stream包括下面几种类型：<br />1) input　stream：<br />1) ByteArrayInputStream：把内存中的一个缓冲区作为InputStream使用<br />2) StringBufferInputStream：把一个String对象作为InputStream<br />3) FileInputStream：把一个文件作为InputStream，实现对文件的读取操作<br />4) PipedInputStream：实现了pipe的概念，主要在线程中使用<br />5) SequenceInputStream：把多个InputStream合并为一个InputStream<br />2) Out　stream<br />1) ByteArrayOutputStream：把信息存入内存中的一个缓冲区中<br />2) FileOutputStream：把信息存入文件中<br />3) PipedOutputStream：实现了pipe的概念，主要在线程中使用<br />4) SequenceOutputStream：把多个OutStream合并为一个OutStream<br />1.2 以Unicode字符为导向的stream<br />以Unicode字符为导向的stream，表示以Unicode字符为单位从stream中读取或往stream中写入信息。以Unicode字符为导向的stream包括下面几种类型：<br />1) Input　Stream<br />1) CharArrayReader：与ByteArrayInputStream对应<br />2) StringReader：与StringBufferInputStream对应<br />3) FileReader：与FileInputStream对应<br />4) PipedReader：与PipedInputStream对应<br />2) Out　Stream<br />1) CharArrayWriter：与ByteArrayOutputStream对应<br />2) StringWriter：无与之对应的以字节为导向的stream<br />3) FileWriter：与FileOutputStream对应<br />4) PipedWriter：与PipedOutputStream对应<br />以字符为导向的stream基本上对有与之相对应的以字节为导向的stream。两个对应类实现的功能相同，字是在操作时的导向不同。如CharArrayReader：和ByteArrayInputStream的作用都是把内存中的一个缓冲区作为InputStream使用，所不同的是前者每次从内存中读取一个字节的信息，而后者每次从内存中读取一个字符。<br /><strong>1.3 两种不现导向的stream之间的转换<br /></strong>InputStreamReader和OutputStreamReader：把一个以字节为导向的stream转换成一个以字符为导向的stream。<br /><strong>2. stream添加属性<br /></strong>2.1 “为stream添加属性”的作用<br />运用上面介绍的Java中操作IO的API，我们就可完成我们想完成的任何操作了。但通过FilterInputStream和FilterOutStream的子类，我们可以为stream添加属性。下面以一个例子来说明这种功能的作用。<br />如果我们要往一个文件中写入数据，我们可以这样操作：<br />FileOutStream fs = new FileOutStream(“test.txt”);<br />然后就可以通过产生的fs对象调用write()函数来往test.txt文件中写入数据了。但是，如果我们想实现“先把要写入文件的数据先缓存到内存中，再把缓存中的数据写入文件中”的功能时，上面的API就没有一个能满足我们的需求了。但是通过FilterInputStream和FilterOutStream的子类，为FileOutStream添加我们所需要的功能。<br />2.2 FilterInputStream的各种类型<br />2.2.1 用于封装以字节为导向的InputStream<br />1) DataInputStream：从stream中读取基本类型（int、char等）数据。<br />2) BufferedInputStream：使用缓冲区<br />3) LineNumberInputStream：会记录input stream内的行数，然后可以调用getLineNumber()和setLineNumber(int)<br />4) PushbackInputStream：很少用到，一般用于编译器开发<br />2.2.2 用于封装以字符为导向的InputStream<br />1) 没有与DataInputStream对应的类。除非在要使用readLine()时改用BufferedReader，否则使用DataInputStream<br />2) BufferedReader：与BufferedInputStream对应<br />3) LineNumberReader：与LineNumberInputStream对应<br />4) PushBackReader：与PushbackInputStream对应<br />2.3 FilterOutStream的各种类型<br />2.2.3 用于封装以字节为导向的OutputStream<br />1) DataIOutStream：往stream中输出基本类型（int、char等）数据。<br />2) BufferedOutStream：使用缓冲区<br />3) PrintStream：产生格式化输出<br />2.2.4 用于封装以字符为导向的OutputStream<br />1) BufferedWrite：与对应<br />2) PrintWrite：与对应<br />3. RandomAccessFile<br />1) 可通过RandomAccessFile对象完成对文件的读写操作<br />2) 在产生一个对象时，可指明要打开的文件的性质：r，只读；w，只写；rw可读写<br />3) 可以直接跳到文件中指定的位置<br />4. I/O应用的一个例子<br />import java.io.*;<br />public class TestIO{<br />public static void main(String[] args)<br />throws IOException{<br />//1.以行为单位从一个文件读取数据<br />BufferedReader in = <br />new BufferedReader(<br />new FileReader("F:\\nepalon\\TestIO.java"));<br />String s, s2 = new String();<br />while((s = in.readLine()) != null)<br />s2 += s + "\n";<br />in.close();</p>
		<p>//1b. 接收键盘的输入<br />BufferedReader stdin = <br />new BufferedReader(<br />new InputStreamReader(System.in));<br />System.out.println("Enter a line:");<br />System.out.println(stdin.readLine());</p>
		<p>//2. 从一个String对象中读取数据<br />StringReader in2 = new StringReader(s2);<br />int c;<br />while((c = in2.read()) != -1)<br />System.out.println((char)c);<br />in2.close();</p>
		<p>//3. 从内存取出格式化输入<br />try{<br />DataInputStream in3 = <br />new DataInputStream(<br />new ByteArrayInputStream(s2.getBytes()));<br />while(true)<br />System.out.println((char)in3.readByte()); <br />}<br />catch(EOFException e){<br />System.out.println("End of stream");<br />}</p>
		<p>//4. 输出到文件<br />try{<br />BufferedReader in4 =<br />new BufferedReader(<br />new StringReader(s2));<br />PrintWriter out1 =<br />new PrintWriter(<br />new BufferedWriter(<br />new FileWriter("F:\\nepalon\\ TestIO.out")));<br />int lineCount = 1;<br />while((s = in4.readLine()) != null)<br />out1.println(lineCount++ + "：" + s);<br />out1.close();<br />in4.close();<br />}<br />catch(EOFException ex){<br />System.out.println("End of stream");<br />}</p>
		<p>//5. 数据的存储和恢复<br />try{<br />DataOutputStream out2 = <br />new DataOutputStream(<br />new BufferedOutputStream(<br />new FileOutputStream("F:\\nepalon\\ Data.txt")));<br />out2.writeDouble(3.1415926);<br />out2.writeChars("\nThas was pi:writeChars\n");<br />out2.writeBytes("Thas was pi:writeByte\n");<br />out2.close();<br />DataInputStream in5 =<br />new DataInputStream(<br />new BufferedInputStream(<br />new FileInputStream("F:\\nepalon\\ Data.txt")));<br />BufferedReader in5br =<br />new BufferedReader(<br />new InputStreamReader(in5));<br />System.out.println(in5.readDouble());<br />System.out.println(in5br.readLine());<br />System.out.println(in5br.readLine());<br />}<br />catch(EOFException e){<br />System.out.println("End of stream");<br />}</p>
		<p>//6. 通过RandomAccessFile操作文件<br />RandomAccessFile rf =<br />new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw");<br />for(int i=0; i&lt;10; i++)<br />rf.writeDouble(i*1.414);<br />rf.close();</p>
		<p>rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r");<br />for(int i=0; i&lt;10; i++)<br />System.out.println("Value " + i + "：" + rf.readDouble());<br />rf.close();</p>
		<p>rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw");<br />rf.seek(5*8);<br />rf.writeDouble(47.0001);<br />rf.close();</p>
		<p>rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r");<br />for(int i=0; i&lt;10; i++)<br />System.out.println("Value " + i + "：" + rf.readDouble());<br />rf.close();<br />}<br />}<br />关于代码的解释（以区为单位）：<br />1区中，当读取文件时，先把文件内容读到缓存中，当调用in.readLine()时，再从缓存中以字符的方式读取数据（以下简称“缓存字节读取方式”）。<br />1b区中，由于想以缓存字节读取方式从标准IO（键盘）中读取数据，所以要先把标准IO（System.in）转换成字符导向的stream，再进行BufferedReader封装。<br />2区中，要以字符的形式从一个String对象中读取数据，所以要产生一个StringReader类型的stream。<br />4区中，对String对象s2读取数据时，先把对象中的数据存入缓存中，再从缓冲中进行读取；对TestIO.out文件进行操作时，先把格式化后的信息输出到缓存中，再把缓存中的信息输出到文件中。<br />5区中，对Data.txt文件进行输出时，是先把基本类型的数据输出屋缓存中，再把缓存中的数据输出到文件中；对文件进行读取操作时，先把文件中的数据读取到缓存中，再从缓存中以基本类型的形式进行读取。注意in5.readDouble()这一行。因为写入第一个writeDouble()，所以为了正确显示。也要以基本类型的形式进行读取。<br />6区是通过RandomAccessFile类对文件进行操作。</p>
		<p>第三篇、花1K内存实现高效I/O的RandomAccessFile类（<a href="http://www-128.ibm.com/developerworks/cn/java/l-javaio/index.html">http://www-128.ibm.com/developerworks/cn/java/l-javaio/index.html</a>），解决RandomAccessFile类效率低下的问题，特别是“<a name="5"><span class="atitle"><font face="Arial" size="4">与JDK1.4新类MappedByteBuffer+RandomAccessFile的对比</font></span></a>”部分讲了怎样用jdk自己的功能实现。</p>
<img src ="http://www.blogjava.net/renyangok/aggbug/87173.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:19 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87173.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>类、对象初始化顺序</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87167.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:13:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87167.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87167.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87167.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87167.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87167.html</trackback:ping><description><![CDATA[
		<div>初始化（initialization）其实包含两部分：<br />1.类的初始化（initialization class &amp; interface）<br />2.对象的创建（creation of new class instances）。<br />因为类的初始化其实是类加载（loading of classes）的最后一步，所以很多书中把它归结为“对象的创建”的第一步。其实只是看问题的角度不同而已。为了更清楚的理解，这里还是分开来。</div>
		<div>顺序：<br />应为类的加载肯定是第一步的，所以类的初始化在前。大体的初始化顺序是：<br />类初始化 -&gt; 子类构造函数 -&gt; 父类构造函数 -&gt; 实例化成员变量 -&gt; 继续执行子类构造函数的语句</div>
		<div>下面结合例子，具体解释一下。<br />1。类的初始化（Initialization classes and interfaces），其实很简单，具体来说有：<br />（a）初始化类（initialization of class），是指初始化static field 和执行static初始化块。<br />例如：<br />class Super {<br />        static String s = “initialization static field”; //初始化static field，其中“= “initialization static field” ”又叫做static field initializer</div>
		<div>        // static初始化块，又叫做static initializer，或 static initialization block<br />        static {<br />        System.out.println(“This is static initializer”);<br />}<br />}<br />btw，有些书上提到static initializer 和 static field initializer 的概念，与之对应的还有 instance initializer 和 instance variable initializer。例子中的注释已经解释了其含义。</div>
		<div>（b）初始化接口（initialization of interface），是指初始化定义在该interface中的field。</div>
		<div>*注意*<br />--initialization classes 时，该class的superclass 将首先被初始化，但其实现的interface则不会被初始化。<br />--initialization classes 时，该class的superclass，以及superlcass的superclass 会首先被递归地初始化，从java.lang.Object一直到该class为止。但initialiazation interface的时候，却不需如此，只会初始化该interface本身。<br />--对于由引用类变量（class field）所引发的初始化，只会初始化真正定义该field的class。<br />--如果一个static field是编译时常量（compile-time constant）（即定义为static final field），则对它的引用不会引起定义它的类的初始化。</div>
		<div>为了帮助理解最后两点，请试试看下面的例子：<br />public class Initialization {<br />       <br />        public static void main(String[] args) {<br />               <br />                System.out.println(Sub.x); // Won't cause initialization of Sub, because x is declared by Super, not Sub.<br />                                         // 不会引起Sub类的初始化，因为x是定义在Super类中的<br />                System.out.println("-------------------------");<br />                System.out.println(Sub.y); // Won't cause initialization of Sub, because y is constant.<br />                                         // 不会引起Sub类的初始化，因为y是常量<br />                System.out.println("-------------------------");<br />                System.out.println(Sub.z = 2004); // Will cause initialization of Sub class<br />  // 将会引起Sub的初始化<br />  }<br />}</div>
		<div>class Super{<br />        static int x = 2006;<br />}</div>
		<div>class Sub extends Super {<br />       <br />        static final int y = 2005;<br />       <br />static int z;<br />       <br />static {<br />                System.out.println("Initialization Sub");<br />        }<br />}</div>
		<div>2。对象的创建（creation of new class instances），稍微有点烦琐，具体的步骤如下<br />(a) 所有的成员变量—包括该类，及它的父类中的成员变量--被分配内存空间，并赋予默认值。（Btw，这里是第一次初始化成员变量）<br />(b) 为所调用的构造函数初始化其参数变量。（如果有参数）<br />(c) 如果在构造函数中用this 调用了同类中的其他构造函数，则按照步骤(b)~(f)去处理被调用到的构造函数。<br />(d) 如果在构造函数中用super调用了其父类的构造函数，则按照步骤(b)~(f)去处理被调用到的父类构造函数。<br />(e) 按照书写顺序，执行instance initializer 和 instance variable initializer来初始化成员变量。（Btw，这里是第二次初始化成员变量）<br />(f) 按照书写顺序，执行constructor的其余部分。</div>
		<div>*注意*<br />成员变量其实都被初始化2次，第一次是赋予默认值，第二次才是你想要设定的值。</div>
		<div>最后看一个例子：<br />public class InitializationOrder {</div>
		<div>        public static void main(String[] args) {<br />                Subclass sb = new Subclass();<br />        }<br />}</div>
		<div>class Super{<br />       <br />        static {<br />                System.out.println(1);<br />        }<br />       <br />        Super(int i){<br />                System.out.println(i);<br />        }<br />}</div>
		<div>class Subclass extends Super implements Interface{<br />       <br />        static {<br />                System.out.println(2);<br />        }       <br />       <br />        Super su = new Super(4);<br />       <br />        Subclass() {<br />                super(3);<br />                new Super(5);<br />        }<br />}</div>
		<div>interface Interface{<br />        static Super su = new Super(0);<br />}</div>
		<div>稍微解释一下：<br />首先，Java虚拟机要执行InitializationOrder类中的static 方法main()，这引起了类的初始化。开始初始化InitializationOrder类。具体的步骤略去不说。<br />接着，InitializationOrder类初始化完毕后，开始执行main()方法。语句Subclass sb = new Subclass()将创建一个Subclass对象。加载类Subclass后对其进行类初始化，但因为Subclass有一个父类Super，所以先初始化Super类，初始化块static {System.out.println(1);}被执行，打印输出1；<br />第三，Super初始化完毕后，开始初始化Subclass类。static {System.out.println(2);}被执行，打印输出2；<br />第四，至此，类的加载工作全部完成。开始进入创建Subclass的对象过程。先为Subclass类和其父类Super类分配内存空间，这时Super su 被附值为null;<br />第五，执行构造函数Subclass()时，super(3)被执行。如前面(d)所说，Super类的构造函数Super(int i){….}被调用，并按照步骤(b)~(f)来处理。因此，递归调用Super类的父类Object类的构造函数，并按照步骤(b)~(f)来初始化Object类，不过没有任何输入结果。最后打印输出3；<br />第六，如前面(e)所说，初始化成员变量su，其结果是打印输出4；<br />第七，如前面(f)所说，执行new Super(5)，并打印输出5；<br />最后，Subclass虽然实现了接口Interface，但是初始化它的时候并不会引起接口的初始化，所以接口Interface中的static Super su = new Super(0)自始至终都没有被执行到。</div>
		<div>max做的小改动：<br />public class Test {</div>
		<div>    public static void main(String[] args) {<br />            Subclass sb = new Subclass();<br />    }<br />}</div>
		<div>class SS{<br /> public SS(int i){<br />  System.out.println(i);<br /> }<br />}<br />class Super{<br />   <br />    static {<br />            System.out.println(1);<br />    }<br />    SS ss = new SS(100);<br />   <br />    Super(int i){<br />            System.out.println(i);<br />    }<br />}</div>
		<div>class Subclass extends Super implements Interface{<br />   <br />    static {<br />            System.out.println(2);<br />    }       <br />   <br />    Super su = new Super(4);<br />   <br />    Subclass() {<br />            super(3);<br />            new Super(5);<br />    }<br />}</div>
		<div>interface Interface{<br />    static Super su = new Super(0);<br />}<br />－－－－－－－－－－－－－－－－－－－－<br />结果为：<br />1<br />2<br />100<br />3<br />100<br />4<br />100<br />5</div>
<img src ="http://www.blogjava.net/renyangok/aggbug/87167.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:13 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87167.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA&amp;J2EE FAQ</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87164.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:12:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87164.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87164.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87164.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87164.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87164.html</trackback:ping><description><![CDATA[
		<p>Java FAQ <br /> <br />目录：<br />Q1.1 什么是Java、Java2、JDK？JDK后面的1.3、1.4版本号又是怎么回事？<br />Q1.2 什么是JRE/J2RE？<br />Q1.3 学习Java用什么工具比较好？<br />Q1.4  学习Java有哪些好的参考书？<br />Q1.5  Java和C++哪个更好？<br />Q1.6  什么是J2SE/J2EE/J2ME？<br />Q2.1  我写了第一个Java程序，应该如何编译/运行？<br />Q2.2  我照你说的做了，但是出现什么“'javac' 不是内部或外部命令，也不是可运行的<br />程序或批处理文件。”。<br />Q2.3  环境变量怎么设置？<br />Q2.4  我在javac xxx.java的时候显示什么“unreported exception java.io.IOExcepti<br />on;”。<br />Q2.5  javac xxx.java顺利通过了，但是java xxx的时候显示什么“NoClassDefFoundErr<br />or”。<br />Q2.6  我在java xxx的时候显示“Exception in thread "main" java.lang.NoSuchMetho<br />dError: main”。<br />Q2.7  在java xxx的时候显示“Exception in thread "main" java.lang.NullPointerEx<br />ception”。<br />Q2.8 package是什么意思？怎么用？<br />Q2.9 我没有声明任何package会怎么样？<br />Q2.10 在一个类中怎么使用其他类？<br />Q2.11 我用了package的时候显示"NoClassDefFoundError"，但是我把所有package去掉的<br />时候能正常运行。<br />Q2.12 我想把java编译成exe文件，该怎么做？<br />Q2.13 我在编译的时候遇到什么"deprecated API"，是什么意思？<br />Q3.1 我怎么给java程序加启动参数，就像dir /p/w那样？<br />Q3.2 我怎么从键盘输入一个int/double/字符串？<br />Q3.3 我怎么输出一个int/double/字符串？<br />Q3.4 我发现有些书上直接用System.in输入，比你要简单得多。<br />Q3.5 我怎么从文件输入一个int/double/字符串？<br />Q3.6 我想读写文件的指定位置，该怎么办？<br />Q3.7 怎么判断要读的文件已经到了尽头？<br />Q4.1  java里面怎么定义宏？<br />Q4.2  java里面没法用const。<br />Q4.3  java里面也不能用goto。<br />Q4.4  java里面能不能重载操作符？<br />Q4.5  我new了一个对象，但是没法delete掉它。<br />Q4.6  我想知道为什么main方法必须被声明为public static？为什么在main方法中不能调<br />用非static成员？<br />Q4.7  throw和throws有什么不同？<br />Q4.8  什么是异常？<br />Q4.9  final和finally有什么不同？<br />Q5.1  extends和implements有什么不同？<br />Q5.2  java怎么实现多继承？<br />Q5.3 abstract是什么？<br />Q5.4 public,protected,private有什么不同？<br />Q5.5 Override和Overload有什么不同？<br />Q5.6 我继承了一个方法，但现在我想调用在父类中定义的方法。<br />Q5.7 我想在子类的构造方法中调用父类的构造方法，该怎么办？<br />Q5.8 我在同一个类中定义了好几个构造方法并且想在一个构造方法中调用另一个。<br />Q5.9 我没有定义构造方法会怎么样？<br />Q5.10 我调用无参数的构造方法失败了。<br />Q5.11 我该怎么定义类似于C++中的析构方法（destructor）？<br />Q5.12 我想将一个父类对象转换成一个子类对象该怎么做？<br />Q5.13 其实我不确定a是不是B的实例，能不能分情况处理？<br />Q5.14 我在方法里修改了一个对象的值，但是退出方法后我发现这个对象的值没变！<br />Q6.1 java能动态分配数组吗？<br />Q6.2 我怎么知道数组的长度？<br />Q6.3 我还想让数组的长度能自动改变，能够增加/删除元素。<br />Q     什么是链表？为什么要有ArrayList和LinkedList两种List？<br />Q6.5 我想用队列/栈。<br />Q6.6 我希望不要有重复的元素。<br />Q6.7 我想遍历集合/Map。<br />Q6.8 我还要能够排序。<br />Q6.9 但是我想给数组排序。<br />Q6.10 我想按不同方式排序。<br />Q6.11 Map有什么用？<br />Q6.12 set方法没问题，但是get方法返回的是Object。<br />Q6.13 ArrayList和Vector有什么不同？HashMap和Hashtable有什么不同？<br />Q6.14 我要获得一个随机数。<br />Q6.15 我比较两个String总是false，但是它们明明都是"abc" ！<br />Q6.16 我想修改一个String但是在String类中没找到编辑方法。<br />Q6.17 我想处理日期/时间。</p>
		<p>一、准备篇</p>
		<p>Q1.1 什么是Java、Java2、JDK？JDK后面的1.3、1.4版本号又是怎么回事？<br />答：Java是一种通用的，并发的，强类型的，面向对象的编程语言（摘自Java规范第二版<br />）。<br />JDK是Sun公司分发的免费Java开发工具包，正式名称为J2SDK(Java2 Software Develop K<br />it)。<br />包括基本的java工具包和标准类库。<br />到目前(2003年7月)为止，Java有3个主要版本，即1.0，1.1，2.0；<br />JDK有1.0，1.1，1.2，1.3，1.4五个版本。<br />从JDK1.2起，Sun公司觉得Java改变足够大而将java语言版本号提升为2.0。<br />不同的JDK主要在于提供的类库不同。作为学习你可以下载最新的JDK1.4.2。<br />真正开发时则应考虑向前兼容，比如1.3。下载请去<a href="http://java.sun.com/">http://java.sun.com</a>。<br />JDK1.5预计将在2004年推出，届时其中将包含若干崭新的特性。</p>
		<p>Q1.2 什么是JRE/J2RE？<br />答：J2RE是Java2 Runtime Environment，即Java运行环境，有时简称JRE。<br />如果你只需要运行Java程序或Applet，下载并安装它即可。<br />如果你要自行开发Java软件，请下载JDK。在JDK中附带有JRE。<br />注意由于Microsoft对Java的支持不完全，请不要使用IE自带的虚拟机来运行Applet，务必<br />安装一个JRE或JDK。</p>
		<p>Q1.3 学习Java用什么工具比较好？<br />答：作者建议首先使用JDK+文本编辑器，这有助你理解下列几个基础概念：path，classp<br />ath，package <br />并熟悉基本命令：javac和java。并且下载和你的JDK版本一致的API帮助。<br />如果你不确定类或函数的用法，请先查阅API而不是发贴求助。<br />当你熟悉Java之后，你可以考虑开始使用一个IDE。<br />作者推荐eclipse，下载网址<a href="http://www.eclipse.org/">http://www.eclipse.org</a>。因为eclispe是免费的，插件化的<br />。<br />eclispe的主要缺点是缺乏一个可视化的桌面程序开发工具，<br />幸运的是IBM在2003年11月已经将部分代码捐给eclipse组织，可以预计这个缺点很快就会<br />得到弥补。<br />无论如何，请不要使用Microsoft的VJ++！众所周知Microsoft从来就没有认真支持过Java<br />。<br />最后但并非最不重要，要有一本好的参考书，并且英文要过关。</p>
		<p>Q1.4  学习Java有哪些好的参考书？<br />答：作者首先推荐Thinking in Java，中文名《Java编程思想》，有中文版。<br />目前的最新版本是第三版。<br />在<a href="http://64.78.49.204/">http://64.78.49.204</a>可以免费下载英文版。<br />该书第一章介绍了很多面向对象的编程思想，作为新手应当认真阅读。<br />除此以外，O'relly出版社和Wrox出版社的书也不错。作者本人不喜欢大陆作者的书。<br />也许你觉得英文太难，但是网上大多数资料都是英文的。另外，你需要经常查阅API，而那<br />也是英文的。</p>
		<p>Q1.5  Java和C++哪个更好？<br />答：这个问题是一个很不恰当的问题。你应该问：Java和C++哪个更适用于我的项目？<br />Java的优点和缺点一样明显。<br />跨平台是Java的主要优点，但代价是运行速度的下降。<br />VC和Windows平台有良好的集成和足够快的速度，但是也只能局限在Windows平台上。<br />和C++相比，Java学起来更快，开发人员不会碰到很多容易出错的特性。<br />但是VB程序员甚至只需要拼装模块就可以了。</p>
		<p>Q1.6  什么是J2SE/J2EE/J2ME？<br />答：J2SE就是一般的Java。<br />J2ME是针对嵌入式设备的，比如支持Java的手机，它有自己的JRE和SDK。<br />J2EE是一组用于企业级程序开发的规范和类库，它使用J2SE的JRE。</p>
		<p>二、命令篇</p>
		<p>Q2.1  我写了第一个Java程序，应该如何编译/运行？<br />答：首先请将程序保存为xxx.java文件，注意你可能需要修改文件后缀名。<br />然后在dos窗口下使用javac xxx.java命令，你会发现该目录下多了一个xxx.class文件，</p>
		<p>再使用java xxx命令，你的java程序就开始运行了。</p>
		<p>Q2.2  我照你说的做了，但是出现什么“'javac' 不是内部或外部命令，也不是可运行的<br />程序或批处理文件。”。<br />答：你遇到了path问题。操作系统在一定的范围(path)内搜索javac.exe，但是没能找到。</p>
		<p>请编辑你的操作系统环境变量，新增一个JAVA_HOME变量，设为你JDK的安装目录，<br />再编辑Path变量，加上一项 %JAVA_HOME%\bin。<br />然后保存并新开一个dos窗口，你就可以使用javac和java命令了。</p>
		<p>Q2.3  环境变量怎么设置？<br />答：请向身边会设的人咨询。</p>
		<p>Q2.4  我在javac xxx.java的时候显示什么“unreported exception java.io.IOExcepti<br />on;”。<br />答：参见Q4.8以了解java中的异常机制。</p>
		<p>Q2.5  javac xxx.java顺利通过了，但是java xxx的时候显示什么“NoClassDefFoundErr<br />or”。<br />答：1. 你遇到了classpath问题。java命令在一定的范围(classpath)内搜索你直接或间接<br />使用的class文件，但是未能找到。<br />首先请确认你没有错敲成java xxx.class，<br />其次，检查你的CLASSPATH环境变量，其实你并不需要设置该变量，<br />但如果你设置了该变量又没有包含.（代表当前目录）的项，<br />你就会遇到这个问题。请在你的CLASSPATH环境变量中加入一项. 或干脆删掉这个变量。</p>
		<p>2. 如果你使用了并非JDK自带的标准包，比如javax.servlet.*包，也会遇到这个问题，请<br />将相应的jar文件加入classpath。<br />3. 如果你在java源文件中定义了package，请参见Q2.11。</p>
		<p>
				<br />Q2.6  我在java xxx的时候显示“Exception in thread "main" java.lang.NoSuchMetho<br />dError: main”。<br />答：首先，在你的程序中每个java文件有且只能有一个public类，<br />这个类的类名必须和文件名的大小写完全一样。<br />其次，在你要运行的类中有且只能有一个public static void main(String[] args)方法<br />，<br />这个方法就是你的主程序。</p>
		<p>
				<br />Q2.7  在java xxx的时候显示“Exception in thread "main" java.lang.NullPointerEx<br />ception”。<br />答：在程序中你试图在值为null的对象变量上调用方法，请检查你的程序确保你的对象被恰当的初始化。<br />参见Q4.8以了解java中的异常机制。</p>
		<p>
				<br />Q2.8 package是什么意思？怎么用？<br />答：为了唯一标识每个类并分组，java使用了package的概念。<br />每个类都有一个全名，例如String的全名是java.lang.String，其中java.lang是包名，S<br />tring是短名。按照java命名惯例，包名是全部小写的，而类名的第一个字母是大写的。<br />这样，如果你自行定义了同样名字的类String，你可以把它放在mypackage中，<br />通过使用全名mypackage.String和java.lang.String来区分这两个类。<br />同时，将逻辑上相关的类放在同一个包中，可以使程序结构更为清楚。<br />为了定义包，你要做的就是在java文件开头加一行“package mypackage;”。<br />注意包没有嵌套或包含关系，mypackage包和mypackage.mysubpackage包对JRE来说是并列的两个包（虽然开发者可<br />能暗示包含关系）。</p>
		<p>Q2.9 我没有声明任何package会怎么样？<br />答：你的类被认为放在默认包中。这时全名和短名是一致的。</p>
		<p>Q2.10 在一个类中怎么使用其他类？<br />答：如果你使用java.lang包或者默认包中的类，不用做任何事。<br />如果你的类位于mypackage包中，并且要调用同一包中的其他类，也不用做任何事。<br />如果你使用其他包中的类，在package声明之后，类声明之前使用import otherpackage1.Class<br />1; 或 import otherpackage2.*;  <br />这里.*表示引入这个包中的所有类。然后在程序中你可以使用其他类的短名。<br />如果短名间有重名冲突，必须使用全名来区分。<br />注意在使用其他包中的类时，你只能使用public的类和接口，参见Q5.4。</p>
		<p>Q2.11 我用了package的时候显示"NoClassDefFoundError"，但是我把所有package去掉的<br />时候能正常运行。<br />答：将你的java文件按包名组织存放。<br />比如你的工作目录是/work，你的类是package1.Class1，那么将它存放为/work/package1<br />/Class1.java。<br />如果没有声明包，那么直接放在/work下。<br />在/work下执行javac package1/class1.java，再执行java package1.class1，你会发现一<br />切正常。<br />另外，如果你的类的个数已经多到了你需要使用包来组织的话，你可以考虑开始使用IDE。</p>
		<p>Q2.12 我想把java编译成exe文件，该怎么做？<br />答：JDK只能将java源文件编译为class文件。<br />class文件是一种跨平台的字节码，必须依赖平台相关的JRE来运行。Java以此来实现跨平<br />台性。<br />有些开发工具可以将java文件编译为exe文件。作者反对这种做法，因为这样就取消了跨平<br />台性。<br />如果你确信你的软件只在Windows平台上运行，你可以考虑使用C++/C#来编程。</p>
		<p>Q2.13 我在编译的时候遇到什么"deprecated API"，是什么意思？<br />答：所谓deprecated是指已经过时，但是为了向前兼容起见仍然保留的方法。<br />这些方法可能会在以后取消支持。你应当改用较新的方法。<br />在API里面会说明你应当用什么方法来代替之。</p>
		<p>三、I/O篇</p>
		<p>Q3.1 我怎么给java程序加启动参数，就像dir /p/w那样？<br />答：还记得public static void main(String[] args)吗？这里的args就是你的启动参数<br />。<br />在运行时你输入java package1.class1 arg1 arg2，args中就会有两个String，第一个是<br />arg1，第二个是arg2。</p>
		<p>Q3.2 我怎么从键盘输入一个int/double/字符串？<br />答：java的I/O操作比C++要复杂一点。如果要从键盘输入，样例代码如下：<br />BufferedReader cin = new BufferedReader( new InputStreamReader( System.in ) );</p>
		<p>String s = cin.readLine();<br />这样你就获得了一个字符串，如果你需要数字的话再使用：<br />int n = Integer.parseInt( s ); 或者 double d = Double.parseDouble( s );<br />来将字符串"534"转换成int或double。</p>
		<p>Q3.3 我怎么输出一个int/double/字符串？<br />答：使用System.out.println(n)或者System.out.println("Hello")等等。</p>
		<p>Q3.4 我发现有些书上直接用System.in输入，比你要简单得多。<br />答：java使用unicode，是双字节。而System.in是单字节的stream。<br />如果你要输入双字节文字比如中文，请使用作者的做法。</p>
		<p>Q3.5 我怎么从文件输入/输出一个int/double/字符串？<br />答：类似于从键盘输入，只不过换成<br />BufferedReader fin = new BufferedReader( new FileReader(" myFileName " ) );<br />PrintWriter fout = new PrintWriter( new FileWriter(" myFileName " ) );<br />另外如果你还没下载API，请开始下载并阅读java.io包中的内容。</p>
		<p>Q3.6 我想读写文件的指定位置，该怎么办？<br />答：java.io.RandomAccessFile可以满足你的需要。</p>
		<p>Q3.7 怎么判断要读的文件已经到了尽头？<br />答：在Reader的read方法中明确说明返回-1表示流的结尾。</p>
		<p>四、 关键字篇</p>
		<p>Q4.1  java里面怎么定义宏？<br />答：java不支持宏，因为宏代换不能保证类型安全。<br />如果你需要定义常量，可以将它定义为某个类的static final成员。参见Q4.2和Q4.6。</p>
		<p>
				<br />Q4.2  java里面没法用const。<br />答：你可以用final关键字。例如 final int m = 9。被声明为final的变量不能被再次赋<br />值。唯一的例外是所谓blank final，如下例所示：<br />public class MyClass1 {<br />    private final int a = 3;<br />    private final int b; // blank final</p>
		<p>    public MyClass1() {<br />        a = 5; // 不合法，final变量不能被再次赋值。<br />        b = 4; // 合法，这是b第一次被赋值。<br />        b = 6; // 不合法，b不能被再次赋值。<br />    }<br />}<br />final也可以用于声明方法或类，被声明为final的方法或类不能被继承。<br />注意const是java的保留字以备扩充。</p>
		<p>Q4.3  java里面也不能用goto。<br />答：甚至在面向过程的语言中你也可以完全不用goto。请检查你的程序流程是否合理。</p>
		<p>如果你需要从多层循环中迅速跳出，java增强了（和C++相比）break和continue的功能，<br />支持label。<br />例如：<br />outer : <br />while( ... )<br />{<br />inner :<br />for( ... )<br />{<br />           ...   break inner; ...<br />           ... continue outer; ...<br />}<br />}<br />和const一样，goto也是java的保留字以备扩充。</p>
		<p>Q4.4  java里面能不能重载操作符？<br />答：不能。String的+号是唯一一个内置的重载操作符。你可以通过定义接口和方法来实现<br />类似功能。</p>
		<p>Q4.5  我new了一个对象，但是没法delete掉它。<br />答：java有自动内存回收机制，即所谓Garbarge Collection。你不需要删除对象。你再也<br />不用担心指针错误，内存溢出了。</p>
		<p>Q4.6  我想知道为什么main方法必须被声明为public static？为什么在main方法中不能调<br />用非static成员？<br />答：声明为public是为了这个方法可以被外部调用，详情见Q5.4。<br />static是为了将某个成员变量/方法关联到类（class）而非实例（instance）。<br />你不需要创建一个对象就可以直接使用这个类的static成员，因而在static成员中不能调<br />用非static成员，因为后者是关联到对象实例（instance）的。<br />在A类中调用B类的static成员可以使用B.staticMember的写法。<br />注意一个类的static成员变量是唯一的，被所有该类对象所共享的，在多线程程序设计中尤其要谨慎小心。<br />类的static成员是在类第一次被JRE装载的时候初始化的。<br />你可以使用如下方法来使用非static成员：<br />public class A<br />{<br />    private void someMethod() //非static成员<br />    {}<br />    public static void main(String args)<br />    {<br />         A a = new A();  //创建一个对象实例<br />         a.someMethod();  //现在你可以使用非static方法了<br />    }<br />}</p>
		<p>
				<br />Q4.7  throw和throws有什么不同？<br />答：throws用于方法声明中，声明一个方法会抛出哪些异常。而throw是在方法体中实际执行抛出异常的<br />动作。<br />如果你在方法中throw一个异常，却没有在方法声明中声明之，编译器会报错。<br />注意Error和RuntimeException的子类是例外，无需特别声明。</p>
		<p>Q4.8  什么是异常？<br />答：异常最早在Ada语言中引入，用于在程序中动态处理错误并恢复。<br />你可以在方法中拦截底层异常并处理之，也可以抛给更高层的模块去处理。<br />你也可以抛出自己的异常指示发生了某些不正常情况。常见的拦截处理代码如下：<br />try<br />{<br />......//以下是可能发生异常的代码<br />        ...... //异常被你或低层API抛出，执行流程中断并转向拦截代码。<br />        ...... <br />}<br />catch(Exception1 e) //如果Exception1是Exception2的子类并要做特别处理，应排在前<br />面<br />{<br />  //发生Exception1时被该段拦截<br />}<br />catch(Exception2 e)<br />{<br />  //发生Exception2时被该段拦截<br />}<br />finally //这是可选的<br />{<br />   //无论异常是否发生，均执行此段代码<br />   //即使在catch段中又向外抛出了新的exception，finally段也会得到执行。<br />}</p>
		<p>Q4.9  final和finally有什么不同？<br />答：final请见Q4.2。finally用于异常机制，参见Q4.8。</p>
		<p>五、 面向对象篇</p>
		<p>Q5.1  extends和implements有什么不同？<br />答：对于class而言，extends用于（单）继承一个类（class），而implements用于实现一个接口（interf<br />ace）。<br />interface的引入是为了部分地提供多继承的功能。<br />在interface中只需声明方法头，而将方法体留给实现的class来做。<br />这些实现的class的实例完全可以当作interface的实例来对待。<br />在interface之间也可以声明为extends（多继承）的关系。<br />注意一个interface可以extends多个其他interface。</p>
		<p>Q5.2  java怎么实现多继承？<br />答：java不支持显式的多继承。<br />因为在显式多继承的语言例如c++中，会出现子类被迫声明祖先虚基类构造函数的问题，</p>
		<p>而这是违反面向对象的封装性原则的。<br />java提供了interface和implements关键字来部分地实现多继承。参见Q5.1。</p>
		<p>Q5.3 abstract是什么？<br />答：被声明为abstract的方法无需给出方法体，留给子类来实现。<br />而如果一个类中有abstract方法，那么这个类也必须声明为abstract。<br />被声明为abstract的类无法实例化，尽管它可以定义构造方法供子类使用。</p>
		<p>Q5.4 public,protected,private有什么不同？<br />答：这些关键字用于声明类和成员的可见性。<br />public成员可以被任何类访问，<br />protected成员限于自己和子类访问，<br />private成员限于自己访问。<br />Java还提供了第四种的默认可见性，一般称为package private，当没有任何public,protected,private修饰符时，成员<br />是同一包内可见。<br />类可以用public或默认来修饰。<br /></p>
		<div>
				<p>Q5.5 Override和Overload有什么不同？<br />答：Override是指父类和子类之间方法的继承关系，这些方法有着相同的名称和参数类型<br />。<br />Overload是指同一个类中不同方法（可以在子类也可以在父类中定义）间的关系，<br />这些方法有着相同的名称和不同的参数类型。</p>
				<p>
						<br />Q5.6 我继承了一个方法，但现在我想调用在父类中定义的方法。<br />答：用super.xxx()可以在子类中调用父类方法。</p>
				<p>Q5.7 我想在子类的构造方法中调用父类的构造方法，该怎么办？<br />答：在子类构造方法的第一行调用super(...)即可。</p>
				<p>Q5.8 我在同一个类中定义了好几个构造方法并且想在一个构造方法中调用另一个。<br />答：在构造方法第一行调用this(...)。</p>
				<p>Q5.9 我没有定义构造方法会怎么样？<br />答：自动获得一个无参数的构造方法。</p>
				<p>Q5.10 我调用无参数的构造方法失败了。<br />答：如果你至少定义了一个构造方法，就不再有自动提供的无参数的构造方法了。<br />你需要另外显式定义一个无参数的构造方法。<br />另外一种可能是你的构造方法或者类不是public的，参见Q5.4了解java中的可见性。</p>
				<p>Q5.11 我该怎么定义类似于C++中的析构方法（destructor）？<br />答：提供一个void finalize()方法。在Garbarge Collector回收该对象时会调用该方法。</p>
				<p>注意实际上你很难判断一个对象会在什么时候被回收。作者从未感到需要用到该方法。</p>
				<p>
						<br />Q5.12 我想将一个父类对象转换成一个子类对象该怎么做？<br />答：强制类型转换。如<br />public void meth(A a)<br />{<br />B b = (B)a;<br />}<br />如果a实际上并不是B的实例，会抛出ClassCastException。所以请确保a确实是B的实例。</p>
				<p>
						<br />Q5.13 其实我不确定a是不是B的实例，能不能分情况处理？<br />答：可以使用instanceof操作符。例如<br />if( a instanceof B )<br />{<br />B b = (B)a;<br />}<br />else<br />{<br />...<br />}</p>
				<p>Q5.14 我在方法里修改了一个对象的值，但是退出方法后我发现这个对象的值没变！<br />答：很可能你把传入参数重赋了一个新对象，例如下列代码就会造成这种错误：<br />public void fun1(A a) //a是局部参数，指向了一个外在对象。<br />{<br />a = new A(); //a指向了一个新对象，和外在对象脱钩了。如果你要让a作为传出变量，<br />不要写这一句。<br />        a.setAttr(attr);//修改了新对象的值，外在对象没有被修改。<br />}<br />基本类型也会出现这种情况。例如：<br />public void fun2(int a)<br />{<br />a = 10;//只作用于本方法，外面的变量不会变化。<br />}</p>
				<p>六、java.util篇</p>
				<p>Q6.1 java能动态分配数组吗？<br />答：可以。例如int n = 3; Language[] myLanguages = new Language[n];</p>
				<p>Q6.2 我怎么知道数组的长度？<br />答：用length属性。如上例中的  myLanguages.length 就为 3。</p>
				<p>Q6.3 我还想让数组的长度能自动改变，能够增加/删除元素。<br />答：用顺序表--java.util.List接口。<br />你可以选择用ArrayList或是LinkedList，前者是数组实现，后者是链表实现。<br />例如：  List list = new ArrayList(); 或是 List list = new LinkedList();  。</p>
				<p>Q    什么是链表？为什么要有ArrayList和LinkedList两种List？<br />答：请补习数据结构。</p>
				<p>Q6.5 我想用队列/栈。<br />答：用java.util.LinkedList。</p>
				<p>Q6.6 我希望不要有重复的元素。<br />答：用集合--java.util.Set接口。例如：Set set = new HashSet()。</p>
				<p>Q6.7 我想遍历集合/Map。<br />答：用java.util.Iterator。参见API。</p>
				<p>Q6.8 我还要能够排序。<br />答：用java.util.TreeSet。例如：Set set = new TreeSet()。放进去的元素会自动排序<br />。<br />你需要为元素实现Comparable接口，还可能需要提供equals()方法，compareTo()方法，h<br />ashCode()方法。</p>
				<p>Q6.9 但是我想给数组排序。<br />答：java.util.Arrays类包含了sort等实用方法。</p>
				<p>Q6.10 我想按不同方式排序。<br />答：为每种方式定义一个实现了接口Comparator的排序类并和Arrays或TreeSet综合运用。</p>
				<p>
						<br />Q6.11 Map有什么用？<br />答：存储key-value的关键字-值对，你可以通过关键字来快速存取相应的值。</p>
				<p>Q6.12 set方法没问题，但是get方法返回的是Object。<br />答：强制类型转换成你需要的类型。参见Q5.12。</p>
				<p>Q6.13 ArrayList和Vector有什么不同？HashMap和Hashtable有什么不同？<br />答：ArrayList和HashMap是多线程不安全的，在多个线程中访问同一个ArrayList对象可能<br />会引起冲突并导致错误。<br />而Vector和Hashtable是多线程安全的，即使在多个线程中同时访问同一个Vector对象也不<br />会引起差错。<br />看起来我们更应该使用Vector和Hashtable，但是实际上Vector和Hashtable的性能太差，<br />所以如果你不在多线程中使用的话，还是应该用ArrayList和HashMap。</p>
				<p>Q6.14 我要获得一个随机数。<br />答：使用java.util.Random类。</p>
				<p>Q6.15 我比较两个String总是false，但是它们明明都是"abc" ！<br />答：比较String一定要使用equals或equalsIgnoreCase方法，不要使用 == ！<br />==比较的是两个引用（变量）是否指向了同一个对象，而不是比较其内容。</p>
				<p>Q6.16 我想修改一个String但是在String类中没找到编辑方法。<br />答：使用StringBuffer类。<br />String str = "......."; //待处理的字符串<br />StringBuffer buffer = new StringBuffer(str); //使用该字符串初始化一个StringBuf<br />fer<br />buffer.append("..."); //调用StringBuffer的相关API来编辑字符串<br />String str2 = buffer.toString(); //获得编辑后的字符串。<br />另外，如果你需要将多个字符串连接起来，请尽量避免使用+号直接连接，而是使用Strin<br />gBuffer.append()方法。</p>
				<p>Q6.17 我想处理日期/时间。<br />答：使用java.util.Date类。你可以使用java.text.SimpleDateFormat类来在String和Da<br />te间互相转换。<br />SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //规<br />定日期格式<br />Date date = formatter.parse("2003-07-26 18:30:35"); //将符合格式的String转换为<br />Date<br />String s = formatter.format(date); //将Date转换为符合格式的String<br />关于定义日期格式的详细信息请参见API。<br /> </p>
				<p>J2EE FAQ <br /> <br />目录：</p>
				<p>一、准备篇<br />Q1.1   什么是J2EE？它和普通的Java有什么不同？<br />Q1.2   J2EE好学吗？<br />Q1.3   J2EE有什么用？<br />Q1.4   学J2EE有前途吗？<br />Q1.5   据说J2EE的性能不如.NET好，是真的吗？<br />Q1.6   听你说了这么多，我想学着玩玩。<br />Q1.7   学习J2EE该怎么开始？<br />Q1.8   我下了一个J2EE服务器但是不会配置。<br />Q1.9   我发现你没有提到Tomcat。</p>
				<p>二、 Servlet/JSP篇<br />Q2.1   什么是Servlet？<br />Q2.2   我怎么获得Http请求里的参数？<br />Q2.3   我怎么返回结果？<br />Q2.4   sendRedirect()和forward()有什么不同？<br />Q2.5   我写了一个Servlet程序，怎么运行它？<br />Q2.6   EAR和WAR有什么不同？<br />Q2.7   EAR格式是怎样的？<br />Q2.8   WAR格式是怎样的？<br />Q2.9   我的普通HTML文件/JSP文件应当放在哪里？<br />Q2.10  我访问不到servlet，甚至连HTML文件都访问不到！<br />Q2.11  我能访问HTML但是访问不到servlet。<br />Q2.12  什么是JSP？它和Servlet有什么区别？<br />Q2.13  我的JSP显示的汉字是乱码。<br />Q2.14  为什么使用gb18030而不是gb2312？<br />Q2.15  在JSP里面怎么引用Java Bean。<br />Q2.16  我想在servlet间传递数据。<br />Q2.17  怎么调用cookie？<br />Q2.18  怎么在JSP里面实现文件下载？<br />Q2.19  怎么实现文件上传？<br />Q2.20  我想让页面自动刷新，比如聊天室。<br />Q2.21  我想让用户登录以后才能访问页面。<br />Q2.22  我想要能注册用户。<br />Q2.23  怎么在JSP中访问数据库？<br />Q2.24  什么是JSTL？</p>
				<p>一、准备篇</p>
				<p>Q1.1  什么是J2EE？它和普通的Java有什么不同？<br />答：J2EE全称为Java2 Platform, Enterprise Edition。<br />“J2EE平台本质上是一个分布式的服务器应用程序设计环境——一个Java环境，它提供了<br />：<br />·宿主应用的一个运行基础框架环境。<br />·一套用来创建应用的Java扩展API。”</p>
				<p>Q1.2  J2EE好学吗？<br />答：J2EE是很多技术的集合体，并且还在成长中。<br />你会遇到很多专有名词：比如(X)HTML，Servlet/JSP，JDBC，JMS，JNDI，EJB，XML，Web<br />Service……。<br />尤其是XML和Web Service正在快速成长。幸运的是，你不需要等到学会所有技术后再开始<br />编程。<br />大体上J2EE可以分成3个主要应用方式：Servlet/JSP，EJB，Web Service 和一些支撑技术<br />例如JDBC和JNDI。<br />你可以一个一个的学。</p>
				<p>Q1.3 J2EE有什么用？<br />答：用来建设大型的分布式企业级应用程序。或者用更时髦的名词说就是“电子商务”应<br />用程序。<br />这些企业可能大到拥有中心数据库服务器，Web服务器集群和遍布全国的办公终端，也可能<br />小到只不过想做一个网站。但是你肯定听过“杀鸡焉用牛刀”的古训。</p>
				<p>Q1.4 学J2EE有前途吗？<br />答：在这一市场目前只有一种技术可以和J2EE竞争，那就是Microsoft的.NET。<br />相对来说.NET要“新”一些而J2EE要“老”一些。这也意味着.NET更易用一点而J2EE更成<br />熟一点。<br />但是.NET只能用于Windows平台（Microsoft声称要开发C#在Linux上的虚拟机但是尚未兑现<br />该诺言）。<br />在过去几年，.NET的市场份额并不理想。不过Microsoft还有Longhorn这一杀手锏，鹿死谁<br />手还很难说。</p>
				<p>Q1.5 据说J2EE的性能不如.NET好，是真的吗？<br />答：在Sun公司提供的样例程序Pet Store上，Microsoft声称不如相同的.NET程序好。<br />而Sun公司反驳说这一程序不能真正体现J2EE的性能，并且指责Microsoft在数据库上做了<br />优化。<br />作者没有学习过.NET因而不能妄下断言。<br />无论如何，大型分布式程序中的性能瓶颈通常首先来自于错误的设计。</p>
				<p>Q1.6 听你说了这么多，我想学着玩玩。<br />答：除非你想靠它当饭吃或者作为技术储备，否则请不要浪费你的时间。<br />Flash要好玩得多。计算机游戏就更加好玩了。</p>
				<p>Q1.7 学习J2EE该怎么开始？<br />答：首先，下载一个免费的J2EE服务器。其次，去java.sun.com下载J2EE的API。第三，找<br />一本好的参考书。最后，找一个顺手的IDE。<br />J2EE服务器。你可以用Sun的J2EE SDK（免费），或者Weblogic（性能最好，但是太大，而<br />且作者不推荐盗版行为），<br />或者JBoss（免费，就是文档太少），或者JRun（开发版免费，作者用这个）。<br />参考书作者感觉Wrox的《J2EE服务器端高级编程》不错，但是太老（作者手头的是2001年<br />中文版）。<br />（似乎很多人不喜欢这本书......所以你得自己判断它是否适合你。）<br />你还需要去下载一些最新的技术资料（当然肯定是英文的）。<br />IDE如果你的机器配置够好（内存至少512M以上，256M或以下请勿考虑），可以用IBM的WS<br />AD，不然就继续用Eclipse或者其他。<br />你也可以经常去水木清华的Java版逛逛，但是在发贴前先看看精华区里有没有你要的答案<br />。</p>
				<p>Q1.8 我下了一个J2EE服务器但是不会配置。<br />答：请认真阅读随机指导文档，不同的服务器的配置都不一样，作者爱莫能助。</p>
				<p>Q1.9 我发现你没有提到Tomcat。<br />答：Tomcat只是一个Web服务器，更准确地说主要只是一个Web Container。<br />如果你想要学习EJB的话，Tomcat无法满足你的需要。</p>
				<p>二、 Servlet/JSP篇</p>
				<p>Q2.1 什么是Servlet？<br />答：一个Servlet是一个Java类。它处理Http(s)请求并作出响应，包括返回一个HTML页面<br />或转交给其他URL处理。<br />Servlet必须运行在一个Web Container例如Tomcat中。<br />Servlet必须是javax.servlet.http.HttpServlet的子类，<br />你可以继承doGet()或者doPost()方法，两者分别对应于Http(s)中的Get请求和Post请求。</p>
				<p>
						<br />Q2.2 我怎么获得Http请求里的参数？<br />答：HttpRequest的getParameter()方法。例如：String paramValue = request.getPara<br />meter("paramName");</p>
				<p>Q2.3 我怎么返回结果？<br />答：你可以利用相关API打开一个输出流，并向流中直接写入一个HTML页面。<br />但是作者完全不赞成这样做。一方面这样做会很罗嗦。<br />另一方面从Model-View-Controller模式（在《J2EE核心模式》中被归为Front Controlle<br />r模式）的观点来看，<br />你应当提供一些HTML或者JSP作为视图（view），而Servlet则根据请求参数决定转到哪一<br />个视图。<br />你可以利用response.sendRedirect(...)方法或request.getDispatcher(...).forward()<br />方法来实现。</p>
				<p>Q2.4 sendRedirect()和forward()有什么不同？<br />答：sendRedirect()是向浏览器发送一个redirect通知，浏览器向新的URL发送一个新的请<br />求。<br />而forward是在服务器端直接将请求转到新的URL，对于浏览器是透明的。<br />换而言之，sendRedirect()应当将共享数据放在session中，forward应当将共享数据放在<br />request中（当然你也可以放在session中，但放在request中可以有效减小session中的数<br />据量，从而改善性能）。<br />前者浏览器的地址栏显示的是新的URL，后者浏览器的地址栏显示的是Servlet的URL。<br />因而当刷新目标URL时，两者会造成一些差别。</p>
				<p>Q2.5 我写了一个Servlet程序，怎么运行它？<br />答：开发J2EE程序有一个部署（deploy）的概念，实际上是开发——部署——运行的三部<br />曲。<br />大多数服务器支持Hot deploy。你只需要在相应的Application目录（具体路径依赖于服务<br />器）下面<br />建立一个符合WAR或EAR格式（参见Q2.7，Q2.8）的目录，启动服务器，就可以通过浏览器<br />访问了。<br />特别的，你的Servlet的class文件应当放在/WEB-INF/classes目录中。<br />注意J2EE SDK不支持Hot deploy，你需要通过它的deploy tool来部署。<br />Tomcat只支持WAR格式。</p>
				<p>Q2.6 EAR和WAR有什么不同？<br />答：EAR是一个完整的J2EE应用程序，包括Web部分和EJB部分。<br />WAR只是其中的Web部分。</p>
				<p>Q2.7 EAR格式是怎样的？<br />答：一个EAR可以包含任意多个WAR或EJB JAR，并且包含一个META-INF的目录。<br />在/META-INF中包含了一个application.xml，其中描述了这个EAR包含哪些模块，以及安全<br />性配置。<br />细节请看参考书。</p>
				<p>Q2.8 WAR格式是怎样的？<br />答：一个WAR包含一个WEB-INF的目录，这个目录下包含classes目录，lib目录和web.xml。</p>
				<p>/WEB-INF/classes存放按package组织的class文件，/WEB-INF/lib目录存放jar文件，<br />web.xml描述了很多东西，请读参考书。</p>
				<p>Q2.9 我的普通HTML文件/JSP文件应当放在哪里？<br />答：放在除了/WEB-INF以外的其他地方。</p>
				<p>感谢antegg网友对于安全性的提醒：<br />如果你想直接用<a href="http://url/***.jsp">http://url/***.jsp</a>的方式来访问，就要像上面说得那样放。<br />但是这样的做法是不安全的，安全的做法是把所有的JSP页面放在/WEB-INF目录下面，并且</p>
				<p>通过WEB-CONTAINER来访问。</p>
				<p>作者意见:<br />我更喜欢用filter来做安全性检查。<br />在MVC模式中，JSP只是一个视图而已，一般无需特别担忧安全性。和普通的html放在一起<br />也利于维护。</p>
				<p>Q2.10 我访问不到servlet，甚至连HTML文件都访问不到！<br />答：<br />第一你没启动服务器。<br />第二你敲错了端口。<br />第三你没有正确配置context-path。<br />第四你的服务器不支持auto reload或者你关闭了这一选项，你得重启服务器或重新部署W<br />AR。<br />第五确认你没有把HTML放在/WEB-INF目录下，那是访问不到的。</p>
				<p>Q2.11 我能访问HTML但是访问不到servlet。<br />答：请检查你的web.xml文件。确保你正确定义了&lt;servlet&gt;和&lt;servlet-mapping&gt;元素。</p>
				<p>前者标识了一个servlet，后者将一个相对于context-path的URL映射到一个servlet。<br />在Tomcat中你可以通过/context-path/servlet/package/servletname的形式访问servlet<br />，<br />但是这只是Tomcat的便捷访问方式，并不是正式规范。<br />细节请看参考书。</p>
				<p>Q2.12  什么是JSP？它和Servlet有什么区别？<br />答：你可以将JSP当做一个可扩充的HTML来对待。<br />虽然在本质上JSP文件会被服务器自动翻译为相应的Servlet来执行。<br />可以说Servlet是面向Java程序员而JSP是面向HTML程序员的，除此之外两者功能完全等价<br />。</p>
				<p>Q2.13  我的JSP显示的汉字是乱码。<br />答：在你的JSP开头加上一行 &lt;%@ page contentType="text/html; charset=gb18030"%&gt;</p>
				<p>如果你已经声明了page我想你知道该怎么修改。</p>
				<p>Q2.14  为什么使用gb18030而不是gb2312？<br />答：gb18030是继gb2312之后的下一代汉字编码标准，最终将过渡到Unicode。</p>
				<p>Q2.15  在JSP里面怎么引用Java Bean。<br />答：首先，确认你要引用的类在/WEB-INF/classes下或在/WEB-INF/lib的某个jar内。<br />其次，在JSP里加一行 &lt;jsp:useBean id="..." scope="..." class="..."/&gt;<br />具体解释请看参考书。</p>
				<p>Q2.16  我想在servlet间传递数据。<br />答：利用session。在Servlet/JSP中，你可以在4个地方保存数据。<br />1) page，本页面。<br />2) session，用来存放客户相关的信息，比如购物车，对应接口为javax.servlet.http.H<br />ttpSession。<br />session机制实际上是cookie和URL Rewriting的抽象，服务器会自动使用cookie或URL Re<br />writing来实现。<br />3) request，可以在forward()时传递信息，对应接口为javax.servlet.http.HttpReques<br />t。<br />4) application，或称context，存放全局信息，对应接口为javax.servlet.ServletCont<br />ext。</p>
				<p>Q2.17  怎么调用cookie？<br />答：作者建议使用session，你总是会遇到某些禁用cookie的用户。这时session会自动使<br />用URL重写来实现。</p>
				<p>Q2.18  怎么在JSP里面实现文件下载？<br />答：实际上这是一个HTML的问题。答案是一个超链接&lt;a&gt;。</p>
				<p>Q2.19  怎么实现文件上传？<br />答：客户端是HTML问题，在form中设置method为post，enctype为multi-part/form-data，<br />加一个&lt;input type="file"&gt;。<br />而在接收的servlet中只是一个I/O问题，你可以使用jakarta的file-upload库。</p>
				<p>Q2.20  我想让页面自动刷新，比如聊天室。<br />答：这是一个HTML问题，在&lt;head&gt;部分中加一条&lt;meta http-equiv="refresh" content="<br />5" url="..."&gt;。<br />这是所谓的Client-pull，客户端刷新技术。<br />相对的还有Server-push，服务器端刷新技术，但是这一技术由于要占用服务器端资源而会<br />在大量访问时出现瓶颈现象，参见<a href="http://216.239.33.104/search?q=cache:autUfoakirY">http://216.239.33.104/search?q=cache:autUfoakirY</a><br />J:www.kfunigraz.ac.at/edvndwww/books/books/javaenterprise/servlet/ch06_03.htm+<br />server-push+servlet&amp;hl=zh-CN&amp;ie=UTF-8</p>
				<p>Q2.21  我想让用户登录以后才能访问页面。<br />答：使用声明式安全措施。<br />你只需要在web.xml中定义安全角色（Role），并定义受保护的URL集合只能由特定Role访<br />问。<br />大多数服务器支持基于数据库的用户映射，你只要在相应数据库中建立两张表并配置服务<br />器就可以了。<br />注意J2EE SDK不支持基于数据库的用户映射。<br />细节请看参考书和服务器文档。<br />不过在商业环境中，J2EE所提供的声明式安全措施仍然偏弱。一般商业程序会使用数据库<br />存储user-role-privilege模型来达到安全性要求，细节请询问你的构架设计师。</p>
				<p>Q2.22  我想要能注册用户。<br />答：参看Q2.21。在接受注册请求的Servlet中执行写入数据库操作即可。</p>
				<p>Q2.23  怎么在JSP中访问数据库？<br />答：标准做法是使用DAO模式，定义一个Java bean来访问数据库并在JSP中使用。<br />然而，当你的数据库模式很简单时，你可以使用JSTL中的&lt;sql:query&gt;标签来快速访问。</p>
				<p>在一般的J2EE项目中，JSP处于表示层（展现层），需要先后通过业务层和集成层才会访问<br />到数据库，所以这个问题确实只会在很小的程序中才会遇到。</p>
				<p>Q2.24  什么是JSTL？<br />答：JSTL是Jsp Standard Tag Library的缩写。这是一组通用标签并将成为JSP 2.0的一部<br />分。<br />其中包含赋值&lt;c:set&gt;，分支&lt;c:if&gt;，循环&lt;c:forEach&gt;，查询数据库&lt;sql:query&gt;，更新数<br />据库&lt;sql:update&gt;<br />等。目前你需要像添加自定义标签库一样来添加JSTL，但是可以预计JSP 2.0会将JSTL作为<br />组成部分。<br />标签库可以在<a href="http://jakarta.apache.org/">http://jakarta.apache.org</a>下载。注意JSTL需要在支持JSP 1.2或更高版本<br />的容器下运行。<br />帮助文件可以阅读sun的JSTL正式规范。</p>
		</div>
<img src ="http://www.blogjava.net/renyangok/aggbug/87164.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:12 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87164.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java对象序列化问题</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87166.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:12:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87166.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87166.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87166.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87166.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87166.html</trackback:ping><description><![CDATA[
		<div>　　serialVersionUID 用来表明类的不同版本间的兼容性.如果你修改了此类, 要修改此值. 否则以前用老版本的类序列化的类恢复时会出错.<br />　　可以利用JDK的bin目录下的serialver.exe工具产生这个serialVersionUID<br />　　对于Test.class,执行命令： serialver Test<br />　　为了在反序列化时，确保类版本的兼容性，最好在每个要序列化的类中加入private static final long serialVersionUID这个属性，具体数值自己定义。这样，即使某个类在与之对应的对象已经序列化出去后做了修改，该对象依然可以被正确反序列化。否则，如果不显示定义该属性，这个属性值将由JVM根据类的相关信息计算，而修改后的类的计算结果与修改前的类的计算结果往往不同，从而造成对象的反序列化因为类版本不兼容而失败。<br />　　不显示定义这个属性值的另一个坏处是，不利于程序在不同的JVM之间的移植。因为不同的编译器实现的该属性值的计算策略可能不同，从而造成虽然类没有改变，但是因为JVM不同，依然会有因类版本不兼容而无法正确反序列化的现象出现。<br />　　因为我做的系统不太会经常需要序列化类，所以为了去掉这些警告，做如下设置：<br />Window－Preferences－Java，如图所示，将serializable class without serialVersionUID的设置由warning改为Ignore。然后Eclipse会重新编译程序，那些警告信息也就会消失了。<br />　　小结：如果我们开发大量需要序列化的类的时候，我们最好还是还原为原来的设置。这样可以保证系统的性能和健壮。</div>
		<div>-------------------------------------------------------------------</div>
		<div>java对象序列化问题研究（转自<a href="http://www.54bk.com/user1/2064/archives/2005/1864.html">http://www.54bk.com/user1/2064/archives/2005/1864.html</a>）<br />    序列化的过程就是对象写入字节流和从字节流中读取对象。将对象状态转换成字节流之后，可以用java.io包中的各种字节流类将其保存到文件中，管道到另一线程中或通过网络连接将对象数据发送到另一主机。对象序列化功能非常简单、强大，在RMI、Socket、JMS、EJB都有应用。对象序列化问题在网络编程中并不是最激动人心的课题，但却相当重要，具有许多实用意义。<br />一：对象序列化可以实现分布式对象。主要应用例如：RMI要利用对象序列化运行远程主机上的服务，就像在本地机上运行对象时一样。<br />二：java对象序列化不仅保留一个对象的数据，而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中，可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的“深复制”，即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。<br />  从上面的叙述中，我们知道了对象序列化是java编程中的必备武器，那么让我们从基础开始，好好学习一下它的机制和用法。</div>
		<div>    java序列化比较简单，通常不需要编写保存和恢复对象状态的定制代码。实现java.io.Serializable接口的类对象可以转换成字节流或从字节流恢复，不需要在类中增加任何代码。只有极少数情况下才需要定制代码保存或恢复对象状态。这里要注意：不是每个类都可序列化，有些类是不能序列化的，例如涉及线程的类与特定JVM有非常复杂的关系。</div>
		<div>序列化机制：</div>
		<div>序列化分为两大部分：序列化和反序列化。序列化是这个过程的第一部分，将数据分解成字节流，以便存储在文件中或在网络上传输。反序列化就是打开字节流并重构对象。对象序列化不仅要将基本数据类型转换成字节表示，有时还要恢复数据。恢复数据要求有恢复数据的对象实例。ObjectOutputStream中的序列化过程与字节流连接，包括对象类型和版本信息。反序列化时，JVM用头信息生成对象实例，然后将对象字节流中的数据复制到对象数据成员中。下面我们分两大部分来阐述：</div>
		<div>
				<br />处理对象流：<br />（序列化过程和反序列化过程）</div>
		<div>  java.io包有两个序列化对象的类。ObjectOutputStream负责将对象写入字节流，ObjectInputStream从字节流重构对象。<br />    我们先了解ObjectOutputStream类吧。ObjectOutputStream类扩展DataOutput接口。<br />writeObject()方法是最重要的方法，用于对象序列化。如果对象包含其他对象的引用，则writeObject()方法递归序列化这些对象。每个ObjectOutputStream维护序列化的对象引用表，防止发送同一对象的多个拷贝。（这点很重要）由于writeObject()可以序列化整组交叉引用的对象，因此同一ObjectOutputStream实例可能不小心被请求序列化同一对象。这时，进行反引用序列化，而不是再次写入对象字节流。<br />下面，让我们从例子中来了解ObjectOutputStream这个类吧。</div>
		<div>// 序列化 today's date 到一个文件中. <br />    FileOutputStream f = new FileOutputStream("tmp"); <br />    ObjectOutputStream s = new ObjectOutputStream(f); <br />    s.writeObject("Today"); <br />    s.writeObject(new Date()); <br />    s.flush(); </div>
		<div>   现在，让我们来了解ObjectInputStream这个类。它与ObjectOutputStream相似。它扩展DataInput接口。ObjectInputStream中的方法镜像DataInputStream中读取Java基本数据类型的公开方法。readObject()方法从字节流中反序列化对象。每次调用readObject()方法都返回流中下一个Object。对象字节流并不传输类的字节码，而是包括类名及其签名。readObject()收到对象时，JVM装入头中指定的类。如果找不到这个类，则readObject()抛出ClassNotFoundException,如果需要传输对象数据和字节码，则可以用RMI框架。ObjectInputStream的其余方法用于定制反序列化过程。<br />例子如下：</div>
		<div>//从文件中反序列化 string 对象和 date 对象 <br />    FileInputStream in = new FileInputStream("tmp"); <br />    ObjectInputStream s = new ObjectInputStream(in); <br />    String today = (String)s.readObject(); <br />    Date date = (Date)s.readObject(); </div>
		<div>
				<br />定制序列化过程:</div>
		<div>
				<br />序列化通常可以自动完成，但有时可能要对这个过程进行控制。java可以将类声明为serializable，但仍可手工控制声明为static或transient的数据成员。<br />例子：一个非常简单的序列化类。</div>
		<div>public class simpleSerializableClass implements Serializable{ <br />    String sToday="Today:"; <br />    transient Date dtToday=new Date(); <br />} </div>
		<div>序列化时，类的所有数据成员应可序列化除了声明为transient或static的成员。将变量声明为transient告诉JVM我们会负责将变元序列化。将数据成员声明为transient后，序列化过程就无法将其加进对象字节流中，没有从transient数据成员发送的数据。后面数据反序列化时，要重建数据成员（因为它是类定义的一部分），但不包含任何数据，因为这个数据成员不向流中写入任何数据。记住，对象流不序列化static或transient。我们的类要用writeObject()与readObject()方法以处理这些数据成员。使用writeObject()与readObject()方法时，还要注意按写入的顺序读取这些数据成员。<br />关于如何使用定制序列化的部分代码如下：</div>
		<div>//重写writeObject()方法以便处理transient的成员。 <br />public void writeObject(ObjectOutputStream outputStream) throws IOException{ <br />    outputStream.defaultWriteObject();//使定制的writeObject()方法可以 <br />                        利用自动序列化中内置的逻辑。 <br />    outputStream.writeObject(oSocket.getInetAddress()); <br />    outputStream.writeInt(oSocket.getPort()); <br />} <br />//重写readObject()方法以便接收transient的成员。 <br />private void readObject(ObjectInputStream inputStream) throws IOException,ClassNotFoundException{ <br />    inputStream.defaultReadObject();//defaultReadObject()补充自动序列化 <br />    InetAddress oAddress=(InetAddress)inputStream.readObject(); <br />    int iPort =inputStream.readInt(); <br />    oSocket = new Socket(oAddress,iPort); <br />    iID=getID(); <br />    dtToday =new Date(); <br />} </div>
		<div>
				<br />完全定制序列化过程:</div>
		<div>如果一个类要完全负责自己的序列化，则实现Externalizable接口而不是Serializable接口。Externalizable接口定义包括两个方法writeExternal()与readExternal()。利用这些方法可以控制对象数据成员如何写入字节流.类实现Externalizable时，头写入对象流中，然后类完全负责序列化和恢复数据成员，除了头以外，根本没有自动序列化。这里要注意了。声明类实现Externalizable接口会有重大的安全风险。writeExternal()与readExternal()方法声明为public，恶意类可以用这些方法读取和写入对象数据。如果对象包含敏感信息，则要格外小心。这包括使用安全套接或加密整个字节流。到此为至，我们学习了序列化的基础部分知识。关于序<br />列化的高级教程，以后再述。</div>
		<div>
				<br /> </div>
<img src ="http://www.blogjava.net/renyangok/aggbug/87166.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:12 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87166.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java编程三十条规则</title><link>http://www.blogjava.net/renyangok/archive/2006/12/12/87163.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 12 Dec 2006 03:11:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/12/87163.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/87163.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/12/87163.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/87163.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/87163.html</trackback:ping><description><![CDATA[
		<p>　  (1) 若在定义中出现了常数初始化字符，则大写static final基本类型标识符中的所有字母，单词之间用“_”连接。eg：private static final int MAX_LENGTH = 1000;Java包（Package）全都是小写字母，即便中间的单词亦是如此。</p>
		<p>　　(2) 为了常规用途而创建一个类时，请采取“经典形式”，并包含对下述元素的定义： (规范要求，如果两个对象进行equals比较时如果返回true,那么它们的hashcode要求返回相等的值。但hashcode一样时两个对象不==)<br />equals()<br />hashCode()<br />toString()<br />clone()(mplement Cloneable)<br />mplement Serializable</p>
		<p>　　(3) 对于自己创建的每一个类，都考虑置入一个main()，其中包含了用于测试那个类的代码。为使用一个项目中的类，我们没必要删除测试代码。若进行了任何形式的改动，可方便地返回测试。这些代码也可作为如何使用类的一个示例使用。main()方法在类定义的最底部。</p>
		<p>    (4)</p>
		<p>    (5) 设计一个类时，请设身处地为客户程序员考虑一下（类的使用方法应该是非常明确的）。然后，再设身处地为管理代码的人考虑一下（预计有可能进行哪些形式的修改，想想用什么方法可把它们变得更简单）。</p>
		<p>　　(6) 使类尽可能短小精悍，而且只解决一个特定的问题。下面是对类设计的一些建议：</p>
		<p>　　■一个复杂的开关语句：考虑采用“多形”机制</p>
		<p>　　■数量众多的方法涉及到类型差别极大的操作：考虑用几个类来分别实现</p>
		<p>　　■许多成员变量在特征上有很大的差别：考虑使用几个类 。</p>
		<p>　　(7) 让一切东西都尽可能地“私有”——private。可使库的某一部分“公共化”（一个方法、类或者一个字段等等），就永远不能把它拿出。若强行拿出，就可能破坏其他人现有的代码，使他们不得不重新编写和设计。若只公布自己必须公布的，就可放心大胆地改变其他任何东西。在多线程环境中，隐私是特别重要的一个因素——只有private字段才能在非同步使用的情况下受到保护。</p>
		<p>　　(8) 谨惕“巨大对象综合症”。对一些习惯于顺序编程思维、且初涉OOP领域的新手，往往喜欢先写一个顺序执行的程序，再把它嵌入一个或两个巨大的对象里。根据编程原理，对象表达的应该是应用程序的概念，而非应用程序本身。</p>
		<p>　　(9) 若不得已进行一些不太雅观的编程，至少应该把那些代码置于一个类的内部。</p>
		<p>　　(10) 任何时候只要发现类与类之间结合得非常紧密，就需要考虑是否采用内部类，从而改善编码及维护工作。</p>
		<p>　　(11) 尽可能细致地加上注释，并用javadoc注释文档语法生成自己的程序文档。</p>
		<p>　　(12) 避免使用“魔术数字”，这些数字很难与代码很好地配合。如以后需要修改它，无疑会成为一场噩梦，因为根本不知道“100”到底是指“数组大小”还是“其他全然不同的东西”。所以，我们应创建一个常数，并为其使用具有说服力的描述性名称，并在整个程序中都采用常数标识符。这样可使程序更易理解以及更易维护。</p>
		<p>　　(13) 涉及构建器和异常的时候，通常希望重新丢弃在构建器中捕获的任何异常——如果它造成了那个对象的创建失败。这样一来，调用者就不会以为那个对象已正确地创建，从而盲目地继续。</p>
		<p>　　(14) 当客户程序员用完对象以后，若你的类要求进行任何清除工作，可考虑将清除代码置于一个良好定义的方法里，采用类似于cleanup()这样的名字，明确表明自己的用途。除此以外，可在类内放置一个boolean（布尔）标记，指出对象是否已被清除。在类的finalize()方法里，请确定对象已被清除，并已丢弃了从RuntimeException继承的一个类（如果还没有的话），从而指出一个编程错误。在采取象这样的方案之前，请确定finalize()能够在自己的系统中工作（可能需要调用System.runFinalizersonExit(true)，从而确保这一行为）。</p>
		<p>　　(15) 在一个特定的作用域内，若一个对象必须清除（非由垃圾收集机制处理），请采用下述方法：初始化对象；若成功，则立即进入一个含有finally从句的try块，开始清除工作。</p>
		<p>　　(16) 若在初始化过程中需要覆盖（取消）finalize()，请记住调用super.finalize()（若Object属于我们的直接超类，则无此必要）。在对finalize()进行覆盖的过程中，对super.finalize()的调用应属于最后一个行动，而不应是第一个行动，这样可确保在需要基础类组件的时候它们依然有效。</p>
		<p>　　(17) 创建大小固定的对象集合时，请将它们传输至一个数组（若准备从一个方法里返回这个集合，更应如此操作）。这样一来，我们就可享受到数组在编译期进行类型检查的好处。此外，为使用它们，数组的接收者也许并不需要将对象“造型”到数组里。</p>
		<p>　　(18) 尽量使用interfaces，不要使用abstract类。若已知某样东西准备成为一个基础类，那么第一个选择应是将其变成一个interface（接口）。只有在不得不使用方法定义或者成员变量的时候，才需要将其变成一个abstract（抽象）类。接口主要描述了客户希望做什么事情，而一个类则致力于（或允许）具体的实施细节。</p>
		<p>　　(19) 在构建器内部，只进行那些将对象设为正确状态所需的工作。尽可能地避免调用其他方法，因为那些方法可能被其他人覆盖或取消，从而在构建过程中产生不可预知的结果（参见第7章的详细说明）。</p>
		<p>　　(20) 对象不应只是简单地容纳一些数据；它们的行为也应得到良好的定义。</p>
		<p>　　(21) 在现成类的基础上创建新类时，请首先选择“新建”或“创作”。只有自己的设计要求必须继承时，才应考虑这方面的问题。若在本来允许新建的场合使用了继承，则整个设计会变得没有必要地复杂。</p>
		<p>　　(22) 用继承及方法覆盖来表示行为间的差异，而用字段表示状态间的区别。一个非常极端的例子是通过对不同类的继承来表示颜色，这是绝对应该避免的：应直接使用一个“颜色”字段。</p>
		<p>　　(23) 为避免编程时遇到麻烦，请保证在自己类路径指到的任何地方，每个名字都仅对应一个类。否则，编译器可能先找到同名的另一个类，并报告出错消息。若怀疑自己碰到了类路径问题，请试试在类路径的每一个起点，搜索一下同名的.class文件。</p>
		<p>　　(24) 在Java 1.1 AWT中使用事件“适配器”时，特别容易碰到一个陷阱。若覆盖了某个适配器方法，同时拼写方法没有特别讲究，最后的结果就是新添加一个方法，而不是覆盖现成方法。然而，由于这样做是完全合法的，所以不会从编译器或运行期系统获得任何出错提示——只不过代码的工作就变得不正常了。</p>
		<p>　　(25) 用合理的设计方案消除“伪功能”。也就是说，假若只需要创建类的一个对象，就不要提前限制自己使用应用程序，并加上一条“只生成其中一个”注释。请考虑将其封装成一个“独生子”的形式。若在主程序里有大量散乱的代码，用于创建自己的对象，请考虑采纳一种创造性的方案，将些代码封装起来。</p>
		<p>　　(26) 警惕“分析瘫痪”。请记住，无论如何都要提前了解整个项目的状况，再去考察其中的细节。由于把握了全局，可快速认识自己未知的一些因素，防止在考察细节的时候陷入“死逻辑”中。</p>
		<p>　　(27) 警惕“过早优化”。首先让它运行起来，再考虑变得更快——但只有在自己必须这样做、而且经证实在某部分代码中的确存在一个性能瓶颈的时候，才应进行优化。除非用专门的工具分析瓶颈，否则很有可能是在浪费自己的时间。性能提升的隐含代价是自己的代码变得难于理解，而且难于维护。</p>
		<p>　　(28) 请记住，阅读代码的时间比写代码的时间多得多。思路清晰的设计可获得易于理解的程序，但注释、细致的解释以及一些示例往往具有不可估量的价值。无论对你自己，还是对后来的人，它们都是相当重要的。如对此仍有怀疑，那么请试想自己试图从联机Java文档里找出有用信息时碰到的挫折，这样或许能将你说服。</p>
		<p>　　(29) 如认为自己已进行了良好的分析、设计或者实施，那么请稍微更换一下思维角度。试试邀请一些外来人士——并不一定是专家，但可以是来自本公司其他部门的人。请他们用完全新鲜的眼光考察你的工作，看看是否能找出你一度熟视无睹的问题。采取这种方式，往往能在最适合修改的阶段找出一些关键性的问题，避免产品发行后再解决问题而造成的金钱及精力方面的损失。</p>
		<p>　　(30) 良好的设计能带来最大的回报。简言之，对于一个特定的问题，通常会花较长的时间才能找到一种最恰当的解决方案。但一旦找到了正确的方法，以后的工作就轻松多了，再也不用经历数小时、数天或者数月的痛苦挣扎。我们的努力工作会带来最大的回报（甚至无可估量）。而且由于自己倾注了大量心血，最终获得一个出色的设计方案，成功的快感也是令人心动的。坚持抵制草草完工的诱惑——那样做往往得不偿失</p>
		<p>    (31) JAVA 对枚举的支持不好，但是下面的代码是一种很有用的模板： <br />class Colour { <br />public static final Colour BLACK = new Colour(0, 0, 0); <br />public static final Colour RED = new Colour(0xFF, 0, 0); <br />public static final Colour GREEN = new Colour(0, 0xFF, 0); <br />public static final Colour BLUE = new Colour(0, 0, 0xFF); <br />public static final Colour WHITE = new Colour(0xFF, 0xFF, 0xFF); <br />} <br />这种技术实现了RED, GREEN, BLUE 等可以象其他语言的枚举类型一样使用的常量。 他们可以用 == 操作符来比较。 但是这样使用有一个缺陷：如果一个用户用这样的方法来创建颜色 BLACK new Colour(0,0,0) 那么这就是另外一个对象，==操作符就会产生错误。她的 equal() 方法仍然有效。由于这个原因，这个技术的缺陷最好注明在文档中，或者只在自己的包中使用。 <br /></p>
<img src ="http://www.blogjava.net/renyangok/aggbug/87163.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-12 11:11 <a href="http://www.blogjava.net/renyangok/archive/2006/12/12/87163.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>