<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>alfred's space</title>
    <description></description>
    <link>http://coffeebar.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>MethodInvokingTimerTaskFactoryBean 的使用</title>
        <author>alfred.w</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coffeebar.javaeye.com">alfred.w</a>&nbsp;
          链接：<a href="http://coffeebar.javaeye.com/blog/210698" style="color:red;">http://coffeebar.javaeye.com/blog/210698</a>&nbsp;
          发表时间: 2008年07月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>集成TimerTask 容易造成对业务代码的侵入，这种方式更符合spring的思想。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p class="MsoNormal" align="left" style="text-align: left;"><span style="font-size: x-small;">http://caterpillar.onlyfun.net/GossipCN/SpringGossip/MethodInvokingTimerTaskFactoryBean.html</span>
</p>
<p class="MsoNormal" align="left" style="text-align: left;"><span style="font-family: 宋体; font-size: x-small;">使 用<span lang="EN-US">Spring</span>
时，您并不一定要继承<span lang="EN-US">TimerTask</span>
来定义一个任务，<span lang="EN-US">Spring</span>
提供<span lang="EN-US">
org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean</span>
，
可以让您直接指定呼叫某个物件的方法，例如可以改写一下</span>
<span style="font-size: x-small;">
</span>
<span style="font-family: 宋体; font-size: x-small;"> <span lang="EN-US"><a href="http://caterpillar.onlyfun.net/GossipCN/SpringGossip/UseTimerTask.html"><span lang="EN-US" style="font-size: 10pt;"><span lang="EN-US">使用
TimerTask</span>
</span>
</a>
 </span>
</span>
<span style="font-family: 宋体; font-size: x-small;">中的<span lang="EN-US">DemoTask</span>
类别，这次不用继承<span lang="EN-US">TimerTask</span>
类别：</span>

</p>
<ul type="disc">
<li class="MsoNormal" style="text-align: left;"><span style="font-family: 宋体; font-size: x-small;">DemoTask.java</span>
</li>
</ul>
<pre name="code" class="java">package onlyfun.caterpillar;

public class DemoTask {
    public void execute() {
        System.out.println(&quot;Task is executed.&quot;);
    }
}</pre>
&nbsp;
<p class="MsoNormal" align="left" style="text-align: left;">&nbsp;</p>
<p class="MsoNormal" align="left" style="text-align: left;"><span style="font-family: 宋体; font-size: x-small;"><br />
</span>
<span style="font-family: 宋体; font-size: x-small;">接着只要在<span lang="EN-US">Bean</span>
定义档中使用<span lang="EN-US">MethodInvokingTimerTaskFactoryBean</span>
即可，例如：</span>

</p>
<ul type="disc">
<li class="MsoNormal" style="text-align: left;"><span style="font-family: 宋体; font-size: x-small;">beans-config.xml</span>
</li>
</ul>
<pre name="code" class="xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE beans PUBLIC &quot;-//SPRING/DTD BEAN/EN&quot;
  &quot;http://www.springframework.org/dtd/spring-beans.dtd&quot;&gt;

&lt;beans&gt;
    &lt;bean id=&quot;demoTask&quot;
           class=&quot;onlyfun.caterpillar.DemoTask&quot;/&gt;

    &lt;bean id=&quot;timerTaskBean&quot;
          class=&quot;org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean&quot;&gt;
        &lt;property name=&quot;targetObject&quot;&gt;
            &lt;ref bean=&quot;demoTask&quot;/&gt;
        &lt;/property&gt;
        &lt;property name=&quot;targetMethod&quot;&gt;
            &lt;value&gt;execute&lt;/value&gt;
        &lt;/property&gt;      
    &lt;/bean&gt;
       
    &lt;bean id=&quot;scheduledTimerTask&quot;
          class=&quot;org.springframework.scheduling.timer.ScheduledTimerTask&quot;&gt;
        &lt;property name=&quot;timerTask&quot;&gt;
            &lt;ref bean=&quot;timerTaskBean&quot;/&gt;
        &lt;/property&gt;
        &lt;property name=&quot;period&quot;&gt;
            &lt;value&gt;5000&lt;/value&gt;
        &lt;/property&gt;
         &lt;property name=&quot;delay&quot;&gt;
            &lt;value&gt;1000&lt;/value&gt;
        &lt;/property&gt;       
    &lt;/bean&gt;
   
    &lt;bean id=&quot;timerFactoryBean&quot;
          class=&quot;org.springframework.scheduling.timer.TimerFactoryBean&quot;&gt;
        &lt;property name=&quot;scheduledTimerTasks&quot;&gt;
            &lt;list&gt;
                &lt;ref bean=&quot;scheduledTimerTask&quot;/&gt;
            &lt;/list&gt;
        &lt;/property&gt;
    &lt;/bean&gt;
&lt;/beans&gt;</pre>
&nbsp;
<p class="MsoNormal" align="left" style="text-align: left;">&nbsp;</p>
<p class="MsoPlainText"><span style="font-size: x-small;"><span lang="EN-US" style="font-size: 10pt;"><br />
</span>
<span style="font-size: 10pt;">执行时可以直接使用 </span>
<span lang="EN-US" style="font-size: 12pt;"><a href="http://caterpillar.onlyfun.net/GossipCN/SpringGossip/UseTimerTask.html"><span lang="EN-US" style="font-size: 10pt;"><span lang="EN-US">使用
TimerTask</span>
</span>
</a>
 </span>
<span style="font-size: 10pt;">中的<span lang="EN-US">TimerTaskDemo</span>
类别，在底层，<span lang="EN-US">MethodInvokingTimerTaskFactoryBean</span>
会自动建立<span lang="EN-US">TimerTask</span>
的实例以呼叫目标物件上的指定方法。</span>
</span>

</p>
          <br/>
          <span style="color:red;">
            <a href="http://coffeebar.javaeye.com/blog/210698#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 02 Jul 2008 16:45:25 +0800</pubDate>
        <link>http://coffeebar.javaeye.com/blog/210698</link>
        <guid>http://coffeebar.javaeye.com/blog/210698</guid>
      </item>
      <item>
        <title>java5新特性</title>
        <author>alfred.w</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coffeebar.javaeye.com">alfred.w</a>&nbsp;
          链接：<a href="http://coffeebar.javaeye.com/blog/208498" style="color:red;">http://coffeebar.javaeye.com/blog/208498</a>&nbsp;
          发表时间: 2008年06月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>Java
5.0发布了，许多人都将开始使用这个JDK版本的一些新增特性。从增强的for循环到诸如泛型(generic)之类更复杂的特性，都将很快出现在您所
编写的代码中。我们刚刚完成了一个基于Java
5.0的大型任务，而本文就是要介绍我们使用这些新特性的体验。本文不是一篇入门性的文章，而是对这些特性以及它们所产生的影响的深入介绍，同时还给出了
一些在项目中更有效地使用这些特性的技巧。 
</p>
<p><strong>简介</strong>
<br />
　　在JDK 1.5的beta阶段，我们为BEA的Java IDE开发了一个Java
5编译器。因为我们实现了许多新特性，所以人们开始以新的方式利用它们；有些用法很聪明，而有些用法明显应该被列入禁用清单。编译器本身使用了新的语言特
性，所以我们也获得了使用这些特性维护代码的直接体验。本文将介绍其中的许多特性和使用它们的体验。 <br />
　　我们假定您已经熟悉了这些新特性，所以不再全面介绍每个特性，而是谈论一些有趣的、但很可能不太明显的内容和用法。这些技巧出自我们的实际体验，并大致按照语言特性进行了分类。 <br />
　　我们将从最简单的特性开始，逐步过渡到高级特性。泛型所包含的内容特别丰富，因此占了本文一半的篇幅。</p>
<p><strong>增强的for循环</strong>
<br />
　　为了迭代集合和数组，增强的for循环提供了一个简单、兼容的语法。有两点值得一提： </p>
<p>
<strong>Init表达式</strong>
<br />
　　在循环中，初始化表达式只计算一次。这意味着您通常可以移除一个变量声明。在这个例子中，我们必须创建一个整型数组来保存
computeNumbers()的结果，以防止每一次循环都重新计算该方法。您可以看到，下面的代码要比上面的代码整洁一些，并且没有泄露变量
numbers：</p>
<pre class="code">未增强的For：
int sum = 0;
Integer[] numbers = computeNumbers();
for (int i=0; i &lt; numbers.length ; i++)
    sum += numbers[i];
增强后的For： 
int sum = 0;

for ( int number: computeNumbers() )
    sum += number;</pre>
<p>
<strong>局限性</strong>
<br />
有时需要在迭代期间访问迭代器或下标，看起来增强的for循环应该允许该操作，但事实上不是这样，请看下面的例子： 
</p>
<pre class="code">for (int i=0; i &lt; numbers.length ; i++) {
    if (i != 0) System.out.print(&quot;,&quot;);
    System.out.print(numbers[i]);
}</pre>
<p>　　我们希望将数组中的值打印为一个用逗号分隔的清单。我们需要知道目前是否是第一项，以便确定是否应该打印逗号。使用增强的for循环是无法获知
这种信息的。我们需要自己保留一个下标或一个布尔值来指示是否经过了第一项。 　　这是另一个例子： </p>
<pre class="code">for (Iterator&lt;integer&gt; it = n.iterator() ; it.hasNext() ; )
    if (it.next() &lt; 0)
        it.remove();</pre>
<p>　　在此例中，我们想从整数集合中删除负数项。为此，需要对迭代器调用一个方法，但是当使用增强的for 循环时，迭代器对我们来说是看不到的。因此，我们只能使用Java 5之前版本的迭代方法。 
　　顺便说一下，这里需要注意的是，由于Iterator是泛型，所以其声明是Iterator&lt;Integer&gt;。许多人都忘记了这一点而使用了Iterator的原始格式。 </p>
<p>
<strong>注释</strong>
<br />
　　注释处理是一个很大的话题。因为本文只关注核心的语言特性，所以我们不打算涵盖它所有的可能形式和陷阱。
　　我们将讨论内置的注释（SuppressWarnings，Deprecated和Override）以及一般注释处理的局限性。 
</p>
<p><strong>Suppress Warnings</strong>
<br />
　　该注释关闭了类或方法级别的编译器警告。有时候您比编译器更清楚地知道，代码必须使用一个被否决的方法或执行一些无法静态确定是否类型安全的动作，而使用：</p>
<pre class="code">@SuppressWarnings(&quot;deprecation&quot;)
public static void selfDestruct() {
    Thread.currentThread().stop();
}</pre>
<p>　　这可能是内置注释最有用的地方。遗憾的是，1.5.0_04的javac不支持它。但是1.6支持它，并且Sun正在努力将其向后移植到1.5中。 <br />
  Eclipse 3.1中支持该注释，其他IDE也可能支持它。这允许您把代码彻底地从警告中解脱出来。如果在编译时出现警告，可以确定是您刚刚把它添加进来&mdash;&mdash;以帮助查看那些可能不安全的代码。随着泛型的添加，它使用起来将更趁手。 </p>
<p><strong>Deprecated</strong>
<br />
　　遗憾的是，Deprecated没那么有用。它本来旨在替换@deprecated
javadoc标签，但是由于它不包含任何字段，所以也就没有方法来建议deprecated类或方法的用户应该使用什么做为替代品。大多数用法都同时需
要javadoc标签和这个注释。</p>
<p> <strong>Override</strong>
<br />
　　Override表示，它所注释的方法应该重写超类中具有相同签名的方法： </p>
<pre class="code">@Override
public int hashCode() {
    ...
}</pre>
<p>　　看上面的例子，如果没有在hashCode中将&ldquo;C&rdquo;大写，在编译时不会出现错误，但是在运行时将无法像期望的那样调用该方法。通过添加Override标签，编译器会提示它是否真正地执行了重写。 <br />
  　　在超类发生改变的情况中，这也很有帮助。如果向该方法中添加一个新参数，而且方法本身也被重命名了，那么子类将突然不能编译，因为它不再重写超类的任何东西。 </p>
<p><strong>其它注释<br />
</strong>
　　注释在其他场景中非常有用。当不是直接修改行为而是增强行为时，特别是在添加样板代码的情况下，注释在诸如EJB和<a href="http://dev2dev.bea.com/pub/a/2004/10/Anil_WServices.html" target="_blank">Web services</a>
这样的框架中运行得非常好。 <br />
  注释不能用做预处理器。Sun的设计特别预防了完全因为注释而修改类的字节码。这样可以正确地理解该语言的成果，而且IDE之类的工具也可以执行深入的代码分析和重构之类的功能。 <br />
注释不是银弹。第一次遇到的时候，人们试图尝试各种技巧。请看下面这个从别人那里获得的建议： </p>
<pre class="code">public class Foo {
 
    @Property
    private int bar;
 
}</pre>
<p>　　其思想是为私有字段bar自动创建getter和setter方法。遗憾的是，这个想法有两个失败之处：1)它不能运行，2)它使代码难以阅读和处理。 
　　它是无法实现的，因为前面已经提到了，Sun特别阻止了对出现注释的类进行修改。 <br />
　　即使是可能的，它也不是一个好主意，因为它使代码可读性差。第一次看到这段代码的人会不知道该注释创建了方法。此外，如果将来您需要在这些方法内部执行一些操作，注释也是没用的。 
　　总之，不要试图用注释去做那些常规代码可以完成的事情。 </p>
<p><strong>枚举 </strong>
<br />
 　　enum非常像public static final
int声明，后者作为枚举值已经使用了很多年。对int所做的最大也是最明显的改进是类型安全&mdash;&mdash;您不能错误地用枚举的一种类型代替另一种类型，这一点和
int不同，所有的int对编译器来说都是一样的。除去极少数例外的情况，通常都应该用enum实例替换全部的枚举风格的int结构。 <br />
  　　枚举提供了一些附加的特性。EnumMap和EnumSet这两个实用类是专门为枚举优化的标准集合实现。如果知道集合只包含枚举类型，那么应该使用这些专门的集合来代替HashMap或HashSet。 <br />
　　大部分情况下，可以使用enum对代码中的所有public static final
int做插入替换。它们是可比的，并且可以静态导入，所以对它们的引用看起来是等同的，即使是对于内部类（或内部枚举类型）。注意，比较枚举类型的时候，
声明它们的指令表明了它们的顺序值。 <br />
  <br />
<strong>&ldquo;隐藏的&rdquo;静态方法 </strong>
<br />
  　　两个静态方法出现在所有枚举类型声明中。因为它们是枚举子类上的静态方法，而不是Enum本身的方法，所以它们在java.lang.Enum的javadoc中没有出现。 <br />
  　　第一个是values()，返回一个枚举类型所有可能值的数组。 <br />
  　　第二个是valueOf()，为提供的字符串返回一个枚举类型，该枚举类型必须精确地匹配源代码声明。 <br />
<strong>方法<br />
</strong>
　　关于枚举类型，我们最喜欢的一个方面是它可以有方法。过去您可能需要编写一些代码，对public
static final int进行转换，把它从数据库类型转换为JDBC
URL。而现在则可以让枚举类型本身带一个整理代码的方法。下面就是一个例子，包括DatabaseType枚举类型的抽象方法以及每个枚举实例中提供的
实现：</p>
<pre class="code">  public enum  DatabaseType {
  ORACLE {
  public String getJdbcUrl() {...}
  },
  MYSQL {
  public String getJdbcUrl() {...}
  };
  public abstract String getJdbcUrl();
  }</pre>
<p>
  　　现在枚举类型可以直接提供它的实用方法。例如：<br />
 
<br />
DatabaseType dbType =  ...;<br />
String jdbcURL =  dbType.getJdbcUrl();<br />
<br />
　　要获取URL，必须预先知道该实用方法在哪里。 
</p>
<p><br />
    <strong>可变参数(Vararg)</strong>
<br />
    　　正确地使用可变参数确实可以清理一些垃圾代码。典型的例子是一个带有可变的String参数个数的log方法：</p>
<pre class="code">    Log.log(String code)
    Log.log(String code,  String arg)
    Log.log(String code,  String arg1, String arg2)
    Log.log(String code,  String[] args)</pre>
<p>
  　　当讨论可变参数时，比较有趣的是，如果用新的可变参数替换前四个例子，将是兼容的： <br />
    Log.log(String code,  String... args)<br />
　　所有的可变参数都是源兼容的&mdash;&mdash;那就是说，如果重新编译log()方法的所有调用程序，可以直接替换全部的四个方法。然而，如果需要向后的二进制兼容
性，那么就需要舍去前三个方法。只有最后那个带一个字符串数组参数的方法等效于可变参数版本，因此可以被可变参数版本替换。 <br />
    <strong><br />
    类型强制转换 </strong>
<br />
    　　如果希望调用程序了解应该使用哪种类型的参数，那么应该避免用可变参数进行类型强制转换。看下面这个例子，第一项希望是String，第二项希望是Exception：</p>
<pre class="code">    Log.log(Object...  objects) {
    String message = (String)objects[0];
    if (objects.length &gt; 1) {
    Exception e = (Exception)objects[1];
    // Do something with the exception
    }
    }</pre>
<p>
    　　方法签名应该如下所示，相应的可变参数分别使用String和Exception声明： <br />
    <br />
    Log.log(String  message, Exception e, Object... objects) {...}<br />
    <br />
    　　不要使用可变参数破坏类型系统。需要强类型化时才可以使用它。对于这个规则，PrintStream.printf()是一个有趣的例外：它提供类型信息作为自己的第一个参数，以便稍后可以接受那些类型。 <br />
    <strong><br />
    协变返回 </strong>
<br />
　　协变返回的基本用法是用于在已知一个实现的返回类型比API更具体的时候避免进行类型强制转换。在下面这个例子中，有一个返回Animal对象的
Zoo接口。我们的实现返回一个AnimalImpl对象，但是在JDK 1.5之前，要返回一个Animal对象就必须声明。:</p>
<pre class="code">    public interface Zoo  {
    public Animal getAnimal();
    }
  public class ZooImpl  implements Zoo {
  public Animal getAnimal(){
  return new AnimalImpl();
  }
  }</pre>
<p>
  　　协变返回的使用替换了三个反模式： 
  
</p>
<ul>
<li>直接字段访问。为了规避API限制，一些实现把子类直接暴露为字段： </li>
</ul>
<p align="left">ZooImpl._animal</p>
<ul>
<li>另一种形式是，在知道实现的实际上是特定的子类的情况下，在调用程序中执行向下转换： </li>
</ul>
<p align="left">((AnimalImpl)ZooImpl.getAnimal()).implMethod();</p>
<ul>
<li>我看到的最后一种形式是一个具体的方法，该方法用来避免由一个完全不同的签名所引发的问题： </li>
</ul>
<p align="left">ZooImpl._getAnimal();<br />
  <br />
  　　这三种模式都有它们的问题和局限性。要么是不够整洁，要么就是暴露了不必要的实现细节。 <br />
  <strong><br />
  协变 </strong>
<br />
  　　协变返回模式就比较整洁、安全并且易于维护，它也不需要类型强制转换或特定的方法或字段： <br />
  public AnimalImpl  getAnimal(){<br />
  return new AnimalImpl();<br />
  }<br />
  　　使用结果： <br />
  ZooImpl.getAnimal().implMethod();<br />
  <br />
<strong>使用泛型<br />
　　</strong>
我们将从两个角度来了解泛型：使用泛型和构造泛型。我们不讨论List、Set和Map的显而易见的用法。知道泛型集合是强大的并且应该经常使用就足够了。 <br />
　　我们将讨论泛型方法的使用以及编译器推断类型的方法。通常这些都不会出问题，但是当出问题时，错误信息会非常令人费解，所以需要了解如何修复这些问题。 </p>
<p>
<strong>泛型方法<br />
　　</strong>
除了泛型类型，Java 5还引入了泛型方法。在这个来自java.util.Collections的例子中，构造了一个单元素列表。新的List的元素类型是根据传入方法的对象的类型来推断的： 
</p>
<pre class="code">static &lt;T&gt; List&lt;T&gt; Collections.singletonList(T o)
示例用法：
public List&lt;Integer&gt; getListOfOne() {
    return Collections.singletonList(1);
}</pre>
<p>　　在示例用法中，我们传入了一个int。所以方法的返回类型就是List&lt;Integer&gt;。编译器把T推断为Integer。这和泛型类型是不同的，因为您通常不需要显式地指定类型参数。 <br />
  这也显示了自动装箱和泛型的相互作用。类型参数必须是引用类型：这就是为什么我们得到的是List&lt;Integer&gt;而不是List&lt;int&gt;。 </p>
<p>
<strong>不带参数的泛型方法<br />
　　</strong>
emptyList()方法与泛型一起引入，作为java.util.Collections中EMPTY_LIST字段的类型安全置换： 
</p>
<pre class="code">static &lt;T&gt; List&lt;T&gt; Collections.emptyList()
示例用法： 
public List&lt;Integer&gt; getNoIntegers() {
    return Collections.emptyList();
}</pre>
<p>　　与先前的例子不同，这个方法没有参数，那么编译器如何推断T的类型呢？基本上，它将尝试使用一次参数。如果没有起作用，它再次尝试使用返回或赋值类型。在本例中，返回的是List&lt;Integer&gt;，所以T被推断为Integer。 <br />
  　　如果在返回语句或赋值语句之外的位置调用泛型方法会怎么样呢？那么编译器将无法执行类型推断的第二次传送。在下面这个例子中，emptyList()是从条件运算符内部调用的： </p>
<pre class="code">public List&lt;Integer&gt; getNoIntegers() {
    return x ? Collections.emptyList() : null;
}</pre>
<p>　　因为编译器看不到返回上下文，也不能推断T，所以它放弃并采用Object。您将看到一个错误消息，比如：&ldquo;无法将List&lt;Object&gt;转换为List&lt;Integer&gt;。&rdquo; <br />
  为了修复这个错误，应显式地向方法调用传递类型参数。这样，编译器就不会试图推断类型参数，就可以获得正确的结果： </p>
<pre class="code">return x ? Collections.&lt;Integer&gt;emptyList() : null;</pre>
<p>　　这种情况经常发生的另一个地方是在方法调用中。如果一个方法带一个List&lt;String&gt;参数，并且需要为那个参数调用这个传递的emptyList()，那么也需要使用这个语法。 </p>
<p>
<strong>集合之外<br />
</strong>
　　这里有三个泛型类型的例子，它们不是集合，而是以一种新颖的方式使用泛型。这三个例子都来自标准的Java库： 
</p>
<ul>
<li>Class&lt;T&gt;<br />
    Class在类的类型上被参数化了。这就使无需类型强制转换而构造一个newInstance成为可能。 </li>
<li>Comparable&lt;T&gt;<br />
    Comparable被实际的比较类型参数化。这就在compareTo()调用时提供了更强的类型化。例如，String实现Comparable&lt;String&gt;。对除String之外的任何东西调用compareTo()，都会在编译时失败。 </li>
<li>Enum&lt;E  extends Enum&lt;E&gt;&gt;<br />
Enum被枚举类型参数化。一个名为Color的枚举类型将扩展Enum&lt;Color&gt;。getDeclaringClass()方法返回枚
举类型的类对象，在这个例子中就是一个Color对象。它与getClass()不同，后者可能返回一个无名类。 </li>
</ul>
<p>
<strong>通配符<br />
　　</strong>
泛型最复杂的部分是对通配符的理解。我们将讨论三种类型的通配符以及它们的用途。 <br />
  　　首先让我们了解一下数组是如何工作的。可以从一个Integer[]为一个Number[]赋值。如果尝试把一个Float写到Number[]中，那么可以编译，但在运行时会失败，出现一个ArrayStoreException： 
</p>
<pre class="code">Integer[] ia = new Integer[5];
Number[] na = ia;
na[0] = 0.5; // compiles, but fails at runtime
如果试图把该例直接转换成泛型，那么会在编译时失败，因为赋值是不被允许的：
List&lt;Integer&gt; iList = new ArrayList&lt;Integer&gt;();
List&lt;Number&gt; nList = iList; // not allowed
nList.add(0.5);</pre>
<p>　　如果使用泛型，只要代码在编译时没有出现警告，就不会遇到运行时ClassCastException。 </p>
<p><strong>上限通配符</strong>
<br />
　　我们想要的是一个确切元素类型未知的列表，这一点与数组是不同的。 <br />
  List&lt;Number&gt;是一个列表，其元素类型是具体类型Number。 <br />
List&lt;?  extends Number&gt;是一个确切元素类型未知的列表。它是Number或其子类型。 </p>
<p><strong>上限<br />
　　</strong>
如果我们更新初始的例子，并赋值给List&lt;? extends Number&gt;，那么现在赋值就会成功了： </p>
<pre class="code">List&lt;Integer&gt; iList = new ArrayList&lt;Integer&gt;();
List&lt;? extends Number&gt; nList = iList;
Number n = nList.get(0);
nList.add(0.5); // Not allowed</pre>
<p>　　我们可以从列表中得到Number，因为无论列表的确切元素类型是什么（Float、Integer或Number），我们都可以把它赋值给Number。 <br />
  　　我们仍然不能把浮点类型插入列表中。这会在编译时失败，因为我们不能证明这是安全的。如果我们想要向列表中添加浮点类型，它将破坏iList的初始类型安全&mdash;&mdash;它只存储Integer。 <br />
  　　通配符给了我们比数组更多的表达能力。 </p>
<p>
<strong>为什么使用通配符<br />
</strong>
　　在下面这个例子中，通配符用于向API的用户隐藏类型信息。在内部，Set被存储为CustomerImpl。而API的用户只知道他们正在获取一个Set，从中可以读取Customer。 <br />
  此处通配符是必需的，因为无法从Set&lt;CustomerImpl&gt;向Set&lt;Customer&gt;赋值： 
  </p>
<pre class="code">public class CustomerFactory {
    private Set&lt;CustomerImpl&gt; _customers;
    public Set&lt;? extends Customer&gt; getCustomers() {
        return _customers;
    }
}</pre>
<p><strong>通配符和协变返回</strong>
<br />
　　通配符的另一种常见用法是和协变返回一起使用。与赋值相同的规则可以应用到协变返回上。如果希望在重写的方法中返回一个更具体的泛型类型，声明的方法必须使用通配符： </p>
<pre class="code">public interface NumberGenerator {
    public List&lt;? extends Number&gt; generate();
}
public class FibonacciGenerator extends NumberGenerator {
    public List&lt;Integer&gt; generate() {
        ...
    }
}</pre>
<p>　　如果要使用数组，接口可以返回Number[]，而实现可以返回Integer[]。 </p>
<p><strong>下限 </strong>
<br />
　　我们所谈的主要是关于上限通配符的。还有一个下限通配符。List&lt;? super
Number&gt;是一个确切&ldquo;元素类型&rdquo;未知的列表，但是可能是Mnumber，或者Number的超类型。所以它可能是一个List&lt;
Number&gt;或一个List&lt;Object&gt;。 <br />
  　　下限通配符远没有上限通配符那样常见，但是当需要它们的时候，它们就是必需的。 </p>
<p>
<strong>下限与上限<br />
</strong>
</p>
<pre class="code">List&lt;? extends Number&gt; readList = new ArrayList&lt;Integer&gt;();
Number n = readList.get(0);

List&lt;? super Number&gt; writeList = new ArrayList&lt;Object&gt;();
writeList.add(new Integer(5));</pre>
<p>　　第一个是可以从中读数的列表。 <br />
  　　第二个是可以向其写数的列表。 </p>
<p><strong>无界通配符</strong>
<br />
　　最后，List&lt;?&gt;列表的内容可以是任何类型，而且它与List&lt;? extends Object&gt;几乎相同。可以随时读取Object，但是不能向列表中写入内容。 </p>
<p><strong>公共API中的通配符 </strong>
<br />
　　总之，正如前面所说，通配符在向调用程序隐藏实现细节方面是非常重要的，但即使下限通配符看起来是提供只读访问，由于remove(int
position)之类的非泛型方法，它们也并非如此。如果您想要一个真正不变的集合，可以使用java.util.Collection上的方法，比如
unmodifiableList()。 <br />
  　　编写API的时候要记得通配符。通常，在传递泛型类型时，应该尝试使用通配符。它使更多的调用程序可以访问API。 <br />
  　　通过接收List&lt;? extends Number&gt;而不是List&lt;Number&gt;，下面的方法可以由许多不同类型的列表调用： </p>
<pre class="code">void removeNegatives(List&lt;? extends Number&gt; list);</pre>
<p><strong>构造泛型类型</strong>
<br />
　　现在我们将讨论构造自己的泛型类型。我们将展示一些例子，其中通过使用泛型可以提高类型安全性，我们还将讨论一些实现泛型类型时的常见问题。</p>
<p> <strong>集合风格(Collection-like)的函数</strong>
<br />
　　第一个泛型类的例子是一个集合风格的例子。Pair有两个类型参数，而且字段是类型的实例： </p>
<pre class="code">public final class Pair&lt;A,B&gt; {
    public final A first;
    public final B second;

    public Pair(A first, B second) {
        this.first = first;
        this.second = second;
    }
}</pre>
<p>　　这使从方法返回两个项而无需为每个两种类型的组合编写专用的类成为可能。另一种方法是返回Object[]，而这样是类型不安全或者不整洁的。 <br />
  在下面的用法中，我们从方法返回一个File和一个Boolean。方法的客户端可以直接使用字段而无需类型强制转换： </p>
<pre class="code">public Pair&lt;File,Boolean&gt; getFileAndWriteStatus(String path){
    // create file and status
    return new Pair&lt;File,Boolean&gt;(file, status);
}

Pair&lt;File,Boolean&gt; result = getFileAndWriteStatus(&quot;...&quot;);
File f = result.first;
boolean writeable = result.second;</pre>
<p><strong>集合之外 </strong>
<br />
　　在下面这个例子中，泛型被用于附加的编译时安全性。通过把DBFactory类参数化为所创建的Peer类型，您实际上是在强制Factory子类返回一个Peer的特定子类型： </p>
<pre class="code">public abstract class DBFactory&lt;T extends DBPeer&gt; {
    protected abstract T createEmptyPeer();
    public List&lt;T&gt; get(String constraint) {
        List&lt;T&gt; peers = new ArrayList&lt;T&gt;();
        // database magic
        return peers;
    }
}
通过实现DBFactory&lt;Customer&gt;，CustomerFactory必须从createEmptyPeer()返回一个Customer：
public class CustomerFactory extends DBFactory&lt;Customer&gt;{

    public Customer createEmptyPeer() {
        return new Customer();
    }
}</pre>
<p><strong>泛型方法<br />
</strong>
　　不管想要对参数之间还是参数与返回类型之间的泛型类型施加约束，都可以使用泛型方法： <br />
　　例如，如果编写的反转函数是在位置上反转，那么可能不需要泛型方法。然而，如果希望反转返回一个新的List，那么可能会希望新List的元素类型与传入的List的类型相同。在这种情况下，就需要一个泛型方法： </p>
<p><br />
&lt;T&gt; List&lt;T&gt; reverse(List&lt;T&gt; list)
</p>
<p><strong>具体化<br />
</strong>
　　当实现一个泛型类时，您可能想要构造一个数组T[]。因为泛型是通过擦除(erasure)实现的，所以这是不允许的。 <br />
　　您可以尝试把Object[]强制转换为T[]。但这是不安全的。 </p>
<p>
<strong>具体化解决方案<br />
　　</strong>
按照泛型教程的惯例，解决方案使用的是&ldquo;类型令牌&rdquo;，通过向构造函数添加一个Class&lt;T&gt;参数，可以强制客户端为类的类型参数提供正确的类对象： 
</p>
<pre class="code">public class ArrayExample&lt;T&gt; {
    private Class&lt;T&gt; clazz;

    public ArrayExample(Class&lt;T&gt; clazz) {
        this.clazz = clazz;
    }

    public T[] getArray(int size) {
        return (T[])Array.newInstance(clazz, size);
    }
}</pre>
<p>　　为了构造ArrayExample&lt;String&gt;，客户端必须把String.class传递给构造函数，因为String.class的类型是Class&lt;String&gt;。 <br />
  拥有类对象使构造一个具有正确元素类型的数组成为可能。 </p>
<p><strong>结束语</strong>
<br />
　　总而言之，新的语言特性有助于从根本上改变Java。通过了解在什么场景下使用以及如何使用这些新特性，您将会编写出更好的代码。</p>
          <br/>
          <span style="color:red;">
            <a href="http://coffeebar.javaeye.com/blog/208498#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 27 Jun 2008 09:53:01 +0800</pubDate>
        <link>http://coffeebar.javaeye.com/blog/208498</link>
        <guid>http://coffeebar.javaeye.com/blog/208498</guid>
      </item>
      <item>
        <title>字符，字节和编码</title>
        <author>alfred.w</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coffeebar.javaeye.com">alfred.w</a>&nbsp;
          链接：<a href="http://coffeebar.javaeye.com/blog/206336" style="color:red;">http://coffeebar.javaeye.com/blog/206336</a>&nbsp;
          发表时间: 2008年06月20日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <h2>字符，字节和编码</h2>
<p><span style="font-size: xx-small;">[原创文章，转载请保留或注明出处：<a href="http://www.regexlab.com/zh/encoding.htm">http://www.regexlab.com/zh/encoding.htm</a>
]</span>
</p>
<p>级别：中级</p>
<blockquote>
<p>摘要：本文介绍了字符与编码的发展过程，相关概念的正确理解。举例说明了一些实际应用中，编码的实现方法。然后，本文讲述了通常对字符与编码的几种误解，由于这些误解而导致乱码产生的原因，以及消除乱码的办法。本文的内容涵盖了&ldquo;中文问题&rdquo;，&ldquo;乱码问题&rdquo;。</p>
<p>掌握编码问题的关键是正确地理解相关概念，编码所涉及的技术其实是很简单的。因此，阅读本文时需要慢读多想，多思考。</p>
</blockquote>
<h4><a name="intro"></a>
引言</h4>
<p>&ldquo;字
符与编码&rdquo;是一个被经常讨论的话题。即使这样，时常出现的乱码仍然困扰着大家。虽然我们有很多的办法可以用来消除乱码，但我们并不一定理解这些办法的内在
原理。而有的乱码产生的原因，实际上由于底层代码本身有问题所导致的。因此，不仅是初学者会对字符编码感到模糊，有的底层开发人员同样对字符编码缺乏准确
的理解。</p>
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr valign="top">
<td align="right" width="100%"><img src="http://www.regexlab.com/images/blue_rule.gif" border="0" height="1" alt="" width="100%" />
</td>
</tr>
<tr valign="top">
<td align="right" width="100%">
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr align="right">
<td>
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr>
<td valign="center"><img src="http://www.regexlab.com/images/u_bold.gif" border="0" height="16" alt="" width="16" />
</td>
<td align="right" valign="top">
<p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h4><a name="develop"></a>
1. 编码问题的由来，相关概念的理解</h4>
<h5>1.1 字符与编码的发展</h5>
<p>从计算机对多国语言的支持角度看，大致可以分为三个阶段：</p>
<table cellspacing="0" border="0" cellpadding="3">
<tbody>
<tr>
<td class="top_1">　</td>
<td class="top_2" align="center"><strong>系统内码</strong>
</td>
<td class="top_2" align="center"><strong>说明</strong>
</td>
<td class="top_2" align="center"><strong>系统</strong>
</td>
</tr>
<tr>
<td class="con_1">阶段一</td>
<td class="con_2" align="center">ASCII</td>
<td class="con_2">计算机刚开始只支持英语，其它语言不能够在计算机上存储和显示。</td>
<td class="con_2">英文 DOS</td>
</tr>
<tr>
<td class="con_1">阶段二</td>
<td class="con_2" align="center">ANSI编码<br />
                （本地化）</td>
<td class="con_2">为使计算机支持更多语言，通常使用 0x80~0xFF 范围的 2 个字节来表示 1 个字符。比如：汉字 '中' 在中文操作系统中，使用 [0xD6,0xD0] 这两个字节存储。<br />
                <br />
                不同的国家和地区制定了不同的标准，由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式，称为<strong> ANSI 编码</strong>
。在简体中文系统下，ANSI 编码代表 GB2312 编码，在日文操作系统下，ANSI 编码代表 JIS 编码。<br />
                <br />
                不同 ANSI 编码之间互不兼容，当信息在国际间交流时，无法将属于两种语言的文字，存储在同一段<strong> ANSI 编码</strong>
的文本中。</td>
<td class="con_2">中文 DOS，中文 Windows 95/98，日文 Windows 95/98</td>
</tr>
<tr>
<td class="bot_1">阶段三</td>
<td class="bot_2" align="center">UNICODE<br />
                （国际化）</td>
<td class="bot_2">为了使国际间信息交流更加方便，国际组织制定了 <strong>UNICODE 字符集</strong>
，为各种语言中的每一个字符设定了统一并且唯一的数字编号，以满足跨语言、跨平台进行文本转换、处理的要求。</td>
<td class="bot_2">Windows NT/2000/XP，Linux，Java</td>
</tr>
</tbody>
</table>
<p>字符串在内存中的存放方法：</p>
<p>在 ASCII 阶段，<strong>单字节字符串</strong>
使用一个字节存放一个字符（SBCS）。比如，&quot;Bob123&quot; 在内存中为：</p>
<table cellspacing="5" border="0" cellpadding="0" style="font-size: 80%; color: #000080;">
<tbody>
<tr>
<td>42</td>
<td>6F</td>
<td>62</td>
<td>31</td>
<td>32</td>
<td>33</td>
<td>00</td>
</tr>
<tr>
<td bgcolor="#000080"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#000080"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#000080"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#000080"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#000080"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#000080"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#000080"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
</tr>
<tr>
<td align="center">B</td>
<td align="center">o</td>
<td align="center">b</td>
<td align="center">1</td>
<td align="center">2</td>
<td align="center">3</td>
<td align="center">\0</td>
</tr>
</tbody>
</table>
<p>在使用 ANSI 编码支持多种语言阶段，每个字符使用一个字节或多个字节来表示（MBCS），因此，这种方式存放的字符也被称作<strong>多字节字符</strong>
。比如，&quot;中文123&quot; 在中文 Windows 95 内存中为7个字节，每个汉字占2个字节，每个英文和数字字符占1个字节：</p>
<table cellspacing="5" border="0" cellpadding="0" style="font-size: 80%; color: #000080;">
<tbody>
<tr>
<td>D6</td>
<td>D0</td>
<td>CE</td>
<td>C4</td>
<td>31</td>
<td>32</td>
<td>33</td>
<td>00</td>
</tr>
<tr>
<td bgcolor="#ff0000" colspan="2"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#ff0000" colspan="2"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#000080"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#000080"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#000080"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#000080"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
</tr>
<tr>
<td align="center" colspan="2">中</td>
<td align="center" colspan="2">文</td>
<td align="center">1</td>
<td align="center">2</td>
<td align="center">3</td>
<td align="center">\0</td>
</tr>
</tbody>
</table>
<p>在 UNICODE 被采用之后，计算机存放字符串时，改为存放每个字符在 UNICODE 字符集中的序号。目前计算机一般使用 2 个字节（16 位）来存放一个序号（DBCS），因此，这种方式存放的字符也被称作<strong>宽字节字符</strong>
。比如，字符串 &quot;中文123&quot; 在 Windows 2000 下，内存中实际存放的是 5 个序号：</p>
<table cellspacing="5" border="0" cellpadding="0" style="font-size: 80%; color: #000080;">
<tbody>
<tr>
<td valign="bottom">2D</td>
<td valign="bottom">4E</td>
<td valign="bottom">87</td>
<td valign="bottom">65</td>
<td valign="bottom">31</td>
<td valign="bottom">00</td>
<td valign="bottom">32</td>
<td valign="bottom">00</td>
<td valign="bottom">33</td>
<td valign="bottom">00</td>
<td valign="bottom">00</td>
<td valign="bottom">00</td>
<td><span style="color: #808080;">&nbsp;&nbsp;&nbsp;&nbsp; &larr; 在 x86 CPU 中，低字节在前</span>
</td>
</tr>
<tr>
<td bgcolor="#ff0000" colspan="2"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#ff0000" colspan="2"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#ff0000" colspan="2"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#ff0000" colspan="2"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#ff0000" colspan="2"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td bgcolor="#ff0000" colspan="2"><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
<td><img src="http://www.regexlab.com/images/spacer.gif" border="0" height="1" alt="" width="1" />
</td>
</tr>
<tr>
<td align="center" colspan="2">中</td>
<td align="center" colspan="2">文</td>
<td align="center" colspan="2">1</td>
<td align="center" colspan="2">2</td>
<td align="center" colspan="2">3</td>
<td align="center" colspan="2">\0</td>
<td align="center">　</td>
</tr>
</tbody>
</table>
<p>一共占 10 个字节。</p>
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr valign="top">
<td align="right" width="100%"><img src="http://www.regexlab.com/images/blue_rule.gif" border="0" height="1" alt="" width="100%" />
</td>
</tr>
<tr valign="top">
<td align="right" width="100%">
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr align="right">
<td>
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr>
<td valign="center"><img src="http://www.regexlab.com/images/u_bold.gif" border="0" height="16" alt="" width="16" />
</td>
<td align="right" valign="top">
<p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h5><a name="concept"></a>
1.2 字符，字节，字符串</h5>
<p>理解编码的关键，是要把字符的概念和字节的概念理解准确。这两个概念容易混淆，我们在此做一下区分：</p>
<table cellspacing="0" border="0" cellpadding="3">
<tbody>
<tr>
<td class="top_1">　</td>
<td class="top_2" align="center"><strong>概念描述</strong>
</td>
<td class="top_2" align="center"><strong>举例</strong>
</td>
</tr>
<tr>
<td class="con_1" align="center">字符</td>
<td class="con_2">人们使用的记号，抽象意义上的一个符号。</td>
<td class="con_2">'1', '中', 'a', '$', '￥', &hellip;&hellip;</td>
</tr>
<tr>
<td class="con_1" align="center">字节</td>
<td class="con_2">计算机中存储数据的单元，一个8位的二进制数，是一个很具体的存储空间。</td>
<td class="con_2">0x01, 0x45, 0xFA, &hellip;&hellip;</td>
</tr>
<tr>
<td class="con_1" align="center">ANSI<br />
                字符串</td>
<td class="con_2">在内存中，如果&ldquo;字符&rdquo;是以 <strong>ANSI 编码</strong>
形式存在的，一个字符可能使用一个字节或多个字节来表示，那么我们称这种字符串为 <strong>ANSI 字符串</strong>
或者<strong>多字节字符串</strong>
。</td>
<td class="con_2">&quot;中文123&quot;<br />
                <span class="rem">（占7字节）</span>
</td>
</tr>
<tr>
<td class="bot_1" align="center">UNICODE<br />
                字符串</td>
<td class="bot_2">在内存中，如果&ldquo;字符&rdquo;是以在 UNICODE 中的序号存在的，那么我们称这种字符串为 <strong>UNICODE 字符串</strong>
或者<strong>宽字节字符串</strong>
。</td>
<td class="bot_2">L&quot;中文123&quot;<br />
                <span class="rem">（占10字节）</span>
</td>
</tr>
</tbody>
</table>
<p>由于不同 ANSI 编码所规定的标准是不相同的，因此，对于一个给定的<strong>多字节字符串</strong>
，我们必须知道它采用的是哪一种编码规则，才能够知道它包含了哪些&ldquo;字符&rdquo;。而对于 <strong>UNICODE 字符串</strong>
来说，不管在什么环境下，它所代表的&ldquo;字符&rdquo;内容总是不变的。</p>
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr valign="top">
<td align="right" width="100%"><img src="http://www.regexlab.com/images/blue_rule.gif" border="0" height="1" alt="" width="100%" />
</td>
</tr>
<tr valign="top">
<td align="right" width="100%">
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr align="right">
<td>
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr>
<td valign="center"><img src="http://www.regexlab.com/images/u_bold.gif" border="0" height="16" alt="" width="16" />
</td>
<td align="right" valign="top">
<p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h5>1.3 字符集与编码</h5>
<p>各个国家和地区所制定的不同 ANSI 编码标准中，都只规定了各自语言所需的&ldquo;字符&rdquo;。比如：汉字标准（GB2312）中没有规定韩国语字符怎样存储。这些 ANSI 编码标准所规定的内容包含两层含义：</p>
<ol>
<li>使用哪些字符。也就是说哪些汉字，字母和符号会被收入标准中。所包含&ldquo;字符&rdquo;的集合就叫做&ldquo;<strong>字符集</strong>
&rdquo;。 </li>
<li>规定每个&ldquo;字符&rdquo;分别用一个字节还是多个字节存储，用哪些字节来存储，这个规定就叫做&ldquo;<strong>编码</strong>
&rdquo;。 </li>
</ol>
<p>各个国家和地区在制定编码标准的时候，&ldquo;字符的集合&rdquo;和&ldquo;编码&rdquo;一般都是同时制定的。因此，平常我们所说的&ldquo;字符集&rdquo;，比如：GB2312, GBK, JIS 等，除了有&ldquo;字符的集合&rdquo;这层含义外，同时也包含了&ldquo;编码&rdquo;的含义。</p>
<p>&ldquo;<strong>UNICODE 字符集</strong>
&rdquo;包含了各种语言中使用到的所有&ldquo;字符&rdquo;。用来给 UNICODE 字符集编码的标准有很多种，比如：UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等。</p>
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr valign="top">
<td align="right" width="100%"><img src="http://www.regexlab.com/images/blue_rule.gif" border="0" height="1" alt="" width="100%" />
</td>
</tr>
<tr valign="top">
<td align="right" width="100%">
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr align="right">
<td>
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr>
<td valign="center"><img src="http://www.regexlab.com/images/u_bold.gif" border="0" height="16" alt="" width="16" />
</td>
<td align="right" valign="top">
<p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h5>1.4 常用的编码简介</h5>
<p>简单介绍一下常用的编码规则，为后边的章节做一个准备。在这里，我们根据编码规则的特点，把所有的编码分成三类：</p>
<table cellspacing="0" border="0" cellpadding="3">
<tbody>
<tr>
<td class="top_1" align="center"><strong>分类</strong>
</td>
<td class="top_2" align="center"><strong>编码标准</strong>
</td>
<td class="top_2" align="center"><strong>说明</strong>
</td>
</tr>
<tr>
<td class="con_1" align="center">单字节字符编码</td>
<td class="con_2">ISO-8859-1</td>
<td class="con_2">最简单的编码规则，每一个字节直接作为一个 UNICODE 字符。比如，[0xD6, 0xD0] 这两个字节，通过 iso-8859-1 转化为字符串时，将直接得到 [0x00D6, 0x00D0] 两个 UNICODE 字符，即 &quot;&Ouml;&ETH;&quot;。<br />
                <br />
                反之，将 UNICODE 字符串通过 iso-8859-1 转化为字节串时，只能正常转化 0~255 范围的字符。</td>
</tr>
<tr>
<td class="con_1" align="center">ANSI 编码</td>
<td class="con_2">GB2312,<br />
                BIG5,<br />
                Shift_JIS,<br />
                ISO-8859-2 &hellip;&hellip;</td>
<td class="con_2">把 UNICODE 字符串通过 ANSI 编码转化为&ldquo;字节串&rdquo;时，根据各自编码的规定，一个 UNICODE 字符可能转化成一个字节或多个字节。<br />
                <br />
                反之，将字节串转化成字符串时，也可能多个字节转化成一个字符。比如，[0xD6, 0xD0] 这两个字节，通过 GB2312 转化为字符串时，将得到 [0x4E2D] 一个字符，即 '中' 字。<br />
                <br />
                &ldquo;ANSI 编码&rdquo;的特点：<br />
                1. 这些&ldquo;ANSI 编码标准&rdquo;都只能处理各自语言范围之内的 UNICODE 字符。<br />
                2. &ldquo;UNICODE 字符&rdquo;与&ldquo;转换出来的字节&rdquo;之间的关系是人为规定的。</td>
</tr>
<tr>
<td class="bot_1" align="center">UNICODE 编码</td>
<td class="bot_2">UTF-8,<br />
                UTF-16, UnicodeBig &hellip;&hellip;</td>
<td class="bot_2">与&ldquo;ANSI 编码&rdquo;类似的，把字符串通过 UNICODE 编码转化成&ldquo;字节串&rdquo;时，一个 UNICODE 字符可能转化成一个字节或多个字节。<br />
                <br />
                与&ldquo;ANSI 编码&rdquo;不同的是：<br />
                1. 这些&ldquo;UNICODE 编码&rdquo;能够处理所有的 UNICODE 字符。<br />
                2. &ldquo;UNICODE 字符&rdquo;与&ldquo;转换出来的字节&rdquo;之间是可以通过计算得到的。</td>
</tr>
</tbody>
</table>
<p>我
们实际上没有必要去深究每一种编码具体把某一个字符编码成了哪几个字节，我们只需要知道&ldquo;编码&rdquo;的概念就是把&ldquo;字符&rdquo;转化成&ldquo;字节&rdquo;就可以了。对于
&ldquo;UNICODE 编码&rdquo;，由于它们是可以通过计算得到的，因此，在特殊的场合，我们可以去了解某一种&ldquo;UNICODE 编码&rdquo;是怎样的规则。</p>
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr valign="top">
<td align="right" width="100%"><img src="http://www.regexlab.com/images/blue_rule.gif" border="0" height="1" alt="" width="100%" />
</td>
</tr>
<tr valign="top">
<td align="right" width="100%">
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr align="right">
<td>
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr>
<td valign="center"><img src="http://www.regexlab.com/images/u_bold.gif" border="0" height="16" alt="" width="16" />
</td>
<td align="right" valign="top">
<p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h4><a name="implement"></a>
2. 字符与编码在程序中的实现</h4>
<h5>2.1 程序中的字符与字节</h5>
<p>在 C++ 和 Java 中，用来代表&ldquo;字符&rdquo;和&ldquo;字节&rdquo;的数据类型，以及进行编码的方法：</p>
<table cellspacing="0" border="0" cellpadding="3">
<tbody>
<tr>
<td class="top_1" align="center"><strong>类型或操作</strong>
</td>
<td class="top_2" align="center"><strong>C++</strong>
</td>
<td class="top_2" align="center"><strong>Java</strong>
</td>
</tr>
<tr>
<td class="con_1" align="center">字符</td>
<td class="con_2">wchar_t</td>
<td class="con_2">char</td>
</tr>
<tr>
<td class="con_1" align="center">字节</td>
<td class="con_2">char</td>
<td class="con_2">byte</td>
</tr>
<tr>
<td class="con_1" align="center">ANSI 字符串</td>
<td class="con_2">char[]</td>
<td class="con_2">byte[]</td>
</tr>
<tr>
<td class="con_1" align="center">UNICODE 字符串</td>
<td class="con_2">wchar_t[]</td>
<td class="con_2">String</td>
</tr>
<tr>
<td class="con_1" align="center">字节串&rarr;字符串</td>
<td class="con_2">mbstowcs(), MultiByteToWideChar()</td>
<td class="con_2">string = new String(bytes, &quot;encoding&quot;)</td>
</tr>
<tr>
<td class="bot_1" align="center">字符串&rarr;字节串</td>
<td class="bot_2">wcstombs(), WideCharToMultiByte()</td>
<td class="bot_2">bytes = string.getBytes(&quot;encoding&quot;)</td>
</tr>
</tbody>
</table>
<p>以上需要注意几点：</p>
<ol>
<li>Java 中的 char 代表一个&ldquo;UNICODE 字符（宽字节字符）&rdquo;，而 C++ 中的 char 代表一个字节。</li>
<li>MultiByteToWideChar() 和 WideCharToMultiByte() 是 Windows API 函数。</li>
</ol>
            
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr valign="top">
<td align="right" width="100%"><img src="http://www.regexlab.com/images/blue_rule.gif" border="0" height="1" alt="" width="100%" />
</td>
</tr>
<tr valign="top">
<td align="right" width="100%">
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr align="right">
<td>
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr>
<td valign="center"><img src="http://www.regexlab.com/images/u_bold.gif" border="0" height="16" alt="" width="16" />
</td>
<td align="right" valign="top">
<p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h5>2.2 C++ 中相关实现方法</h5>
<p>声明一段字符串常量：</p>
<table cellspacing="0" border="1" bgcolor="#eeeeee" cellpadding="6">
<tbody>
<tr>
<td class="code"><span class="rem">// ANSI 字符串，内容长度 7 字节</span>
<span class="key"><br />
                char</span>
&nbsp;&nbsp;&nbsp;&nbsp; sz[<span class="number">20</span>
] = <span class="string">&quot;中文123&quot;</span>
;<br />
                <br />
                <span class="rem">// UNICODE 字符串，内容长度 5 个 wchar_t（10 字节）</span>
<br />
                wchar_t wsz[<span class="number">20</span>
] = L<span class="string">&quot;\x4E2D\x6587\x0031\x0032\x0033&quot;</span>
;</td>
</tr>
</tbody>
</table>
<p>UNICODE 字符串的 I/O 操作，字符与字节的转换操作：</p>
<table cellspacing="0" border="1" bgcolor="#eeeeee" cellpadding="6">
<tbody>
<tr>
<td class="code"><span class="rem">// 运行时设定当前 ANSI 编码，VC 格式<br />
                </span>
setlocale(LC_ALL, <span class="string">&quot;.936&quot;</span>
);<br />
                <br />
                <span class="rem">// GCC 中格式</span>
<br />
                setlocale(LC_ALL, <span class="string">&quot;zh_CN.GBK&quot;</span>
);<br />
                <br />
                <span class="rem">// Visual C++ 中使用小写 %s，按照 setlocale 指定编码输出到文件<br />
                // GCC 中使用大写 %S</span>
<br />
                fwprintf(fp, L<span class="string">&quot;%s\n&quot;</span>
, wsz);<br />
                <br />
                <span class="rem">// 把 UNICODE 字符串按照 setlocale 指定的编码转换成字节</span>
<br />
                wcstombs(sz, wsz, <span class="number">20</span>
);<span class="rem"><br />
                // 把字节串按照 setlocale 指定的编码转换成 UNICODE 字符串<br />
                </span>
mbstowcs(wsz, sz, <span class="number">20</span>
);</td>
</tr>
</tbody>
</table>
<p>在 Visual C++ 中，UNICODE 字符串常量有更简单的表示方法。如果源程序的编码与当前默认 ANSI 编码不符，则需要使用 #pragma setlocale，告诉编译器源程序使用的编码：</p>
<table cellspacing="0" border="1" bgcolor="#eeeeee" cellpadding="6">
<tbody>
<tr>
<td class="code"><span class="rem">// 如果源程序的编码与当前默认 ANSI 编码不一致，<br />
                // 则需要此行，编译时用来指明当前源程序使用的编码</span>
<span class="key"><br />
                #pragma setlocale</span>
(<span class="string">&quot;.936&quot;</span>
)<br />
                <br />
                <span class="rem">// UNICODE 字符串常量，内容长度 10 字节</span>
<br />
                wchar_t wsz[<span class="number">20</span>
] = L<span class="string">&quot;中文123&quot;</span>
;</td>
</tr>
</tbody>
</table>
<p>以上需要注意 #pragma setlocale 与 setlocale(LC_ALL, &quot;&quot;) 的作用是不同的，#pragma setlocale 在编译时起作用，setlocale() 在运行时起作用。</p>
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr valign="top">
<td align="right" width="100%"><img src="http://www.regexlab.com/images/blue_rule.gif" border="0" height="1" alt="" width="100%" />
</td>
</tr>
<tr valign="top">
<td align="right" width="100%">
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr align="right">
<td>
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr>
<td valign="center"><img src="http://www.regexlab.com/images/u_bold.gif" border="0" height="16" alt="" width="16" />
</td>
<td align="right" valign="top">
<p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h5>2.3 Java 中相关实现方法</h5>
<p>字符串类 String 中的内容是 UNICODE 字符串：</p>
<table cellspacing="0" border="1" bgcolor="#eeeeee" cellpadding="6">
<tbody>
<tr>
<td class="code"><span class="rem">// Java 代码，直接写中文</span>
<span class="pw"><br />
                String</span>
 string = <span class="string">&quot;中文123&quot;</span>
;<br />
                <br />
                <span class="rem">// 得到长度为 5，因为是 5 个字符</span>
<br />
                <span class="pw">System</span>
.out.println(string.length());</td>
</tr>
</tbody>
</table>
<p>字符串 I/O 操作，字符与字节转换操作。在 Java 包 java.io.* 中，以&ldquo;Stream&rdquo;结尾的类一般是用来操作&ldquo;字节串&rdquo;的类，以&ldquo;Reader&rdquo;，&ldquo;Writer&rdquo;结尾的类一般是用来操作&ldquo;字符串&rdquo;的类。</p>
<table cellspacing="0" border="1" bgcolor="#eeeeee" cellpadding="6">
<tbody>
<tr>
<td class="code"><span class="rem">// 字符串与字节串间相互转化<br />
                <br />
                // 按照 GB2312 得到字节（得到多字节字符串）</span>
<span class="key"><br />
                byte</span>
 [] bytes = string.getBytes(<span class="string">&quot;GB2312&quot;</span>
);<br />
                <br />
                <span class="rem">// 从字节按照 GB2312 得到 UNICODE 字符串</span>
<br />
                string = <span class="key">new</span>
 <span class="pw">String</span>
(bytes, <span class="string">&quot;GB2312&quot;</span>
);<br />
                <br />
                <span class="rem">// 要将 String 按照某种编码写入文本文件，有两种方法：<br />
                <br />
                // 第一种办法：用 Stream 类写入已经按照指定编码转化好的字节串</span>
<br />
                OutputStream os = <span class="key">new</span>
 FileOutputStream(<span class="string">&quot;1.txt&quot;</span>
);<br />
                os.write(bytes);<br />
                os.close();<br />
                <br />
                <span class="rem">// 第二种办法：构造指定编码的 Writer 来写入字符串</span>
<br />
                Writer ow = <span class="key">new</span>
 OutputStreamWriter(<span class="key">new</span>
 FileOutputStream(<span class="string">&quot;2.txt&quot;</span>
), <span class="string">&quot;GB2312&quot;</span>
);<br />
                ow.write(string);<br />
                ow.close();<br />
                <br />
                <span class="rem">/* 最后得到的 1.txt 和 2.txt 都是 7 个字节 */</span>
</td>
</tr>
</tbody>
</table>
<p>如果 java 的源程序编码与当前默认 ANSI 编码不符，则在编译的时候，需要指明一下源程序的编码。比如：</p>
<table cellspacing="0" border="1" bgcolor="#eeeeee" cellpadding="6">
<tbody>
<tr>
<td class="code">E:\&gt;javac <span style="color: #ff0000;">-encoding BIG5</span>
 Hello.java</td>
</tr>
</tbody>
</table>
<p>以上需要注意区分源程序的编码与 I/O 操作的编码，前者是在编译时起作用，后者是在运行时起作用。</p>
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr valign="top">
<td align="right" width="100%"><img src="http://www.regexlab.com/images/blue_rule.gif" border="0" height="1" alt="" width="100%" />
</td>
</tr>
<tr valign="top">
<td align="right" width="100%">
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr align="right">
<td>
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr>
<td valign="center"><img src="http://www.regexlab.com/images/u_bold.gif" border="0" height="16" alt="" width="16" />
</td>
<td align="right" valign="top">
<p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h4><a name="misunderstand"></a>
3. 几种误解，以及乱码产生的原因和解决办法</h4>
<h5>3.1 容易产生的误解</h5>
<table cellspacing="0" border="0" cellpadding="3">
<tbody>
<tr>
<td class="top_1">　</td>
<td class="top_2" align="center"><strong>对编码的误解</strong>
</td>
</tr>
<tr>
<td class="con_1" align="center">误解一</td>
<td class="con_2">在将&ldquo;字节串&rdquo;转化成&ldquo;UNICODE 字符串&rdquo;时，比如在读取文本文件时，或者通过网络传输文本时，容易将&ldquo;字节串&rdquo;简单地作为<strong>单字节字符串</strong>
，采用每&ldquo;一个字节&rdquo;就是&ldquo;一个字符&rdquo;的方法进行转化。<br />
                <br />
                而实际上，在非英文的环境中，应该将&ldquo;字节串&rdquo;作为 ANSI 字符串，采用适当的编码来得到 UNICODE 字符串，有可能&ldquo;多个字节&rdquo;才能得到&ldquo;一个字符&rdquo;。<br />
                <br />
                通常，一直在英文环境下做开发的程序员们，容易有这种误解。</td>
</tr>
<tr>
<td class="bot_1" align="center">误解二</td>
<td class="bot_2">在 DOS，Windows 98 等非 UNICODE 环境下，字符串都是以 ANSI 编码的字节形式存在的。这种以字节形式存在的字符串，必须知道是哪种编码才能被正确地使用。这使我们形成了一个惯性思维：&ldquo;字符串的编码&rdquo;。<br />
                <br />
当 UNICODE 被支持后，Java 中的 String
是以字符的&ldquo;序号&rdquo;来存储的，不是以&ldquo;某种编码的字节&rdquo;来存储的，因此已经不存在&ldquo;字符串的编码&rdquo;这个概念了。只有在&ldquo;字符串&rdquo;与&ldquo;字节串&rdquo;转化时，或
者，将一个&ldquo;字节串&rdquo;当成一个 ANSI 字符串时，才有编码的概念。<br />
                <br />
                不少的人都有这个误解。</td>
</tr>
</tbody>
</table>
<p>第一种误解，往往是导致乱码产生的原因。第二种误解，往往导致本来容易纠正的乱码问题变得更复杂。</p>
<p>在
这里，我们可以看到，其中所讲的&ldquo;误解一&rdquo;，即采用每&ldquo;一个字节&rdquo;就是&ldquo;一个字符&rdquo;的转化方法，实际上也就等同于采用 iso-8859-1
进行转化。因此，我们常常使用 bytes = string.getBytes(&quot;iso-8859-1&quot;)
来进行逆向操作，得到原始的&ldquo;字节串&rdquo;。然后再使用正确的 ANSI 编码，比如 string = new String(bytes,
&quot;GB2312&quot;)，来得到正确的&ldquo;UNICODE 字符串&rdquo;。</p>
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr valign="top">
<td align="right" width="100%"><img src="http://www.regexlab.com/images/blue_rule.gif" border="0" height="1" alt="" width="100%" />
</td>
</tr>
<tr valign="top">
<td align="right" width="100%">
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr align="right">
<td>
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr>
<td valign="center"><img src="http://www.regexlab.com/images/u_bold.gif" border="0" height="16" alt="" width="16" />
</td>
<td align="right" valign="top">
<p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h5><a name="instances"></a>
3.2 非 UNICODE 程序在不同语言环境间移植时的乱码</h5>
<p>非 UNICODE 程序中的字符串，都是以某种 ANSI 编码形式存在的。如果程序运行时的语言环境与开发时的语言环境不同，将会导致 ANSI 字符串的显示失败。</p>
<p>比如，在日文环境下开发的非 UNICODE 的日文程序界面，拿到中文环境下运行时，界面上将显示乱码。如果这个日文程序界面改为采用 UNICODE 来记录字符串，那么当在中文环境下运行时，界面上将可以显示正常的日文。</p>
<p>由于客观原因，有时候我们必须在中文操作系统下运行非 UNICODE 的日文软件，这时我们可以采用一些工具，比如，南极星，AppLocale 等，暂时的模拟不同的语言环境。</p>
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr valign="top">
<td align="right" width="100%"><img src="http://www.regexlab.com/images/blue_rule.gif" border="0" height="1" alt="" width="100%" />
</td>
</tr>
<tr valign="top">
<td align="right" width="100%">
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr align="right">
<td>
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr>
<td valign="center"><img src="http://www.regexlab.com/images/u_bold.gif" border="0" height="16" alt="" width="16" />
</td>
<td align="right" valign="top">
<p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h5>3.3 网页提交字符串</h5>
<p>当页面中的表单提交字符串时，首先把字符串按照当前页面的编码，转化成字节串。然后再将每个字节转化成 &quot;%XX&quot; 的格式提交到 Web 服务器。比如，一个编码为 GB2312 的页面，提交 &quot;中&quot; 这个字符串时，提交给服务器的内容为 &quot;%D6%D0&quot;。</p>
<p>在服务器端，Web 服务器把收到的 &quot;%D6%D0&quot; 转化成 [0xD6, 0xD0] 两个字节，然后再根据 GB2312 编码规则得到 &quot;中&quot; 字。</p>
<p>在
Tomcat 服务器中，request.getParameter() 得到乱码时，常常是因为前面提到的&ldquo;误解一&rdquo;造成的。默认情况下，当提交
&quot;%D6%D0&quot; 给 Tomcat 服务器时，request.getParameter() 将返回 [0x00D6, 0x00D0] 两个
UNICODE 字符，而不是返回一个 &quot;中&quot; 字符。因此，我们需要使用 bytes =
string.getBytes(&quot;iso-8859-1&quot;) 得到原始的字节串，再用 string = new String(bytes,
&quot;GB2312&quot;) 重新得到正确的字符串 &quot;中&quot;。</p>
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr valign="top">
<td align="right" width="100%"><img src="http://www.regexlab.com/images/blue_rule.gif" border="0" height="1" alt="" width="100%" />
</td>
</tr>
<tr valign="top">
<td align="right" width="100%">
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr align="right">
<td>
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr>
<td valign="center"><img src="http://www.regexlab.com/images/u_bold.gif" border="0" height="16" alt="" width="16" />
</td>
<td align="right" valign="top">
<p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h5>3.4 从数据库读取字符串</h5>
<p>通过数据库客户端（比如 ODBC 或 JDBC）从数据库服务器中读取字符串时，客户端需要从服务器获知所使用的 ANSI 编码。当数据库服务器发送字节流给客户端时，客户端负责将字节流按照正确的编码转化成 UNICODE 字符串。</p>
<p>如
果从数据库读取字符串时得到乱码，而数据库中存放的数据又是正确的，那么往往还是因为前面提到的&ldquo;误解一&rdquo;造成的。解决的办法还是通过 string
= new String( string.getBytes(&quot;iso-8859-1&quot;), &quot;GB2312&quot;)
的方法，重新得到原始的字节串，再重新使用正确的编码转化成字符串。</p>
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr valign="top">
<td align="right" width="100%"><img src="http://www.regexlab.com/images/blue_rule.gif" border="0" height="1" alt="" width="100%" />
</td>
</tr>
<tr valign="top">
<td align="right" width="100%">
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr align="right">
<td>
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr>
<td valign="center"><img src="http://www.regexlab.com/images/u_bold.gif" border="0" height="16" alt="" width="16" />
</td>
<td align="right" valign="top">
<p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h5>3.5 电子邮件中的字符串</h5>
<p>当一段 Text 或者 HTML 通过电子邮件传送时，发送的内容首先通过一种指定的<strong>字符编码</strong>
转化成&ldquo;字节串&rdquo;，然后再把&ldquo;字节串&rdquo;通过一种指定的<strong>传输编码</strong>
（Content-Transfer-Encoding）进行转化得到另一串&ldquo;字节串&rdquo;。比如，打开一封电子邮件源代码，可以看到类似的内容：</p>
<table cellspacing="0" border="1" bgcolor="#eeeeee" width="100%" cellpadding="6">
<tbody>
<tr>
<td class="code">Content-Type: text/plain;<br />
                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #ff0000;">charset=&quot;gb2312&quot;</span>
<br />
                <span style="color: #ff0000;">Content-Transfer-Encoding: base64</span>
<br />
                <br />
                sbG+qcrQuqO17cf4yee74bGjz9W7+b3wudzA7dbQ0MQNCg0KvPKzxqO6uqO17cnnsaPW0NDEDQoNCg==</td>
</tr>
</tbody>
</table>
<p>最
常用的 Content-Transfer-Encoding 有 Base64 和 Quoted-Printable
两种。在对二进制文件或者中文文本进行转化时，Base64 得到的&ldquo;字节串&rdquo;比 Quoted-Printable
更短。在对英文文本进行转化时，Quoted-Printable 得到的&ldquo;字节串&rdquo;比 Base64 更短。</p>
<p>邮件的标题，用了一种更简短的格式来标注&ldquo;字符编码&rdquo;和&ldquo;传输编码&rdquo;。比如，标题内容为 &quot;中&quot;，则在邮件源代码中表示为：</p>
<table cellspacing="0" border="1" bgcolor="#eeeeee" width="100%" cellpadding="6">
<tbody>
<tr>
<td class="code"><span class="rem">// 正确的标题格式</span>
<br />
                Subject: <span style="background-color: #ffff00;">=?</span>
GB2312<span style="background-color: #ffff00;">?B?</span>
1tA=<span style="background-color: #ffff00;">?=</span>
</td>
</tr>
</tbody>
</table>
<p>其中，</p>
<ul>
<li>第一个&ldquo;=?&rdquo;与&ldquo;?&rdquo;中间的部分指定了字符编码，在这个例子中指定的是 GB2312。</li>
<li>&ldquo;?&rdquo;与&ldquo;?&rdquo;中间的&ldquo;B&rdquo;代表 Base64。如果是&ldquo;Q&rdquo;则代表 Quoted-Printable。</li>
<li>最后&ldquo;?&rdquo;与&ldquo;?=&rdquo;之间的部分，就是经过 GB2312 转化成字节串，再经过 Base64 转化后的标题内容。</li>
</ul>
<p>如果&ldquo;传输编码&rdquo;改为 Quoted-Printable，同样，如果标题内容为 &quot;中&quot;：</p>
<table cellspacing="0" border="1" bgcolor="#eeeeee" width="100%" cellpadding="6">
<tbody>
<tr>
<td class="code"><span class="rem">// 正确的标题格式</span>
<br />
                Subject: <span style="background-color: #ffff00;">=?</span>
GB2312<span style="background-color: #ffff00;">?Q?</span>
=D6=D0<span style="background-color: #ffff00;">?=</span>
</td>
</tr>
</tbody>
</table>
<p>如果阅读邮件时出现乱码，一般是因为&ldquo;字符编码&rdquo;或&ldquo;传输编码&rdquo;指定有误，或者是没有指定。比如，有的发邮件组件在发送邮件时，标题 &quot;中&quot;：</p>
<table cellspacing="0" border="1" bgcolor="#eeeeee" width="100%" cellpadding="6">
<tbody>
<tr>
<td class="code"><span class="rem">// 错误的标题格式</span>
<br />
                Subject: <span style="background-color: #ffff00;">=?</span>
<span style="color: #ff0000;">ISO-8859-1</span>
<span style="background-color: #ffff00;">?Q?</span>
=D6=D0<span style="background-color: #ffff00;">?=</span>
</td>
</tr>
</tbody>
</table>
<p>这样的表示，实际上是明确指明了标题为 [0x00D6, 0x00D0]，即 &quot;&Ouml;&ETH;&quot;，而不是 &quot;中&quot;。</p>
<table cellspacing="0" border="0" width="100%" cellpadding="0">
<tbody>
<tr valign="top">
<td align="right" width="100%"><img src="http://www.regexlab.com/images/blue_rule.gif" border="0" height="1" alt="" width="100%" />
</td>
</tr>
<tr valign="top">
<td align="right" width="100%">
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr align="right">
<td>
<table cellspacing="0" border="0" cellpadding="0">
<tbody>
<tr>
<td valign="center"><img src="http://www.regexlab.com/images/u_bold.gif" border="0" height="16" alt="" width="16" />
</td>
<td align="right" valign="top">
<p><a href="http://www.regexlab.com/zh/encoding.htm#main">回页首</a>
</p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<h4><a name="correct"></a>
4. 几种错误理解的纠正</h4>
<h5>误解：&ldquo;ISO-8859-1 是国际编码？&rdquo;</h5>
<p>非
也。iso-8859-1 只是单字节字符集中最简单的一种，也就是&ldquo;字节编号&rdquo;与&ldquo;UNICODE
字符编号&rdquo;一致的那种编码规则。当我们要把一个&ldquo;字节串&rdquo;转化成&ldquo;字符串&rdquo;，而又不知道它是哪一种 ANSI
编码时，先暂时地把&ldquo;每一个字节&rdquo;作为&ldquo;一个字符&rdquo;进行转化，不会造成信息丢失。然后再使用 bytes =
string.getBytes(&quot;iso-8859-1&quot;) 的方法可恢复到原始的字节串。</p>
<h5>误解：&ldquo;Java 中，怎样知道某个字符串的内码？&rdquo;</h5>
<p>Java 中，字符串类 java.lang.String 处理的是 UNICODE 字符串，不是 ANSI 字符串。我们只需要把字符串作为&ldquo;抽象的符号的串&rdquo;来看待。因此不存在字符串的内码的问题。</p>
          <br/>
          <span style="color:red;">
            <a href="http://coffeebar.javaeye.com/blog/206336#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 20 Jun 2008 15:55:20 +0800</pubDate>
        <link>http://coffeebar.javaeye.com/blog/206336</link>
        <guid>http://coffeebar.javaeye.com/blog/206336</guid>
      </item>
      <item>
        <title>Oracle 常用命令</title>
        <author>alfred.w</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coffeebar.javaeye.com">alfred.w</a>&nbsp;
          链接：<a href="http://coffeebar.javaeye.com/blog/198678" style="color:red;">http://coffeebar.javaeye.com/blog/198678</a>&nbsp;
          发表时间: 2008年05月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <ol>
<li><span style="font-family: 宋体;">查看服务端字符集：select userenv('language') from dual</span>
</li>
<li><span style="font-family: 宋体;">建立tablespace:<span style="color: #000000; font-family: 宋体;">create tablespace test1 datafile 
'/infowarelab/oradata/test1.dbf' size 10M;</span>
</span>
</li>
<li><span style="font-family: 宋体;"><span style="color: #000000; font-family: 宋体;">建立用户：</span>
create user tom identified by tom default tablespace test1 ;</span>
</li>
<li><span style="font-family: 宋体;">授权：<span style="color: #000000; font-family: 宋体;">grant connect,resource to smart 
identified by smart;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; grant dba to smart;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; grant create any 
table to tom;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; grant create any trigger to tom;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; grant 
create any sequence to tom;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; grant create any procedure to tom;</span>
</span>
</li>
</ol>
          <br/>
          <span style="color:red;">
            <a href="http://coffeebar.javaeye.com/blog/198678#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 30 May 2008 15:10:18 +0800</pubDate>
        <link>http://coffeebar.javaeye.com/blog/198678</link>
        <guid>http://coffeebar.javaeye.com/blog/198678</guid>
      </item>
      <item>
        <title>struts2 使用 jakarta 上传文件时commons fileupload的异常捕捉</title>
        <author>alfred.w</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coffeebar.javaeye.com">alfred.w</a>&nbsp;
          链接：<a href="http://coffeebar.javaeye.com/blog/197345" style="color:red;">http://coffeebar.javaeye.com/blog/197345</a>&nbsp;
          发表时间: 2008年05月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>问题：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struts2 使用jakarta 上传文件时，如果上传文件的大小超出commons fileupload(jakarta上传文件还是依赖commons-fileupload)设置的大小就会在进入action以前抛出异常.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果想返回用户的输入界面（input）,那么页面原来的参数会丢失。</p>
<p>首先看一下struts2 执行一个action的过程</p>
<p>1.&nbsp; 将用户请求发给org.apache.struts2.dispatcher.Dispatcher，<br />
&nbsp;&nbsp;&nbsp;&nbsp; wrapRequest(HttpServletRequest request, ServletContext servletContext)&nbsp; 方法会判断是否&quot;multipart/form-data&quot;，如果是建立一个multiPartRequest 的实例，并且建立MultiPartRequestWrapper</p>
<div class="quote_title"> 写道</div>
<div class="quote_div">...if (content_type != null &amp;&amp; content_type.indexOf(&quot;multipart/form-data&quot;) != -1) {<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; MultiPartRequest multi = getContainer().getInstance(MultiPartRequest.class);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request = new MultiPartRequestWrapper(multi, request, getSaveDir(servletContext));<br />
        } else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request = new StrutsRequestWrapper(request);<br />
        }</div>
&nbsp;
<p>2. 建立 MultiPartRequestWrapper 时解析(parse) request,</p>
<pre name="code" class="java">public void parse(HttpServletRequest servletRequest, String saveDir)
            throws IOException {
        DiskFileItemFactory fac = new DiskFileItemFactory();
        // Make sure that the data is written to file
        fac.setSizeThreshold(0);
        if (saveDir != null) {
            fac.setRepository(new File(saveDir));
        }

        // Parse the request
        try {
            ServletFileUpload upload = new ServletFileUpload(fac);
            upload.setSizeMax(maxSize);
            //upload 解析request并取得页面参数
 	    List items = upload.parseRequest(createRequestContext(servletRequest));
 		......
	    
</pre>
<p>&nbsp;3.我们看一下ServletFileUpload(commons-fileupload v1.1.1) 的parseRequest做了什么</p>
<pre name="code" class="java"> public List /* FileItem */ parseRequest(RequestContext ctx)
            throws FileUploadException {
        if (ctx == null) {
            throw new NullPointerException(&quot;ctx parameter&quot;);
        }

        ArrayList items = new ArrayList();
        String contentType = ctx.getContentType();

        if ((null == contentType)
            || (!contentType.toLowerCase().startsWith(MULTIPART))) {
            throw new InvalidContentTypeException(
                &quot;the request doesn't contain a &quot;
                + MULTIPART_FORM_DATA
                + &quot; or &quot;
                + MULTIPART_MIXED
                + &quot; stream, content type header is &quot;
                + contentType);
        }
        int requestSize = ctx.getContentLength();
        
        if (requestSize == -1) {
            throw new UnknownSizeException(
                &quot;the request was rejected because its size is unknown&quot;);
        }
        //关键就这里了，大小超出的异常，这里是所有上传文件合计的大小，如果超出就抛出异常
        //这时上层是拿不到保存参数的items的
        if (sizeMax &gt;= 0 &amp;&amp; requestSize &gt; sizeMax) {
            throw new SizeLimitExceededException(
                &quot;the request was rejected because its size (&quot; + requestSize
                + &quot;) exceeds the configured maximum (&quot; + sizeMax + &quot;)&quot;,
                requestSize, sizeMax);
        }

        String charEncoding = headerEncoding;
        if (charEncoding == null) {
            charEncoding = ctx.getCharacterEncoding();
        }

        try {
            byte[] boundary = getBoundary(contentType);
            if (boundary == null) {
                throw new FileUploadException(
                        &quot;the request was rejected because &quot;
                        + &quot;no multipart boundary was found&quot;);
            }

            InputStream input = ctx.getInputStream();

            MultipartStream multi = new MultipartStream(input, boundary);
            multi.setHeaderEncoding(charEncoding);

            boolean nextPart = multi.skipPreamble();
            while (nextPart) {
                Map headers = parseHeaders(multi.readHeaders());
                String fieldName = getFieldName(headers);
                if (fieldName != null) {
                    String subContentType = getHeader(headers, CONTENT_TYPE);
                    if (subContentType != null &amp;&amp; subContentType
                        .toLowerCase().startsWith(MULTIPART_MIXED)) {
                        // Multiple files.
                        byte[] subBoundary = getBoundary(subContentType);
                        multi.setBoundary(subBoundary);
                        boolean nextSubPart = multi.skipPreamble();
                        while (nextSubPart) {
                            headers = parseHeaders(multi.readHeaders());
                            if (getFileName(headers) != null) {
                                FileItem item =
                                        createItem(headers, false);
                                OutputStream os = item.getOutputStream();
                                try {
                                    multi.readBodyData(os);
                                } finally {
                                    os.close();
                                }
                                items.add(item);
                            } else {
                                // Ignore anything but files inside
                                // multipart/mixed.
                                multi.discardBodyData();
                            }
                            nextSubPart = multi.readBoundary();
                        }
                        multi.setBoundary(boundary);
                    } else {
                        FileItem item = createItem(headers,
                                getFileName(headers) == null);
                        OutputStream os = item.getOutputStream();
                        try {
                            multi.readBodyData(os);
                        } finally {
                            os.close();
                        }
                        items.add(item);
                    }
                } else {
                    // Skip this part.
                    multi.discardBodyData();
                }
                nextPart = multi.readBoundary();
            }
        } catch (IOException e) {
            throw new FileUploadException(
                &quot;Processing of &quot; + MULTIPART_FORM_DATA
                    + &quot; request failed. &quot; + e.getMessage());
        }

        return items;
    }</pre>
<p>&nbsp;4.这之后才开始逐个进入interceptor,见DefaultActionInvocation.invoke()</p>
<pre name="code" class="java">....
//递归interceptor
if (interceptors.hasNext()) {
    			final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
    			UtilTimerStack.profile(&quot;interceptor: &quot;+interceptor.getName(), 
    					new UtilTimerStack.ProfilingBlock&lt;String&gt;() {
							public String doProfiling() throws Exception {
				    			resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
				    			return null;
							}
    			});
    		} else {
                        //如果有errors,resultCode会得到‘input’
    			resultCode = invokeActionOnly();
    		}
...
</pre>
<p>&nbsp;5.我们的目标就是返回input并且保留页面原来的参数，那么就要不要让ServletFileUpload抛出异常，并且要让strusts使用我们自己的jakart.</p>
<p>&nbsp;6.写自己的ServletFileUpload</p>
<pre name="code" class="java">/*
 * Copyright 2001-2005 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.infowarelab.newcentury.web.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.MultipartStream;
import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.servlet.ServletRequestContext;
import org.apache.log4j.Logger;

/**
  * come from commons-fileupload
  * @author alfred
 */
public class ServletFileUpload extends FileUpload {

	// ---------------------------------------------------------- Class methods

	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger.getLogger(ServletFileUpload.class);
	private List&lt;String&gt; errors = new ArrayList&lt;String&gt;();

	/**
	 * Constructs an uninitialised instance of this class. A factory must be
	 * configured, using &lt;code&gt;setFileItemFactory()&lt;/code&gt;, before attempting
	 * to parse requests.
	 * 
	 * @see FileUpload#FileUpload(FileItemFactory)
	 */
	public ServletFileUpload() {
		super();
	}

	/**
	 * Constructs an instance of this class which uses the supplied factory to
	 * create &lt;code&gt;FileItem&lt;/code&gt; instances.
	 * 
	 * @see FileUpload#FileUpload()
	 */
	public ServletFileUpload(FileItemFactory fileItemFactory) {
		super(fileItemFactory);
	}
	/**
	 * overide parseRequest
	 */
	public List /* FileItem */parseRequest(RequestContext ctx) throws FileUploadException {
		if (ctx == null) {
			throw new NullPointerException(&quot;ctx parameter&quot;);
		}

		ArrayList items = new ArrayList();
		String contentType = ctx.getContentType();

		if ((null == contentType) || (!contentType.toLowerCase().startsWith(MULTIPART))) {
			throw new InvalidContentTypeException(&quot;the request doesn't contain a &quot; + MULTIPART_FORM_DATA + &quot; or &quot;
					+ MULTIPART_MIXED + &quot; stream, content type header is &quot; + contentType);
		}
		int requestSize = ctx.getContentLength();

		if (requestSize == -1) {
			// throw new UnknownSizeException(
			// &quot;the request was rejected because its size is unknown&quot;);
			logger.error(&quot;the request was rejected because its size is unknown&quot;);
			errors.add(&quot;the request was rejected because its size is unknown&quot;);
		}

		String charEncoding = getHeaderEncoding();
		if (charEncoding == null) {
			charEncoding = ctx.getCharacterEncoding();
		}

		try {
			byte[] boundary = getBoundary(contentType);
			if (boundary == null) {
				// throw new FileUploadException(
				// &quot;the request was rejected because &quot;
				// + &quot;no multipart boundary was found&quot;);
				logger.error(&quot;the request was rejected because no multipart boundary was found&quot;);
				errors.add(&quot;the request was rejected because no multipart boundary was found&quot;);
			}

			InputStream input = ctx.getInputStream();

			MultipartStream multi = new MultipartStream(input, boundary);
			multi.setHeaderEncoding(charEncoding);

			boolean nextPart = multi.skipPreamble();
			while (nextPart) {
				Map headers = parseHeaders(multi.readHeaders());
				String fieldName = getFieldName(headers);
				if (fieldName != null) {
					String subContentType = getHeader(headers, CONTENT_TYPE);
					if (subContentType != null &amp;&amp; subContentType.toLowerCase().startsWith(MULTIPART_MIXED)) {
						// Multiple files.
						byte[] subBoundary = getBoundary(subContentType);
						multi.setBoundary(subBoundary);
						boolean nextSubPart = multi.skipPreamble();
						while (nextSubPart) {
							headers = parseHeaders(multi.readHeaders());
							if (getFileName(headers) != null) {
								FileItem item = createItem(headers, false);
								OutputStream os = item.getOutputStream();
								try {
									multi.readBodyData(os);
								} finally {
									os.close();
								}
								items.add(item);
							} else {
								// Ignore anything but files inside
								// multipart/mixed.
								multi.discardBodyData();
							}
							nextSubPart = multi.readBoundary();
						}
						multi.setBoundary(boundary);
					} else {
						FileItem item = createItem(headers, getFileName(headers) == null);
						OutputStream os = item.getOutputStream();
						try {
							multi.readBodyData(os);
						} finally {
							os.close();
						}
						items.add(item);
					}
				} else {
					// Skip this part.
					multi.discardBodyData();
				}
				nextPart = multi.readBoundary();
			}
			// remove SizeLimitExceededException
			if (getSizeMax() &gt;= 0 &amp;&amp; requestSize &gt; getSizeMax()) {
				// throw new SizeLimitExceededException(
				// &quot;the request was rejected because its size (&quot; + requestSize
				// + &quot;) exceeds the configured maximum (&quot; + getSizeMax() + &quot;)&quot;,
				// requestSize, getSizeMax());
				logger.error(&quot;the request was rejected because its size (&quot; + requestSize
						+ &quot;) exceeds the configured maximum (&quot; + getSizeMax() + &quot;)&quot;);
			}
		} catch (IOException e) {
			logger.error(&quot;Processing of &quot; + MULTIPART_FORM_DATA + &quot; request failed. &quot; + e.getMessage());
			errors.add(&quot;Processing of &quot; + MULTIPART_FORM_DATA + &quot; request failed. &quot; + e.getMessage());
			// throw new FileUploadException(
			// &quot;Processing of &quot; + MULTIPART_FORM_DATA
			// + &quot; request failed. &quot; + e.getMessage());
		} 
		
		return items;
	}

	/**
	 * @return the errors
	 */
	public List&lt;String&gt; getErrors() {
		return errors;
	}

	/**
	 * @param errors the errors to set
	 */
	public void setErrors(List&lt;String&gt; errors) {
		this.errors = errors;
	}

}
</pre>
&nbsp;
<p>7.copy org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest，只是import上面自己的ServletFileUpload.这样就可以保存页面的所有参数了。</p>
<p>8.更改struts配置文件加入你自己的JakartaMultiReques</p>
<pre name="code" class="xml"> &lt;bean type=&quot;org.apache.struts2.dispatcher.multipart.MultiPartRequest&quot; 
name=&quot;jakarta_yourself&quot; 
        class=&quot;com.xxxxx.util.JakartaMultiPartRequest&quot; 
scope=&quot;default&quot; optional=&quot;true&quot; /&gt;</pre>
<p>&nbsp;9.更改struts.properties</p>
<p>struts.multipart.parser=jakarta_yourself</p>
<p>10.就OK啦</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://coffeebar.javaeye.com/blog/197345#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 27 May 2008 10:43:55 +0800</pubDate>
        <link>http://coffeebar.javaeye.com/blog/197345</link>
        <guid>http://coffeebar.javaeye.com/blog/197345</guid>
      </item>
      <item>
        <title>osgi实战，学习笔记</title>
        <author>alfred.w</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coffeebar.javaeye.com">alfred.w</a>&nbsp;
          链接：<a href="http://coffeebar.javaeye.com/blog/192241" style="color:red;">http://coffeebar.javaeye.com/blog/192241</a>&nbsp;
          发表时间: 2008年05月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>很久就了解了一些OSGI.<br />
今天翻到bluedavy的osgi实战。<br />
1.osgi微核机制。不会因为bundle而影响系统稳定，使bundle的热插拔成为可能。<br />
2.规范的可积累的模块，热插拔功能一直是大家期望的。<br />
<br />
实例<br />
1.Eclipse 3.0开始使用微核+系统插件+应用插件。<br />
2.ＢＭＷ应用控制系统<br />
<br />
开源支持<br />
１.Equinox <br />
2.felix ,apache 的一级项目,oscar演变而来，目前还比较活跃<br />
<br />
Begin.....<br />
Bundle通过实现BundleActivator接口去控制其生命周期，在Activator中编写Bundle启动、停止时所需要进行的工作.<br /></p>
<ol>
<li>由于是基于equinox,下载目前稳定版eclipse-equinox-3.3.2。</li>
</ol>
<p>建议以plugin的方式安装，而不要直接拷贝的eclipse的plugin.<br />
<br />
&nbsp;&nbsp; 2.按照实战提到，运行，不过3.3.2没有发现org.eclipse.equinox.servlet.api 这个包，待续。。<br />
&nbsp;&nbsp; 3.可以看到osgi&gt;&nbsp;&nbsp; input ss ,真神奇啊。。。<br />
&nbsp;&nbsp; 4.建立自己的plugin project,就是一个bundle.<br />
&nbsp;&nbsp; 5.建立一个validator interface.<br />
&nbsp;&nbsp; 6.<span style="font-weight: bold;">OSGI框架中通过Export-package元数据来标识Bundle对外提供的package.<br />
&nbsp;&nbsp;</span>
 7.对实例就不多介绍了。<br />
&nbsp;&nbsp; 8.Core Framework<br />
<img src="http://1802.img.pp.sohu.com.cn/images/blog/2008/5/9/11/19/11a7007406d.jpg" border="0" alt="" style="margin: 0px auto 10px; display: block; text-align: center;" />
<br />
&nbsp;&nbsp;&nbsp; 8.osgi 的classloader。。<br />
&nbsp;&nbsp;&nbsp; 9.about lifecycle layer,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; install,analyse,start,stop,uninstall.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不要在activator 加载过多的东西。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OSGI 通过bundle,bundelContext两个对象来实现对bundle的管理，通过监听机制监听bundle的状态。<br />
&nbsp;&nbsp;&nbsp; 10.about service layer:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-weight: bold;">Declarative Services提供了更好的服务注册、获取、监听等方式，使得其成为了OSGI R4中的重要角色，并由此替代了Service Layer。</span>
<br />
&nbsp;&nbsp;&nbsp; 11.StartLevel Service<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bundle的startLevel可以设置。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StartLevel Service的适用场景：安全模式，Splash Screen，处理不稳定的Bundles，高优先级的Bundles.<br />
&nbsp;&nbsp;&nbsp; <span style="font-weight: bold;">12.Declarative Services</span>
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 后面详细学习一下。<br />
&nbsp;&nbsp;&nbsp; 13.Configuration Admin Service<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 它用于动态的管理Bundle的配置的属性<br />
&nbsp;&nbsp;&nbsp; 14.Event Admin Service<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OSGI提供了Event Admin Service以方便开发人员实现自定义的事件发布和事件处理，和Java的事件处理模型基本是一致的。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://coffeebar.javaeye.com/blog/192241#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 12 May 2008 14:57:12 +0800</pubDate>
        <link>http://coffeebar.javaeye.com/blog/192241</link>
        <guid>http://coffeebar.javaeye.com/blog/192241</guid>
      </item>
      <item>
        <title>John Resig Speaks on Future Directions for jQuery </title>
        <author>alfred.w</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coffeebar.javaeye.com">alfred.w</a>&nbsp;
          链接：<a href="http://coffeebar.javaeye.com/blog/191907" style="color:red;">http://coffeebar.javaeye.com/blog/191907</a>&nbsp;
          发表时间: 2008年05月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>Posted by Nick Laiacona  on May 08, 2008 04:06 PM<br />
<br />
jQuery creator and Mozilla Javascript Evangelist John Resig recently posted a video presentation outlining future release plans for the jQuery project and highlighting some exciting new Javascript features that will be coming soon to browsers. Here are some of the highlights of his presentation:<br />
jQuery Core<br />
<br />
jQuery 1.2.4 is scheduled for release in mid-May. It will include the Dimensions plugin as part of jQuery Core. This popular plugin provides cross-browser compatible methods of dealing with the dimensions of page elements. jQuery 1.2.4 will also include speed optimizations for event handling methods. John Resig reports that drag-and-drop operations are now 3x faster in the new version. jQuery 1.3 is the next planned for this summer and will include speed improvements to the Selector Engine and DOM Manipulation. The jQuery project is also planning a live conference for this fall.<br />
jQuery UI 1.5<br />
<br />
jQuery UI 1.5 will be a complete overhaul from jQuery UI 1.0 both in features and API. It will be accompanied by new documentation, demos, and a brand new website. A new sub-library called Enchant is in the works which will be a full set of animations and effects that integrates seamlessly with jQuery.<br />
Upcoming Javascript Features<br />
<br />
Resig then outlined a number of interesting Javascript features in the works at Mozilla and other browser manufacturers:<br />
<br />
    * Speed improvements are coming in two commonly used methods: getElementsByClassName() in Firefox 3 and Safari and querySelectorAll() in Safari, Opera, and IE 8 and Firefox.<br />
    * ARIA Accessible Ajax is a coming standard which will allow Javascript developers to directly command the user's screen reader. A lack of screen reader accessibility is a common criticism of AJAX enabled applications.<br />
    * CSS 3 is coming to fruition and browsers are finally getting solid implementations of its feature set.<br />
    * postMessage() from HTML 5 specification will allow users to communicate across domains to allow for secure cross domain communications.<br />
    * Native JSON support is coming in Firefox 3, Javascript 1.9. This will allow faster serialization and de-serialization of JSON objects.<br />
    * Function.prototype.bind() will provide built in language support for binding functions with objects.<br />
    * ISO Date() parsing will provide a cleaner way to deal with dates that works on both the browser and the server.<br />
<br />
Appeal to Standards Bodies<br />
<br />
Resig caps off his presentation with an appeal to standards bodies. He says that they need to take more of a cue from the Javascript libraries and formalize proven features from the library space, while maintaining usability. He also makes an appeal for browser makers to come together around a standard codex for the &lt;audio&gt; and &lt;video&gt; tags.</p>
<p>&nbsp;</p>
<p><a href="http://jquery.com/" title="jQuery" id="l-qv">Query</a>
作者和Mozilla JavaScript传道者<a href="http://ejohn.org/" title="John Resig" id="b_tz">John Resig</a>
近日在一次<a href="http://vimeo.com/984675" title="视频演示" id="xrki">视频演示</a>
中为大家描绘了jQuery项目未来的发布计划，并强调了即将出现在浏览器中的激动人心的JavaScript新特性。下面是演示中的一些亮点：
</p>
<p id="j.c224"><span id="da_i1"><strong id="ep6.2">jQuery 核心</strong>
</span>
</p>
<p id="j.c224">jQuery 1.2.4计划在五月中旬发布。这次新的内核中将包括<a href="http://plugins.jquery.com/project/dimensions" title="尺寸插件" id="ie7g">尺寸插件</a>
。
这个流行的插件提供了一些跨浏览器兼容的方法，来处理页面元素的尺寸问题。jQuery1.2.4还将包括对事件处理方法的加速优化。Jhon
Resig的报告还提到新版的拖放（Drag and
drop）的性能将有三倍的提升。jQuery1.3版本计划在今年夏天发布，包括对选择器引擎（Selector
Engine）和DOM操控处理的速度提升。jQuery项目还计划在今年秋天举行一次现场的技术大会。</p>
<p id="j.c224"><strong id="ep6.3">jQuery UI 1.5</strong>
</p>
<p id="j.c224">jQuery UI 1.5将会是在jQuery UI 1.0的特性和API之上一次彻底的升级。它将会带有新文档、演示以及完全崭新的站点。其中包含一个新的叫做Enchant的子库，它将是一个可以和jQuery无缝集成的完整的动画和特效集合。<br id="vtcm9" />
 <br id="vtcm10" />
 <span id="u::50"><strong id="ep6.4">Javascript的新特性</strong>
</span>
<br id="vtcm11" />
 <br id="vtcm12" />
Resig接着列出了大量在Mozila和其他浏览器厂商的工作中完成的非常有趣的JavaScript特性：</p>
<ul id="kfp70">
<li id="kfp71">对两个经常使用的方法的速度提升：Firefox 3和Safari中的getElementsByClassName()和Safari、Opera、IE 8和Firefox支持的querySelectorAll()。</li>
<li id="kfp72">ARIA 可访问Ajax即将成为，它将允许Javascript开发者直接控制用户的屏幕阅读器（user's screen reader）。 jAjax应用程序常常因缺少屏幕阅读器而遭人诟病。</li>
<li id="kfp72">CSS 3即将完成。浏览器最终将实现对它的全部特性的支持。</li>
<li id="kfp72">HMTL 5规范中postMessage()将允许用户进行安全的跨域通讯。</li>
<li id="kfp72">Firefox 3和JavaScript1.9将提供原生JSON的支持。这将更快地实现JSON对象的序列化和反序列化。</li>
<li id="kfp72">Function.prototype.bind()将为绑定函数到对象提供语言级别的内建支持。</li>
<li id="kfp72">ISO Date()解析将提供一种更干净的方式来处理浏览器和服务器上的日期。<br id="vtcm20" />
   </li>
</ul>
<p id="j.c224"> <br id="vtcm22" />
 <span id="vtcm23"><strong id="ep6.5">呼吁标准体</strong>
</span>
<br id="vtcm24" />
</p>
<p id="j.c224">Resig在报告结束时呼吁建立标准体（Standards
Bodies）。他说他们应该在保持可使用性的同时，参照更多的JavaScript库，并将库空间中已确认的特性正式化。他还呼吁浏览器厂商一起来为
&lt;audio&gt;和&lt;video&gt;标签定义标准的规范。<strong id="j.c225"><br id="vtcm28" />
 </strong>
</p>
<p>
<strong id="j.c225">查看英文原文：</strong>
<a href="http://www.infoq.com/news/2008/05/resig-video" title="http://www.infoq.com/news/2008/05/resig-video" id="o38-">John Resig Speaks on Future Directions for jQuery and Javascript</a>
</p>
<p><br />
<br />
source:<a href="http://www.infoq.com/news/2008/05/resig-video" target="_blank">http://www.infoq.com/news/2008/05/resig-video</a>
</p>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://coffeebar.javaeye.com/blog/191907#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 11 May 2008 11:34:08 +0800</pubDate>
        <link>http://coffeebar.javaeye.com/blog/191907</link>
        <guid>http://coffeebar.javaeye.com/blog/191907</guid>
      </item>
  </channel>
</rss>