﻿<?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-人生就是努力!-文章分类-java基础</title><link>http://www.blogjava.net/jesenblog/category/30190.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 23 May 2008 09:08:05 GMT</lastBuildDate><pubDate>Fri, 23 May 2008 09:08:05 GMT</pubDate><ttl>60</ttl><item><title>Java1.5泛型指南中文版(Java1.5 Generic Tutorial)</title><link>http://www.blogjava.net/jesenblog/articles/202439.html</link><dc:creator>白露</dc:creator><author>白露</author><pubDate>Fri, 23 May 2008 08:33:00 GMT</pubDate><guid>http://www.blogjava.net/jesenblog/articles/202439.html</guid><wfw:comment>http://www.blogjava.net/jesenblog/comments/202439.html</wfw:comment><comments>http://www.blogjava.net/jesenblog/articles/202439.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesenblog/comments/commentRss/202439.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesenblog/services/trackbacks/202439.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 目&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 录摘要和关键字1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 介绍2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 定义简单的泛型3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 泛型和子类继承4.&nbs...&nbsp;&nbsp;<a href='http://www.blogjava.net/jesenblog/articles/202439.html'>阅读全文</a><img src ="http://www.blogjava.net/jesenblog/aggbug/202439.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesenblog/" target="_blank">白露</a> 2008-05-23 16:33 <a href="http://www.blogjava.net/jesenblog/articles/202439.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 理论和实践: 了解泛型(ibm文章)</title><link>http://www.blogjava.net/jesenblog/articles/202414.html</link><dc:creator>白露</dc:creator><author>白露</author><pubDate>Fri, 23 May 2008 07:26:00 GMT</pubDate><guid>http://www.blogjava.net/jesenblog/articles/202414.html</guid><wfw:comment>http://www.blogjava.net/jesenblog/comments/202414.html</wfw:comment><comments>http://www.blogjava.net/jesenblog/articles/202414.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesenblog/comments/commentRss/202414.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesenblog/services/trackbacks/202414.html</trackback:ping><description><![CDATA[<blockquote>JDK 5.0 中增加的泛型类型，是 Java 语言中类型安全的一次重要改进。但是，对于初次使用泛型类型的用户来说，泛型的某些方面看起来可能不容易明白，甚至非常奇怪。在本月的&#8220;<em>Java 理论和实践</em>&#8221;中，Brian Goetz 分析了束缚第一次使用泛型的用户的常见陷阱。您可以通过<a href="javascript:void forumWindow()" cmimpressionsent="1">讨论论坛</a>与作者和其他读者分享您对本文的看法。（也可以单击本文顶端或底端的<strong>讨论</strong>来访问这个论坛。）</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p>表面上看起来，无论语法还是应用的环境（比如容器类），泛型类型（或者泛型）都类似于 C++ 中的模板。但是这种相似性仅限于表面，Java 语言中的泛型基本上完全在编译器中实现，由编译器执行类型检查和类型推断，然后生成普通的非泛型的字节码。这种实现技术称为<em>擦除（erasure）</em>（编译器使用泛型类型信息保证类型安全，然后在生成字节码之前将其清除），这项技术有一些奇怪，并且有时会带来一些令人迷惑的后果。虽然范型是 Java 类走向类型安全的一大步，但是在学习使用泛型的过程中几乎肯定会遇到头痛（有时候让人无法忍受）的问题。 </p>
<p><em><strong>注意：</strong>本文假设您对 JDK 5.0 中的范型有基本的了解。</em> </p>
<p><a name="1.0"><span class="atitle">泛型不是协变的</span></a></p>
<p>虽然将集合看作是数组的抽象会有所帮助，但是数组还有一些集合不具备的特殊性质。Java 语言中的数组是协变的（covariant），也就是说，如果 <code>Integer</code> 扩展了 <code>Number</code>（事实也是如此），那么不仅 <code>Integer</code> 是 <code>Number</code>，而且 <code>Integer[]</code> 也是 <code>Number[]</code>，在要求 <code>Number[]</code> 的地方完全可以传递或者赋予 <code>Integer[]</code>。（更正式地说，如果 <code>Number</code> 是 <code>Integer</code> 的超类型，那么 <code>Number[]</code> 也是 <code>Integer[]</code> 的超类型）。您也许认为这一原理同样适用于泛型类型 —— <code>List&lt;Number&gt;</code> 是 <code>List&lt;Integer&gt;</code> 的超类型，那么可以在需要 <code>List&lt;Number&gt;</code> 的地方传递 <code>List&lt;Integer&gt;</code>。不幸的是，情况并非如此。 </p>
<p>不允许这样做有一个很充分的理由：这样做将破坏要提供的类型安全泛型。如果能够将 <code>List&lt;Integer&gt;</code> 赋给 <code>List&lt;Number&gt;</code>。那么下面的代码就允许将非 <code>Integer</code> 的内容放入 <code>List&lt;Integer&gt;</code>： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();
            List&lt;Number&gt; ln = li; // illegal
            ln.add(new Float(3.1415));
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>因为 <code>ln</code> 是 <code>List&lt;Number&gt;</code>，所以向其添加 <code>Float</code> 似乎是完全合法的。但是如果 <code>ln</code> 是 <code>li</code> 的别名，那么这就破坏了蕴含在 <code>li</code> 定义中的类型安全承诺 —— 它是一个整数列表，这就是泛型类型不能协变的原因。 </p>
<p><a name="1.1"><span class="smalltitle">其他的协变问题</span></a></p>
<p>数组能够协变而泛型不能协变的另一个后果是，不能实例化泛型类型的数组（<code>new List&lt;String&gt;[3]</code> 是不合法的），除非类型参数是一个未绑定的通配符（<code>new List&lt;?&gt;[3]</code> 是合法的）。让我们看看如果允许声明泛型类型数组会造成什么后果： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">List&lt;String&gt;[] lsa = new List&lt;String&gt;[10]; // illegal
            Object[] oa = lsa;  // OK because List&lt;String&gt; is a subtype of Object
            List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();
            li.add(new Integer(3));
            oa[0] = li;
            String s = lsa[0].get(0);
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>最后一行将抛出 <code>ClassCastException</code>，因为这样将把 <code>List&lt;Integer&gt;</code> 填入本应是 <code>List&lt;String&gt;</code> 的位置。因为数组协变会破坏泛型的类型安全，所以不允许实例化泛型类型的数组（除非类型参数是未绑定的通配符，比如 <code>List&lt;?&gt;</code>）。 </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="middle"><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.ibm.com/developerworks/cn/java/j-jtp01255.html#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="2.0"><span class="atitle">构造延迟</span></a></p>
<p>因为可以擦除功能，所以 <code>List&lt;Integer&gt;</code> 和 <code>List&lt;String&gt;</code> 是同一个类，编译器在编译 <code>List&lt;V&gt;</code> 时只生成一个类（和 C++ 不同）。因此，在编译 <code>List&lt;V&gt;</code> 类时，编译器不知道 <code>V</code> 所表示的类型，所以它就不能像知道类所表示的具体类型那样处理 <code>List&lt;V&gt;</code> 类定义中的类型参数（<code>List&lt;V&gt;</code> 中的 <code>V</code>）。 </p>
<p>因为运行时不能区分 <code>List&lt;String&gt;</code> 和 <code>List&lt;Integer&gt;</code>（运行时都是 <code>List</code>），用泛型类型参数标识类型的变量的构造就成了问题。运行时缺乏类型信息，这给泛型容器类和希望创建保护性副本的泛型类提出了难题。 </p>
<p>比如泛型类 <code>Foo</code>： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">class Foo&lt;T&gt; {
            public void doSomething(T param) { ... }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>假设 <code>doSomething()</code> 方法希望复制输入的 <code>param</code> 参数，会怎么样呢？没有多少选择。您可能希望按以下方式实现 <code>doSomething()</code>： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">public void doSomething(T param) {
            T copy = new T(param);  // illegal
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>但是您不能使用类型参数访问构造函数，因为在编译的时候还不知道要构造什么类，因此也就不知道使用什么构造函数。使用泛型不能表达&#8220;<code>T</code> 必须拥有一个拷贝构造函数（copy constructor）&#8221;（甚至一个无参数的构造函数）这类约束，因此不能使用泛型类型参数所表示的类的构造函数。 </p>
<p><code>clone()</code> 怎么样呢？假设在 <code>Foo</code> 的定义中，<code>T</code> 扩展了 <code>Cloneable</code>： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">class Foo&lt;T extends Cloneable&gt; {
            public void doSomething(T param) {
            T copy = (T) param.clone();  // illegal
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>不幸的是，仍然不能调用 <code>param.clone()</code>。为什么呢？因为 <code>clone()</code> 在 <code>Object</code> 中是保护访问的，调用 <code>clone()</code> 必须通过将 <code>clone()</code> 改写公共访问的类引用来完成。但是重新声明 <code>clone()</code> 为 public 并不知道 <code>T</code>，因此克隆也无济于事。 </p>
<p><a name="2.1"><span class="smalltitle">构造通配符引用</span></a></p>
<p>因此，不能复制在编译时根本不知道是什么类的类型引用。那么使用通配符类型怎么样？假设要创建类型为 <code>Set&lt;?&gt;</code> 的参数的保护性副本。您知道 <code>Set</code> 有一个拷贝构造函数。而且别人可能曾经告诉过您，如果不知道要设置的内容的类型，最好使用 <code>Set&lt;?&gt;</code> 代替原始类型的 <code>Set</code>，因为这种方法引起的未检查类型转换警告更少。于是，可以试着这样写： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">class Foo {
            public void doSomething(Set&lt;?&gt; set) {
            Set&lt;?&gt; copy = new HashSet&lt;?&gt;(set);  // illegal
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>不幸的是，您不能用通配符类型的参数调用泛型构造函数，即使知道存在这样的构造函数也不行。不过您可以这样做： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">class Foo {
            public void doSomething(Set&lt;?&gt; set) {
            Set&lt;?&gt; copy = new HashSet&lt;Object&gt;(set);
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>这种构造不那么直观，但它是类型安全的，而且可以像 <code>new HashSet&lt;?&gt;(set)</code> 那样工作。 </p>
<p><a name="2.2"><span class="smalltitle">构造数组</span></a></p>
<p>如何实现 <code>ArrayList&lt;V&gt;</code>？假设类 <code>ArrayList</code> 管理一个 <code>V</code> 数组，您可能希望用 <code>ArrayList&lt;V&gt;</code> 的构造函数创建一个 <code>V</code> 数组： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">class ArrayList&lt;V&gt; {
            private V[] backingArray;
            public ArrayList() {
            backingArray = new V[DEFAULT_SIZE]; // illegal
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>但是这段代码不能工作 —— 不能实例化用类型参数表示的类型数组。编译器不知道 <code>V</code> 到底表示什么类型，因此不能实例化 <code>V</code> 数组。 </p>
<p>Collections 类通过一种别扭的方法绕过了这个问题，在 Collections 类编译时会产生类型未检查转换的警告。<code>ArrayList</code> 具体实现的构造函数如下： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">class ArrayList&lt;V&gt; {
            private V[] backingArray;
            public ArrayList() {
            backingArray = (V[]) new Object[DEFAULT_SIZE];
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>为何这些代码在访问 <code>backingArray</code> 时没有产生 <code>ArrayStoreException</code> 呢？无论如何，都不能将 <code>Object</code> 数组赋给 <code>String</code> 数组。因为泛型是通过擦除实现的，<code>backingArray</code> 的类型实际上就是 <code>Object[]</code>，因为 <code>Object</code> 代替了 <code>V</code>。这意味着：实际上这个类期望 <code>backingArray</code> 是一个 <code>Object</code> 数组，但是编译器要进行额外的类型检查，以确保它包含 <code>V</code> 类型的对象。所以这种方法很奏效，但是非常别扭，因此不值得效仿（甚至连泛型 Collections 框架的作者都这么说，请参阅<a href="http://www.ibm.com/developerworks/cn/java/j-jtp01255.html#resources" cmimpressionsent="1">参考资料</a>）。 </p>
<p>还有一种方法就是声明 <code>backingArray</code> 为 <code>Object</code> 数组，并在使用它的各个地方强制将它转化为 <code>V[]</code>。仍然会看到类型未检查转换警告（与上一种方法一样），但是它使一些未明确的假设更清楚了（比如 <code>backingArray</code> 不应逃避 <code>ArrayList</code> 的实现）。 </p>
<p><a name="2.3"><span class="smalltitle">其他方法</span></a></p>
<p>最好的办法是向构造函数传递类文字（<code>Foo.class</code>），这样，该实现就能在运行时知道 <code>T</code> 的值。不采用这种方法的原因在于向后兼容性 —— 新的泛型集合类不能与 Collections 框架以前的版本兼容。 </p>
<p>下面的代码中 <code>ArrayList</code> 采用了以下方法： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">public class ArrayList&lt;V&gt; implements List&lt;V&gt; {
            private V[] backingArray;
            private Class&lt;V&gt; elementType;
            public ArrayList(Class&lt;V&gt; elementType) {
            this.elementType = elementType;
            backingArray = (V[]) Array.newInstance(elementType, DEFAULT_LENGTH);
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>但是等一等！仍然有不妥的地方，调用 <code>Array.newInstance()</code> 时会引起未经检查的类型转换。为什么呢？同样是由于向后兼容性。<code>Array.newInstance()</code> 的签名是： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">public static Object newInstance(Class&lt;?&gt; componentType, int length)
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>而不是类型安全的： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">public static&lt;T&gt; T[] newInstance(Class&lt;T&gt; componentType, int length)
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>为何 <code>Array</code> 用这种方式进行泛化呢？同样是为了保持向后兼容。要创建基本类型的数组，如 <code>int[]</code>，可以使用适当的包装器类中的 <code>TYPE</code> 字段调用 <code>Array.newInstance()</code>（对于 <code>int</code>，可以传递 <code>Integer.TYPE</code> 作为类文字）。用 <code>Class&lt;T&gt;</code> 参数而不是 <code>Class&lt;?&gt;</code> 泛化 <code>Array.newInstance()</code>，对于引用类型有更好的类型安全，但是就不能使用 <code>Array.newInstance()</code> 创建基本类型数组的实例了。也许将来会为引用类型提供新的 <code>newInstance()</code> 版本，这样就两者兼顾了。 </p>
<p>在这里可以看到一种模式 —— 与泛型有关的很多问题或者折衷并非来自泛型本身，而是保持和已有代码兼容的要求带来的副作用。</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="middle"><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.ibm.com/developerworks/cn/java/j-jtp01255.html#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="3.0"><span class="atitle">泛化已有的类</span></a></p>
<p>在转化现有的库类来使用泛型方面没有多少技巧，但与平常的情况相同，向后兼容性不会凭空而来。我已经讨论了两个例子，其中向后兼容性限制了类库的泛化。 </p>
<p>另一种不同的泛化方法可能不存在向后兼容问题，这就是 <code>Collections.toArray(Object[])</code>。传入 <code>toArray()</code> 的数组有两个目的 —— 如果集合足够小，那么可以将其内容直接放在提供的数组中。否则，利用反射（reflection）创建相同类型的新数组来接受结果。如果从头开始重写 Collections 框架，那么很可能传递给 <code>Collections.toArray()</code> 的参数不是一个数组，而是一个类文字： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">interface Collection&lt;E&gt; {
            public T[] toArray(Class&lt;T super E&gt; elementClass);
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>因为 Collections 框架作为良好类设计的例子被广泛效仿，但是它的设计受到向后兼容性约束，所以这些地方值得您注意，不要盲目效仿。 </p>
<p>首先，常常被混淆的泛型 Collections API 的一个重要方面是 <code>containsAll()</code>、<code>removeAll()</code> 和 <code>retainAll()</code> 的签名。您可能认为 <code>remove()</code> 和 <code>removeAll()</code> 的签名应该是： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">interface Collection&lt;E&gt; {
            public boolean remove(E e);  // not really
            public void removeAll(Collection&lt;? extends E&gt; c);  // not really
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>但实际上却是： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">interface Collection&lt;E&gt; {
            public boolean remove(Object o);
            public void removeAll(Collection&lt;?&gt; c);
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>为什么呢？答案同样是因为向后兼容性。<code>x.remove(o)</code> 的接口表明&#8220;如果 <code>o</code> 包含在 <code>x</code> 中，则删除它，否则什么也不做。&#8221;如果 <code>x</code> 是一个泛型集合，那么 <code>o</code> 不一定与 <code>x</code> 的类型参数兼容。如果 <code>removeAll()</code> 被泛化为只有类型兼容时才能调用（<code>Collection&lt;? extends E&gt;</code>），那么在泛化之前，合法的代码序列就会变得不合法，比如： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">// a collection of Integers
            Collection c = new HashSet();
            // a collection of Objects
            Collection r = new HashSet();
            c.removeAll(r);
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>如果上述片段用直观的方法泛化（将 <code>c</code> 设为 <code>Collection&lt;Integer&gt;</code>，<code>r</code> 设为 <code>Collection&lt;Object&gt;</code>），如果 <code>removeAll()</code> 的签名要求其参数为 <code>Collection&lt;? extends E&gt;</code> 而不是 no-op，那么就无法编译上面的代码。泛型类库的一个主要目标就是不打破或者改变已有代码的语义，因此，必须用比从头重新设计泛型所使用类型约束更弱的类型约束来定义 <code>remove()</code>、<code>removeAll()</code>、<code>retainAll()</code> 和 <code>containsAll()</code>。 </p>
<p>在泛型之前设计的类可能阻碍了&#8220;显然的&#8221;泛型化方法。这种情况下就要像上例这样进行折衷，但是如果从头设计新的泛型类，理解 Java 类库中的哪些东西是向后兼容的结果很有意义，这样可以避免不适当的模仿。 </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="middle"><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.ibm.com/developerworks/cn/java/j-jtp01255.html#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="4.0"><span class="atitle">擦除的实现</span></a></p>
<p>因为泛型基本上都是在 Java 编译器中而不是运行库中实现的，所以在生成字节码的时候，差不多所有关于泛型类型的类型信息都被&#8220;擦掉&#8221;了。换句话说，编译器生成的代码与您手工编写的不用泛型、检查程序的类型安全后进行强制类型转换所得到的代码基本相同。与 C++ 不同，<code>List&lt;Integer&gt;</code> 和 <code>List&lt;String&gt;</code> 是同一个类（虽然是不同的类型但都是 <code>List&lt;?&gt;</code> 的子类型，与以前的版本相比，在 JDK 5.0 中这是一个更重要的区别）。 </p>
<p>擦除意味着一个类不能同时实现 <code>Comparable&lt;String&gt;</code> 和 <code>Comparable&lt;Number&gt;</code>，因为事实上两者都在同一个接口中，指定同一个 <code>compareTo()</code> 方法。声明 <code>DecimalString</code> 类以便与 <code>String</code> 与 <code>Number</code> 比较似乎是明智的，但对于 Java 编译器来说，这相当于对同一个方法进行了两次声明： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">public class DecimalString implements Comparable&lt;Number&gt;, Comparable&lt;String&gt; { ... } // nope
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>擦除的另一个后果是，对泛型类型参数是用强制类型转换或者 <code>instanceof</code> 毫无意义。下面的代码完全不会改善代码的类型安全性： </p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">public &lt;T&gt; T naiveCast(T t, Object o) { return (T) o; }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>编译器仅仅发出一个类型未检查转换警告，因为它不知道这种转换是否安全。<code>naiveCast()</code> 方法实际上根本不作任何转换，<code>T</code> 直接被替换为 <code>Object</code>，与期望的相反，传入的对象被强制转换为 <code>Object</code>。 </p>
<p>擦除也是造成上述构造问题的原因，即不能创建泛型类型的对象，因为编译器不知道要调用什么构造函数。如果泛型类需要构造用泛型类型参数来指定类型的对象，那么构造函数应该接受类文字（<code>Foo.class</code>）并将它们保存起来，以便通过反射创建实例。 </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="middle"><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.ibm.com/developerworks/cn/java/j-jtp01255.html#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="5.0"><span class="atitle">结束语</span></a></p>
<p>泛型是 Java 语言走向类型安全的一大步，但是泛型设施的设计和类库的泛化并非未经过妥协。扩展虚拟机指令集来支持泛型被认为是无法接受的，因为这会为 Java 厂商升级其 JVM 造成难以逾越的障碍。因此采用了可以完全在编译器中实现的擦除方法。类似地，在泛型 Java 类库时，保持向后兼容也为类库的泛化方式设置了很多限制，产生了一些混乱的、令人沮丧的结构（如 <code>Array.newInstance()</code>）。这并非泛型本身的问题，而是与语言的演化与兼容有关。但这些也使得泛型学习和应用起来更让人迷惑，更加困难。 </p>
<img src ="http://www.blogjava.net/jesenblog/aggbug/202414.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesenblog/" target="_blank">白露</a> 2008-05-23 15:26 <a href="http://www.blogjava.net/jesenblog/articles/202414.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>字符，字节和编码</title><link>http://www.blogjava.net/jesenblog/articles/191075.html</link><dc:creator>白露</dc:creator><author>白露</author><pubDate>Sun, 06 Apr 2008 10:22:00 GMT</pubDate><guid>http://www.blogjava.net/jesenblog/articles/191075.html</guid><wfw:comment>http://www.blogjava.net/jesenblog/comments/191075.html</wfw:comment><comments>http://www.blogjava.net/jesenblog/articles/191075.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesenblog/comments/commentRss/191075.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesenblog/services/trackbacks/191075.html</trackback:ping><description><![CDATA[<h4><a name="develop"></a>1. 编码问题的由来，相关概念的理解</h4>
<h5>1.1 字符与编码的发展</h5>
<p>从计算机对多国语言的支持角度看，大致可以分为三个阶段：</p>
<table cellspacing="0" cellpadding="3" border="0">
    <tbody>
        <tr>
            <td class="top_1">　</td>
            <td class="top_2" noWrap 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" noWrap>阶段一</td>
            <td class="con_2" noWrap align="center">ASCII</td>
            <td class="con_2">计算机刚开始只支持英语，其它语言不能够在计算机上存储和显示。</td>
            <td class="con_2">英文 DOS</td>
        </tr>
        <tr>
            <td class="con_1" noWrap>阶段二</td>
            <td class="con_2" noWrap 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" noWrap>阶段三</td>
            <td class="bot_2" noWrap 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）。比如，"Bob123" 在内存中为：</p>
<table style="font-size: 80%; color: #000080" cellspacing="5" cellpadding="0" border="0">
    <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 height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></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>。比如，"中文123" 在中文 Windows 95 内存中为7个字节，每个汉字占2个字节，每个英文和数字字符占1个字节：</p>
<table style="font-size: 80%; color: #000080" cellspacing="5" cellpadding="0" border="0">
    <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 height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#000080"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></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>。比如，字符串 "中文123" 在 Windows 2000 下，内存中实际存放的是 5 个序号：</p>
<table style="font-size: 80%; color: #000080" cellspacing="5" cellpadding="0" border="0">
    <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><font color="#808080">&nbsp;&nbsp;&nbsp;&nbsp; &#8592; 在 x86 CPU 中，低字节在前</font></td>
        </tr>
        <tr>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td bgcolor="#ff0000" colspan="2"><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></td>
            <td><img height="1" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/spacer.gif" width="1" border="0"  alt="" /></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" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <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" cellpadding="3" border="0">
    <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" noWrap align="center">字符</td>
            <td class="con_2">人们使用的记号，抽象意义上的一个符号。</td>
            <td class="con_2">'1', '中', 'a', '$', '￥', &#8230;&#8230;</td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">字节</td>
            <td class="con_2">计算机中存储数据的单元，一个8位的二进制数，是一个很具体的存储空间。</td>
            <td class="con_2">0x01, 0x45, 0xFA, &#8230;&#8230;</td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">ANSI<br />
            字符串</td>
            <td class="con_2">在内存中，如果&#8220;字符&#8221;是以 <strong>ANSI 编码</strong>形式存在的，一个字符可能使用一个字节或多个字节来表示，那么我们称这种字符串为 <strong>ANSI 字符串</strong>或者<strong>多字节字符串</strong>。</td>
            <td class="con_2">"中文123"<br />
            <span class="rem">（占7字节）</span></td>
        </tr>
        <tr>
            <td class="bot_1" noWrap align="center">UNICODE<br />
            字符串</td>
            <td class="bot_2">在内存中，如果&#8220;字符&#8221;是以在 UNICODE 中的序号存在的，那么我们称这种字符串为 <strong>UNICODE 字符串</strong>或者<strong>宽字节字符串</strong>。</td>
            <td class="bot_2">L"中文123"<br />
            <span class="rem">（占10字节）</span></td>
        </tr>
    </tbody>
</table>
<p>由于不同 ANSI 编码所规定的标准是不相同的，因此，对于一个给定的<strong>多字节字符串</strong>，我们必须知道它采用的是哪一种编码规则，才能够知道它包含了哪些&#8220;字符&#8221;。而对于 <strong>UNICODE 字符串</strong>来说，不管在什么环境下，它所代表的&#8220;字符&#8221;内容总是不变的。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <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 编码标准中，都只规定了各自语言所需的&#8220;字符&#8221;。比如：汉字标准（GB2312）中没有规定韩国语字符怎样存储。这些 ANSI 编码标准所规定的内容包含两层含义：</p>
<ol>
    <li>使用哪些字符。也就是说哪些汉字，字母和符号会被收入标准中。所包含&#8220;字符&#8221;的集合就叫做&#8220;<strong>字符集</strong>&#8221;。
    <li>规定每个&#8220;字符&#8221;分别用一个字节还是多个字节存储，用哪些字节来存储，这个规定就叫做&#8220;<strong>编码</strong>&#8221;。 </li>
</ol>
<p>各个国家和地区在制定编码标准的时候，&#8220;字符的集合&#8221;和&#8220;编码&#8221;一般都是同时制定的。因此，平常我们所说的&#8220;字符集&#8221;，比如：GB2312, GBK, JIS 等，除了有&#8220;字符的集合&#8221;这层含义外，同时也包含了&#8220;编码&#8221;的含义。</p>
<p>&#8220;<strong>UNICODE 字符集</strong>&#8221;包含了各种语言中使用到的所有&#8220;字符&#8221;。用来给 UNICODE 字符集编码的标准有很多种，比如：UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <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" cellpadding="3" border="0">
    <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" noWrap align="center">单字节字符编码</td>
            <td class="con_2">ISO-8859-1</td>
            <td class="con_2">最简单的编码规则，每一个字节直接作为一个 UNICODE 字符。比如，[0xD6, 0xD0] 这两个字节，通过 iso-8859-1 转化为字符串时，将直接得到 [0x00D6, 0x00D0] 两个 UNICODE 字符，即 "&#214;&#208;"。<br />
            <br />
            反之，将 UNICODE 字符串通过 iso-8859-1 转化为字节串时，只能正常转化 0~255 范围的字符。</td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">ANSI 编码</td>
            <td class="con_2">GB2312,<br />
            BIG5,<br />
            Shift_JIS,<br />
            ISO-8859-2 &#8230;&#8230;</td>
            <td class="con_2">把 UNICODE 字符串通过 ANSI 编码转化为&#8220;字节串&#8221;时，根据各自编码的规定，一个 UNICODE 字符可能转化成一个字节或多个字节。<br />
            <br />
            反之，将字节串转化成字符串时，也可能多个字节转化成一个字符。比如，[0xD6, 0xD0] 这两个字节，通过 GB2312 转化为字符串时，将得到 [0x4E2D] 一个字符，即 '中' 字。<br />
            <br />
            &#8220;ANSI 编码&#8221;的特点：<br />
            1. 这些&#8220;ANSI 编码标准&#8221;都只能处理各自语言范围之内的 UNICODE 字符。<br />
            2. &#8220;UNICODE 字符&#8221;与&#8220;转换出来的字节&#8221;之间的关系是人为规定的。</td>
        </tr>
        <tr>
            <td class="bot_1" noWrap align="center">UNICODE 编码</td>
            <td class="bot_2">UTF-8,<br />
            UTF-16, UnicodeBig &#8230;&#8230;</td>
            <td class="bot_2">与&#8220;ANSI 编码&#8221;类似的，把字符串通过 UNICODE 编码转化成&#8220;字节串&#8221;时，一个 UNICODE 字符可能转化成一个字节或多个字节。<br />
            <br />
            与&#8220;ANSI 编码&#8221;不同的是：<br />
            1. 这些&#8220;UNICODE 编码&#8221;能够处理所有的 UNICODE 字符。<br />
            2. &#8220;UNICODE 字符&#8221;与&#8220;转换出来的字节&#8221;之间是可以通过计算得到的。</td>
        </tr>
    </tbody>
</table>
<p>我们实际上没有必要去深究每一种编码具体把某一个字符编码成了哪几个字节，我们只需要知道&#8220;编码&#8221;的概念就是把&#8220;字符&#8221;转化成&#8220;字节&#8221;就可以了。对于&#8220;UNICODE 编码&#8221;，由于它们是可以通过计算得到的，因此，在特殊的场合，我们可以去了解某一种&#8220;UNICODE 编码&#8221;是怎样的规则。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <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 中，用来代表&#8220;字符&#8221;和&#8220;字节&#8221;的数据类型，以及进行编码的方法：</p>
<table cellspacing="0" cellpadding="3" border="0">
    <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" noWrap align="center">字符</td>
            <td class="con_2">wchar_t</td>
            <td class="con_2">char</td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">字节</td>
            <td class="con_2">char</td>
            <td class="con_2">byte</td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">ANSI 字符串</td>
            <td class="con_2">char[]</td>
            <td class="con_2">byte[]</td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">UNICODE 字符串</td>
            <td class="con_2">wchar_t[]</td>
            <td class="con_2">String</td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">字节串&#8594;字符串</td>
            <td class="con_2">mbstowcs(), MultiByteToWideChar()</td>
            <td class="con_2">string = new String(bytes, "encoding")</td>
        </tr>
        <tr>
            <td class="bot_1" noWrap align="center">字符串&#8594;字节串</td>
            <td class="bot_2">wcstombs(), WideCharToMultiByte()</td>
            <td class="bot_2">bytes = string.getBytes("encoding")</td>
        </tr>
    </tbody>
</table>
<p>以上需要注意几点：</p>
<ol>
    <li>Java 中的 char 代表一个&#8220;UNICODE 字符（宽字节字符）&#8221;，而 C++ 中的 char 代表一个字节。
    <li>MultiByteToWideChar() 和 WideCharToMultiByte() 是 Windows API 函数。 </li>
</ol>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <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" cellpadding="6" bgcolor="#eeeeee" border="1">
    <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">"中文123"</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">"\x4E2D\x6587\x0031\x0032\x0033"</span>;</td>
        </tr>
    </tbody>
</table>
<p>UNICODE 字符串的 I/O 操作，字符与字节的转换操作：</p>
<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// 运行时设定当前 ANSI 编码，VC 格式<br />
            </span>setlocale(LC_ALL, <span class="string">".936"</span>);<br />
            <br />
            <span class="rem">// GCC 中格式</span><br />
            setlocale(LC_ALL, <span class="string">"zh_CN.GBK"</span>);<br />
            <br />
            <span class="rem">// Visual C++ 中使用小写 %s，按照 setlocale 指定编码输出到文件<br />
            // GCC 中使用大写 %S</span><br />
            fwprintf(fp, L<span class="string">"%s\n"</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" cellpadding="6" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// 如果源程序的编码与当前默认 ANSI 编码不一致，<br />
            // 则需要此行，编译时用来指明当前源程序使用的编码</span><span class="key"><br />
            #pragma setlocale</span>(<span class="string">".936"</span>)<br />
            <br />
            <span class="rem">// UNICODE 字符串常量，内容长度 10 字节</span><br />
            wchar_t wsz[<span class="number">20</span>] = L<span class="string">"中文123"</span>;</td>
        </tr>
    </tbody>
</table>
<p>以上需要注意 #pragma setlocale 与 setlocale(LC_ALL, "") 的作用是不同的，#pragma setlocale 在编译时起作用，setlocale() 在运行时起作用。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <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" cellpadding="6" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// Java 代码，直接写中文</span><span class="pw"><br />
            String</span> string = <span class="string">"中文123"</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.* 中，以&#8220;Stream&#8221;结尾的类一般是用来操作&#8220;字节串&#8221;的类，以&#8220;Reader&#8221;，&#8220;Writer&#8221;结尾的类一般是用来操作&#8220;字符串&#8221;的类。</p>
<table cellspacing="0" cellpadding="6" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// 字符串与字节串间相互转化<br />
            <br />
            // 按照 GB2312 得到字节（得到多字节字符串）</span><span class="key"><br />
            byte</span> [] bytes = string.getBytes(<span class="string">"GB2312"</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">"GB2312"</span>);<br />
            <br />
            <span class="rem">// 要将 String 按照某种编码写入文本文件，有两种方法：<br />
            <br />
            // 第一种办法：用 Stream 类写入已经按照指定编码转化好的字节串</span><br />
            OutputStream os = <span class="key">new</span> FileOutputStream(<span class="string">"1.txt"</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">"2.txt"</span>), <span class="string">"GB2312"</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" cellpadding="6" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code">E:\&gt;javac <font color="#ff0000">-encoding BIG5</font> Hello.java</td>
        </tr>
    </tbody>
</table>
<p>以上需要注意区分源程序的编码与 I/O 操作的编码，前者是在编译时起作用，后者是在运行时起作用。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <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" cellpadding="3" border="0">
    <tbody>
        <tr>
            <td class="top_1">　</td>
            <td class="top_2" align="center"><strong>对编码的误解</strong></td>
        </tr>
        <tr>
            <td class="con_1" noWrap align="center">误解一</td>
            <td class="con_2">在将&#8220;字节串&#8221;转化成&#8220;UNICODE 字符串&#8221;时，比如在读取文本文件时，或者通过网络传输文本时，容易将&#8220;字节串&#8221;简单地作为<strong>单字节字符串</strong>，采用每&#8220;一个字节&#8221;就是&#8220;一个字符&#8221;的方法进行转化。<br />
            <br />
            而实际上，在非英文的环境中，应该将&#8220;字节串&#8221;作为 ANSI 字符串，采用适当的编码来得到 UNICODE 字符串，有可能&#8220;多个字节&#8221;才能得到&#8220;一个字符&#8221;。<br />
            <br />
            通常，一直在英文环境下做开发的程序员们，容易有这种误解。</td>
        </tr>
        <tr>
            <td class="bot_1" noWrap align="center">误解二</td>
            <td class="bot_2">在 DOS，Windows 98 等非 UNICODE 环境下，字符串都是以 ANSI 编码的字节形式存在的。这种以字节形式存在的字符串，必须知道是哪种编码才能被正确地使用。这使我们形成了一个惯性思维：&#8220;字符串的编码&#8221;。<br />
            <br />
            当 UNICODE 被支持后，Java 中的 String 是以字符的&#8220;序号&#8221;来存储的，不是以&#8220;某种编码的字节&#8221;来存储的，因此已经不存在&#8220;字符串的编码&#8221;这个概念了。只有在&#8220;字符串&#8221;与&#8220;字节串&#8221;转化时，或者，将一个&#8220;字节串&#8221;当成一个 ANSI 字符串时，才有编码的概念。<br />
            <br />
            不少的人都有这个误解。</td>
        </tr>
    </tbody>
</table>
<p>第一种误解，往往是导致乱码产生的原因。第二种误解，往往导致本来容易纠正的乱码问题变得更复杂。</p>
<p>在这里，我们可以看到，其中所讲的&#8220;误解一&#8221;，即采用每&#8220;一个字节&#8221;就是&#8220;一个字符&#8221;的转化方法，实际上也就等同于采用 iso-8859-1 进行转化。因此，我们常常使用 bytes = string.getBytes("iso-8859-1") 来进行逆向操作，得到原始的&#8220;字节串&#8221;。然后再使用正确的 ANSI 编码，比如 string = new String(bytes, "GB2312")，来得到正确的&#8220;UNICODE 字符串&#8221;。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <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" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <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>当页面中的表单提交字符串时，首先把字符串按照当前页面的编码，转化成字节串。然后再将每个字节转化成 "%XX" 的格式提交到 Web 服务器。比如，一个编码为 GB2312 的页面，提交 "中" 这个字符串时，提交给服务器的内容为 "%D6%D0"。</p>
<p>在服务器端，Web 服务器把收到的 "%D6%D0" 转化成 [0xD6, 0xD0] 两个字节，然后再根据 GB2312 编码规则得到 "中" 字。</p>
<p>在 Tomcat 服务器中，request.getParameter() 得到乱码时，常常是因为前面提到的&#8220;误解一&#8221;造成的。默认情况下，当提交 "%D6%D0" 给 Tomcat 服务器时，request.getParameter() 将返回 [0x00D6, 0x00D0] 两个 UNICODE 字符，而不是返回一个 "中" 字符。因此，我们需要使用 bytes = string.getBytes("iso-8859-1") 得到原始的字节串，再用 string = new String(bytes, "GB2312") 重新得到正确的字符串 "中"。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <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>如果从数据库读取字符串时得到乱码，而数据库中存放的数据又是正确的，那么往往还是因为前面提到的&#8220;误解一&#8221;造成的。解决的办法还是通过 string = new String( string.getBytes("iso-8859-1"), "GB2312") 的方法，重新得到原始的字节串，再重新使用正确的编码转化成字符串。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <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>转化成&#8220;字节串&#8221;，然后再把&#8220;字节串&#8221;通过一种指定的<strong>传输编码</strong>（Content-Transfer-Encoding）进行转化得到另一串&#8220;字节串&#8221;。比如，打开一封电子邮件源代码，可以看到类似的内容：</p>
<table cellspacing="0" cellpadding="6" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code">Content-Type: text/plain;<br />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#ff0000">charset="gb2312"</font><br />
            <font color="#ff0000">Content-Transfer-Encoding: base64</font><br />
            <br />
            sbG+qcrQuqO17cf4yee74bGjz9W7+b3wudzA7dbQ0MQNCg0KvPKzxqO6uqO17cnnsaPW0NDEDQoNCg==</td>
        </tr>
    </tbody>
</table>
<p>最常用的 Content-Transfer-Encoding 有 Base64 和 Quoted-Printable 两种。在对二进制文件或者中文文本进行转化时，Base64 得到的&#8220;字节串&#8221;比 Quoted-Printable 更短。在对英文文本进行转化时，Quoted-Printable 得到的&#8220;字节串&#8221;比 Base64 更短。</p>
<p>邮件的标题，用了一种更简短的格式来标注&#8220;字符编码&#8221;和&#8220;传输编码&#8221;。比如，标题内容为 "中"，则在邮件源代码中表示为：</p>
<table cellspacing="0" cellpadding="6" width="100%" bgcolor="#eeeeee" border="1">
    <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>第一个&#8220;=?&#8221;与&#8220;?&#8221;中间的部分指定了字符编码，在这个例子中指定的是 GB2312。
    <li>&#8220;?&#8221;与&#8220;?&#8221;中间的&#8220;B&#8221;代表 Base64。如果是&#8220;Q&#8221;则代表 Quoted-Printable。
    <li>最后&#8220;?&#8221;与&#8220;?=&#8221;之间的部分，就是经过 GB2312 转化成字节串，再经过 Base64 转化后的标题内容。 </li>
</ul>
<p>如果&#8220;传输编码&#8221;改为 Quoted-Printable，同样，如果标题内容为 "中"：</p>
<table cellspacing="0" cellpadding="6" width="100%" bgcolor="#eeeeee" border="1">
    <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>如果阅读邮件时出现乱码，一般是因为&#8220;字符编码&#8221;或&#8220;传输编码&#8221;指定有误，或者是没有指定。比如，有的发邮件组件在发送邮件时，标题 "中"：</p>
<table cellspacing="0" cellpadding="6" width="100%" bgcolor="#eeeeee" border="1">
    <tbody>
        <tr>
            <td class="code"><span class="rem">// 错误的标题格式</span><br />
            Subject: <span style="background-color: #ffff00">=?</span><font color="#ff0000">ISO-8859-1</font><span style="background-color: #ffff00">?Q?</span>=D6=D0<span style="background-color: #ffff00">?=</span></td>
        </tr>
    </tbody>
</table>
<p>这样的表示，实际上是明确指明了标题为 [0x00D6, 0x00D0]，即 "&#214;&#208;"，而不是 "中"。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td align="right" width="100%"><img height="1" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/blue_rule.gif" width="100%" border="0" /></td>
        </tr>
        <tr valign="top">
            <td align="right" width="100%">
            <table cellspacing="0" cellpadding="0">
                <tbody>
                    <tr align="right">
                        <td>
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="file:///E:/新建文件夹/字符，字节和编码%20-%20Characters,%20Bytes%20And%20Encoding.files/u_bold.gif" width="16" border="0" /></td>
                                    <td valign="top" align="right">
                                    <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>误解：&#8220;ISO-8859-1 是国际编码？&#8221;</h5>
<p>非也。iso-8859-1 只是单字节字符集中最简单的一种，也就是&#8220;字节编号&#8221;与&#8220;UNICODE 字符编号&#8221;一致的那种编码规则。当我们要把一个&#8220;字节串&#8221;转化成&#8220;字符串&#8221;，而又不知道它是哪一种 ANSI 编码时，先暂时地把&#8220;每一个字节&#8221;作为&#8220;一个字符&#8221;进行转化，不会造成信息丢失。然后再使用 bytes = string.getBytes("iso-8859-1") 的方法可恢复到原始的字节串。</p>
<h5>误解：&#8220;Java 中，怎样知道某个字符串的内码？&#8221;</h5>
<p>Java 中，字符串类 java.lang.String 处理的是 UNICODE 字符串，不是 ANSI 字符串。我们只需要把字符串作为&#8220;抽象的符号的串&#8221;来看待。因此不存在字符串的内码的问题。</p>
<img src ="http://www.blogjava.net/jesenblog/aggbug/191075.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesenblog/" target="_blank">白露</a> 2008-04-06 18:22 <a href="http://www.blogjava.net/jesenblog/articles/191075.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>异常转型和异常链</title><link>http://www.blogjava.net/jesenblog/articles/187282.html</link><dc:creator>白露</dc:creator><author>白露</author><pubDate>Wed, 19 Mar 2008 08:16:00 GMT</pubDate><guid>http://www.blogjava.net/jesenblog/articles/187282.html</guid><wfw:comment>http://www.blogjava.net/jesenblog/comments/187282.html</wfw:comment><comments>http://www.blogjava.net/jesenblog/articles/187282.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesenblog/comments/commentRss/187282.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesenblog/services/trackbacks/187282.html</trackback:ping><description><![CDATA[<p style="text-align: left" align="left" _extended="true"><strong _extended="true"><font face="宋体" color="red" size="3" _extended="true"><span style="font-weight: bold; font-size: 12pt; color: red; font-family: 宋体" _extended="true">异常转型和异常链</span></font></strong></p>
<p style="text-align: left" align="left" _extended="true"><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">异常转型在上面已经提到过了，实际上就是捕获到异常后，将异常以新的类型的异常再抛出，这样做一般为了异常的信息更直观！比如：</span></font><font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US"><br _extended="true" />
</span></font><font face="Verdana" color="black" size="2" _extended="true"><span lang="EN-US" style="font-size: 10pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">public void run() throws MyException{<br _extended="true" />
&nbsp;...<br _extended="true" />
&nbsp;try{<br _extended="true" />
&nbsp;&nbsp;...<br _extended="true" />
&nbsp;}catch(IOException e){<br _extended="true" />
&nbsp;&nbsp;...<br _extended="true" />
&nbsp;&nbsp;throw new MyException();<br _extended="true" />
&nbsp;}finally{<br _extended="true" />
&nbsp;&nbsp;...<br _extended="true" />
&nbsp;}<br _extended="true" />
}</span></font></p>
<p style="text-align: left" align="left" _extended="true"><font face="Verdana" color="black" size="1" _extended="true"><span lang="EN-US" style="font-size: 7.5pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">&nbsp;</span></font></p>
<p style="text-align: left" align="left" _extended="true"><strong _extended="true"><font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-weight: bold; font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></font></strong> <strong _extended="true"><font face="宋体" color="black" size="3" _extended="true"><span style="font-weight: bold; font-size: 12pt; color: black; font-family: 宋体" _extended="true">异常链</span></font></strong><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">，在</span></font><font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">JDK1.4</span></font><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">以后版本中，</span></font><font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">Throwable</span></font><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">类支持异常链机制。</span></font><font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">Throwable</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">包含了其线程创建时线程执行堆栈的快照。它还包含了给出有关错误更多信息的消息字符串。最后，它还可以包含</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">cause</span></font><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">（原因）：另一个导致此</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">throwable</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">抛出的</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">throwable</span></font><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">。它也称为异常链</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">设施，因为</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">cause</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">自身也会有</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">cause</span></font><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">，依此类推，就形成了异常链，每个异常都是由另一个异常引起的。</span></font><font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">&nbsp;<br _extended="true" />
<strong _extended="true"><span style="font-weight: bold" _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></strong></span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">通俗的说，异常链就是把原始的异常包装为新的异常类，并在新的异常类中封装了原始异常类，这样做的目的在于找到异常的根本原因。</span></font></p>
<p style="text-align: left" align="left" _extended="true"><font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US"><br _extended="true" />
<strong _extended="true"><span style="font-weight: bold" _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></strong></span></font> <font face="宋体" color="blue" size="3" _extended="true"><span style="font-size: 12pt; color: blue; font-family: 宋体" _extended="true">通过</span></font><font face="Verdana" color="blue" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: blue; font-family: Verdana" _extended="true" xml:lang="EN-US">Throwable</span></font><font face="宋体" color="blue" size="3" _extended="true"><span style="font-size: 12pt; color: blue; font-family: 宋体" _extended="true">的两个构造方法可以创建自定义的包含异常原因的异常类型</span></font><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">：</span></font><font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US"><br _extended="true" />
Throwable(String message, Throwable cause)<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">构造一个带指定详细消息和</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">cause</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">的新</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">throwable</span></font><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">。</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US"><br _extended="true" />
Throwable(Throwable cause)<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">构造一个带指定</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">cause</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">和</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">(cause==null ? null :cause.toString())</span></font><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">（它通常包含类和</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">cause</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">的详细消息）的详细消息的新</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">throwable</span></font><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">。</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US"><br _extended="true" />
getCause()<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">返回此</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">throwable</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">的</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">cause</span></font><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">；如果</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">cause</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">不存在或未知，则返回</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">null</span></font><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">。</span></font><font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US"><br _extended="true" />
initCause(Throwable cause)<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">将此</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">throwable</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">的</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">cause</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">初始化为指定值。</span></font></p>
<p style="text-align: left" align="left" _extended="true"><strong _extended="true"><font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-weight: bold; font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></font></strong> <font face="宋体" color="blue" size="3" _extended="true"><span style="font-size: 12pt; color: blue; font-family: 宋体" _extended="true">在</span></font><font face="Verdana" color="blue" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: blue; font-family: Verdana" _extended="true" xml:lang="EN-US">Throwable</span></font><font face="宋体" color="blue" size="3" _extended="true"><span style="font-size: 12pt; color: blue; font-family: 宋体" _extended="true">的子类</span></font><font face="Verdana" color="blue" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: blue; font-family: Verdana" _extended="true" xml:lang="EN-US">Exception</span></font><font face="宋体" color="blue" size="3" _extended="true"><span style="font-size: 12pt; color: blue; font-family: 宋体" _extended="true">中，也有类似的指定异常原因的构造方法：</span></font><font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US"><br _extended="true" />
Exception(String message, Throwable cause)<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">构造带指定详细消息和原因的新异常。</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US"><br _extended="true" />
Exception(Throwable cause)<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">根据指定的原因和</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">(cause==null ? null : cause.toString())</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">的详细消息构造新异常（它通常包含</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">cause</span></font> <font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">的类和详细消息）。</span></font> <font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US"><br _extended="true" />
</span></font><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">因此，可以通过扩展</span></font><font face="Verdana" color="black" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">Exception</span></font><font face="宋体" color="black" size="3" _extended="true"><span style="font-size: 12pt; color: black; font-family: 宋体" _extended="true">类来构造带有异常原因的新的异常类。</span></font></p>
<p style="text-align: left" align="left" _extended="true"><font face="Verdana" color="black" size="1" _extended="true"><span lang="EN-US" style="font-size: 7.5pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">&nbsp;</span></font></p>
<p style="text-align: left" align="left" _extended="true"><strong _extended="true"><font face="宋体" color="red" size="3" _extended="true"><span style="font-weight: bold; font-size: 12pt; color: red; font-family: 宋体" _extended="true">七、</span></font></strong><strong _extended="true"><font face="Verdana" color="red" size="3" _extended="true"><span lang="EN-US" style="font-weight: bold; font-size: 12pt; color: red; font-family: Verdana" _extended="true" xml:lang="EN-US">Java</span></font></strong><strong _extended="true"><font face="宋体" color="red" size="3" _extended="true"><span style="font-weight: bold; font-size: 12pt; color: red; font-family: 宋体" _extended="true">异常处理的原则和技巧</span></font></strong></p>
<p style="text-align: left" align="left" _extended="true"><font face="Verdana" color="black" size="1" _extended="true"><span lang="EN-US" style="font-size: 7.5pt; color: black; font-family: Verdana" _extended="true" xml:lang="EN-US">&nbsp;</span></font></p>
<p style="text-align: left" align="left" _extended="true"><font face="Verdana" color="green" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: green; font-family: Verdana" _extended="true" xml:lang="EN-US">1</span></font><font face="宋体" color="green" size="3" _extended="true"><span style="font-size: 12pt; color: green; font-family: 宋体" _extended="true">、避免过大的</span></font><font face="Verdana" color="green" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: green; font-family: Verdana" _extended="true" xml:lang="EN-US">try</span></font><font face="宋体" color="green" size="3" _extended="true"><span style="font-size: 12pt; color: green; font-family: 宋体" _extended="true">块，不要把不会出现异常的代码放到</span></font><font face="Verdana" color="green" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: green; font-family: Verdana" _extended="true" xml:lang="EN-US">try</span></font><font face="宋体" color="green" size="3" _extended="true"><span style="font-size: 12pt; color: green; font-family: 宋体" _extended="true">块里面，尽量保持一个</span></font><font face="Verdana" color="green" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: green; font-family: Verdana" _extended="true" xml:lang="EN-US">try</span></font><font face="宋体" color="green" size="3" _extended="true"><span style="font-size: 12pt; color: green; font-family: 宋体" _extended="true">块对应一个或多个异常。</span></font><font face="Verdana" color="green" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: green; font-family: Verdana" _extended="true" xml:lang="EN-US"><br _extended="true" />
2</span></font><font face="宋体" color="green" size="3" _extended="true"><span style="font-size: 12pt; color: green; font-family: 宋体" _extended="true">、细化异常的类型，不要不管什么类型的异常都写成</span></font><font face="Verdana" color="green" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: green; font-family: Verdana" _extended="true" xml:lang="EN-US">Excetpion</span></font><font face="宋体" color="green" size="3" _extended="true"><span style="font-size: 12pt; color: green; font-family: 宋体" _extended="true">。</span></font><font face="Verdana" color="green" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: green; font-family: Verdana" _extended="true" xml:lang="EN-US"><br _extended="true" />
3</span></font><font face="宋体" color="green" size="3" _extended="true"><span style="font-size: 12pt; color: green; font-family: 宋体" _extended="true">、</span></font><font face="Verdana" color="green" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: green; font-family: Verdana" _extended="true" xml:lang="EN-US">catch</span></font><font face="宋体" color="green" size="3" _extended="true"><span style="font-size: 12pt; color: green; font-family: 宋体" _extended="true">块尽量保持一个块捕获一类异常，不要忽略捕获的异常，捕获到后要么处理，要么转译，要么重新抛出新类型的异常。</span></font><font face="Verdana" color="green" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: green; font-family: Verdana" _extended="true" xml:lang="EN-US"><br _extended="true" />
4</span></font><font face="宋体" color="green" size="3" _extended="true"><span style="font-size: 12pt; color: green; font-family: 宋体" _extended="true">、不要把自己能处理的异常抛给别人。</span></font><font face="Verdana" color="green" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: green; font-family: Verdana" _extended="true" xml:lang="EN-US"><br _extended="true" />
5</span></font><font face="宋体" color="green" size="3" _extended="true"><span style="font-size: 12pt; color: green; font-family: 宋体" _extended="true">、不要用</span></font><font face="Verdana" color="green" size="3" _extended="true"><span lang="EN-US" style="font-size: 12pt; color: green; font-family: Verdana" _extended="true" xml:lang="EN-US">try...catch</span></font><font face="宋体" color="green" size="3" _extended="true"><span style="font-size: 12pt; color: green; font-family: 宋体" _extended="true">参与控制程序流程，异常控制的根本目的是处理程序的非正常情况。</span></font></p>
<img src ="http://www.blogjava.net/jesenblog/aggbug/187282.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesenblog/" target="_blank">白露</a> 2008-03-19 16:16 <a href="http://www.blogjava.net/jesenblog/articles/187282.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java Throwable </title><link>http://www.blogjava.net/jesenblog/articles/187281.html</link><dc:creator>白露</dc:creator><author>白露</author><pubDate>Wed, 19 Mar 2008 08:14:00 GMT</pubDate><guid>http://www.blogjava.net/jesenblog/articles/187281.html</guid><wfw:comment>http://www.blogjava.net/jesenblog/comments/187281.html</wfw:comment><comments>http://www.blogjava.net/jesenblog/articles/187281.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesenblog/comments/commentRss/187281.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesenblog/services/trackbacks/187281.html</trackback:ping><description><![CDATA[<dl _extended="true">
<dd _extended="true">
<pre _extended="true">public class <strong _extended="true">Throwable</strong>
</pre>
<dt _extended="true">extends <a title="java.lang 中的类" href="file:///G:/html_zh_CN/html/zh_CN/api/java/lang/Object.html" _extended="true">Object</a>
<dt _extended="true">implements <a title="java.io 中的接口" href="file:///G:/html_zh_CN/html/zh_CN/api/java/io/Serializable.html" _extended="true">Serializable</a> </dt></dl>
<pre _extended="true">&nbsp;
</pre>
<p _extended="true"><code _extended="true">Throwable</code> 类是 Java 语言中所有错误或异常的超类。只有当对象是此类（或其子类之一）的实例时，才能通过 Java 虚拟机或者 Java <code _extended="true">throw</code> 语句抛出。类似地，只有此类或其子类之一才可以是 <code _extended="true">catch</code> 子句中的参数类型。</p>
<p _extended="true">两个子类的实例，<a title="java.lang 中的类" href="file:///G:/html_zh_CN/html/zh_CN/api/java/lang/Error.html" _extended="true"><code _extended="true">Error</code></a> 和 <a title="java.lang 中的类" href="file:///G:/html_zh_CN/html/zh_CN/api/java/lang/Exception.html" _extended="true"><code _extended="true">Exception</code></a>，通常用于指示发生了异常情况。通常，这些实例是在异常情况的上下文中新近创建的，因此包含了相关的信息（比如堆栈跟踪数据）。</p>
<p _extended="true">Throwable 包含了其线程创建时线程执行堆栈的快照。它还包含了给出有关错误更多信息的消息字符串。最后，它还可以包含 <em _extended="true">cause（原因）</em>：另一个导致此 throwable 抛出的 throwable。此 cause 设施在 1.4 版本中首次出现。它也称为<em _extended="true">异常链</em> 设施，因为 cause 自身也会有 cause，依此类推，就形成了异常链，每个异常都是由另一个异常引起的。</p>
<p _extended="true">导致 throwable cause 的一个理由是，抛出它的类构建在低层抽象之中，而高层操作由于低层操作的失败而失败。让低层抛出的 throwable 向外传播是一种糟糕的设计方法，因为它通常与高层提供的抽象不相关。此外，这样做将高层 API 与其实现细节关联起来，假定低层异常是经过检查的异常。抛出&#8220;经过包装的异常&#8221;（即包含 cause 的异常）允许高层与其调用方交流失败详细信息，而不会招致上述任何一个缺点。这种方式保留了改变高层实现而不改变其 API 的灵活性（尤其是，异常集合通过其方法抛出）。</p>
<p _extended="true">导致 throwable cause 的另一个 cause 是，抛出它的方法必须符合通用接口，而通用接口不允许方法直接抛出 cause。例如，假定持久集合符合 <a title="java.util 中的接口" href="file:///G:/html_zh_CN/html/zh_CN/api/java/util/Collection.html" _extended="true"><code _extended="true">Collection</code></a> 接口，而其持久性在 <tt _extended="true">java.io</tt> 的基础上实现。假定 <tt _extended="true">put</tt> 方法的内部可以抛出 <a title="java.io 中的类" href="file:///G:/html_zh_CN/html/zh_CN/api/java/io/IOException.html" _extended="true"><code _extended="true">IOException</code></a>。实现可以与其调用方交流 <tt _extended="true">IOException</tt> 的详细消息，同时通过以一种合适的未检查的异常来包装 <tt _extended="true">IOException</tt>，使其符合 <tt _extended="true">Collection</tt> 接口。（持久集合的规范应该指示它能够抛出这种异常。）</p>
<p _extended="true">Cause 可以通过两种方式与 throwable 关联起来：通过一个将 cause 看作参数的构造方法；或者通过 <a href="file:///G:/html_zh_CN/html/zh_CN/api/java/lang/Throwable.html#initCause(java.lang.Throwable)" _extended="true"><code _extended="true">initCause(Throwable)</code></a> 方法。对于那些希望将 cause 与其关联起来的新 throwable 类，应该提供带有 cause 的构造方法，并委托（可能间接）给一个带有 cause 的 <tt _extended="true">Throwable</tt> 构造方法。例如：</p>
<pre _extended="true">     try {
lowLevelOp();
} catch (LowLevelException le) {
throw new HighLevelException(le);  // Chaining-aware constructor
}
</pre>
因为 <tt _extended="true">initCause</tt> 方法是公共的，它允许 cause 与任何 throwable 相关联，甚至包括&#8220;遗留 throwable&#8221;，它的实现提前将异常链机制的附件应用到 <tt _extended="true">Throwable</tt>。例如：
<pre _extended="true">     try {
lowLevelOp();
} catch (LowLevelException le) {
throw (HighLevelException)
new HighLevelException().initCause(le);  // Legacy constructor
}
</pre>
<img src ="http://www.blogjava.net/jesenblog/aggbug/187281.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesenblog/" target="_blank">白露</a> 2008-03-19 16:14 <a href="http://www.blogjava.net/jesenblog/articles/187281.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入剖析Java编程中的中文问题及建议最优解决方法 </title><link>http://www.blogjava.net/jesenblog/articles/187280.html</link><dc:creator>白露</dc:creator><author>白露</author><pubDate>Wed, 19 Mar 2008 08:13:00 GMT</pubDate><guid>http://www.blogjava.net/jesenblog/articles/187280.html</guid><wfw:comment>http://www.blogjava.net/jesenblog/comments/187280.html</wfw:comment><comments>http://www.blogjava.net/jesenblog/articles/187280.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesenblog/comments/commentRss/187280.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesenblog/services/trackbacks/187280.html</trackback:ping><description><![CDATA[1、中文问题的来源&nbsp;<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;计算机最初的操作系统支持的编码是单字节的字符编码，于是，在计算机中一切处理程序最初都是以单字节编码的英文为准进行处理。随着计算机的发展，为了适应世界其它民族的语言（当然包括我们的汉字），人们提出了UNICODE编码，它采用双字节编码，兼容英文字符和其它民族的双字节字符编码，所以，目前，大多数国际性的软件内部均采用UNICODE编码，在软件运行时，它获得本地支持系统（多数时间是操作系统）默认支持的编码格式，然后再将软件内部的UNICODE转化为本地系统默认支持的格式显示出来。Java的JDK和JVM即是如此，我这里说的JDK是指国际版的JDK，我们大多数程序员使用的是国际化的JDK版本，以下所有的JDK均指国际化的JDK版本。我们的汉字是双字节编码语言，为了能让计算机处理中文，我们自己制定的gb2312、GBK、GBK2K等标准以适应计算机处理的需求。所以，大部分的操作系统为了适应我们处理中文的需求，均定制有中文操作系统，它们采用的是GBK,GB2312编码格式以正确显示我们的汉字。如：中文Win2K默认采用的是GBK编码显示，在中文WIN2k中保存文件时默认采用的保存文件的编码格式也是GBK的，即，所有在中文WIN2K中保存的文件它的内部编码默认均采用GBK编码，注意：GBK是在GB2312基础上扩充来的。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;由于Java语言内部采用UNICODE编码，所以在JAVA程序运行时，就存在着一个从UNICODE编码和对应的操作系统及浏览器支持的编码格式转换输入、输出的问题，这个转换过程有着一系列的步骤，如果其中任何一步出错，则显示出来的汉字就会出是乱码，这就是我们常见的JAVA中文问题。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;同时，Java是一个跨平台的编程语言，也即我们编写的程序不仅能在中文windows上运行，也能在中文Linux等系统上运行，同时也要求能在英文等系统上运行（我们经常看到有人把在中文win2k上编写的JAVA程序，移植到英文Linux上运行）。这种移植操作也会带来中文问题。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;还有，有人使用英文的操作系统和英文的IE等浏览器，来运行带中文字符的程序和浏览中文网页，它们本身就不支持中文，也会带来中文问题。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;有，几乎所有的浏览器默认在传递参数时都是以UTF-8编码格式来传递，而不是按中文编码传递，所以，传递中文参数时也会有问题，从而带来乱码现象。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;总之，以上几个方面是JAVA中的中文问题的主要来源，我们把以上原因造成的程序不能正确运行而产生的问题称作：JAVA中文问题。<br _extended="true" />
2、JAVA编码转换的详细过程&nbsp;<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;我们常见的JAVA程序包括以下类别：<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*直接在console上运行的类(包括可视化界面的类)<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*JSP代码类（注：JSP是Servlets类的变型）<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*Servelets类<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*EJB类<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*其它不可以直接运行的支持类<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;这些类文件中，都有可能含有中文字符串，并且我们常用前三类JAVA程序和用户直接交互，用于输出和输入字符，如：我们在JSP和Servlet中得到客户端送来的字符，这些字符也包括中文字符。无论这些JAVA类的作用如何，这些JAVA程序的生命周期都是这样的：<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;*编程人员在一定的操作系统上选择一个合适的编辑软件来实现源程序代码并以.java扩展名保存在操作系统中，例如我们在中文win2k中用记事本编辑一个java源程序；<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*编程人员用JDK中的javac.exe来编译这些源代码，形成.class类(JSP文件是由容器调用JDK来编译的)；<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*直接运行这些类或将这些类布署到WEB容器中去运行，并输出结果。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;那么，在这些过程中，JDK和JVM是如何将这些文件如何编码和解码并运行的呢？<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;这里，我们以中文win2k操作系统为例说明JAVA类是如何来编码和被解码的。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;第一步，我们在中文win2k中用编辑软件如记事本编写一个Java源程序文件(包括以上五类JAVA程序)，程序文件在保存时默认采用了操作系统默认支持GBK编码格式(操作系统默认支持的格式为file.encoding格式)形成了一个.java文件，也即，java程序在被编译前，我们的JAVA源程序文件是采用操作系统默认支持的file.encoding编码格式保存的，java源程序中含有中文信息字符和英文程序代码；要查看系统的file.encoding参数，可以用以下代码：<br _extended="true" />
public&nbsp;class&nbsp;ShowSystemDefaultEncoding&nbsp;{<br _extended="true" />
public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{<br _extended="true" />
String&nbsp;encoding&nbsp;=&nbsp;System.getProperty("file.encoding");<br _extended="true" />
System.out.println(encoding);<br _extended="true" />
}}<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;第二步，我们用JDK的javac.exe文件编译我们的Java源程序，由于JDK是国际版的，在编译的时候，如果我们没有用-encoding参数指定我们的JAVA源程序的编码格式，则javac.exe首先获得我们操作系统默认采用的编码格式，也即在编译java程序时，若我们不指定源程序文件的编码格式，JDK首先获得操作系统的file.encoding参数(它保存的就是操作系统默认的编码格式，如WIN2k，它的值为GBK)，然后JDK就把我们的java源程序从file.encoding编码格式转化为JAVA内部默认的UNICODE格式放入内存中。然后，javac把转换后的unicode格式的文件进行编译成.class类文件，此时.class文件是UNICODE编码的，它暂放在内存中，紧接着，JDK将此以UNICODE编码的编译后的class文件保存到我们的操作系统中形成我们见到的.class文件。对我们来说，我们最终获得的.class文件是内容以UNICODE编码格式保存的类文件，它内部包含我们源程序中的中文字符串，只不过此时它己经由file.encoding格式转化为UNICODE格式了。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;这一步中，对于JSP源程序文件是不同的，对于JSP，这个过程是这样的：即WEB容器调用JSP编译器，JSP编译器先查看JSP文件中是否设置有文件编码格式，如果JSP文件中没有设置JSP文件的编码格式，则JSP编译器调用JDK先把JSP文件用JVM默认的字符编码格式(也即WEB容器所在的操作系统的默认的file.encoding)转化为临时的Servlet类，然后再把它编译成UNICODE格式的class类，并保存在临时文件夹中。如：在中文win2k上，WEB容器就把JSP文件从GBK编码格式转化为UNICODE格式，然后编译成临时保存的Servlet类，以响应用户的请求。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;第三步，运行第二步编译出来的类，分为三种情况：<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;A、&nbsp;直接在console上运行的类<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;B、&nbsp;EJB类和不可以直接运行的支持类(如JavaBean类)<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;C、&nbsp;JSP代码和Servlet类<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;D、&nbsp;JAVA程序和数据库之间<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;下面我们分这四种情况来看。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;A、直接在console上运行的类<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;这种情况，运行该类首先需要JVM支持，即操作系统中必须安装有JRE。运行过程是这样的：首先java启动JVM，此时JVM读出操作系统中保存的class文件并把内容读入内存中，此时内存中为UNICODE格式的class类，然后JVM运行它，如果此时此类需要接收用户输入，则类会默认用file.encoding编码格式对用户输入的串进行编码并转化为unicode保存入内存（用户可以设置输入流的编码格式）。程序运行后，产生的字符串（UNICODE编码的）再回交给JVM，最后JRE把此字符串再转化为file.encoding格式(用户可以设置输出流的编码格式)传递给操作系统显示接口并输出到界面上。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;以上每一步的转化都需要正确的编码格式转化，才能最终不出现乱码现象。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;B、EJB类和不可以直接运行的支持类(如JavaBean类)<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;由于EJB类和不可以直接运行的支持类，它们一般不与用户直接交互输入和输出，它们常常与其它的类进行交互输入和输出，所以它们在第二步被编译后，就形成了内容是UNICODE编码的类保存在操作系统中了，以后只要它与其它的类之间的交互在参数传递过程中没有丢失，则它就会正确的运行。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;C、JSP代码和Servlet类<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;经过第二步后，JSP文件也被转化为Servlets类文件，只不过它不像标准的Servlets一校存在于classes目录中，它存在于WEB容器的临时目录中，故这一步中我们也把它做为Servlets来看。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;对于Servlets，客户端请求它时，WEB容器调用它的JVM来运行Servlet，首先，JVM把Servlet的class类从系统中读出并装入内存中，内存中是以UNICODE编码的Servlet类的代码，然后JVM在内存中运行该Servlet类，如果Servlet在运行的过程中，需要接受从客户端传来的字符如：表单输入的值和URL中传入的值，此时如果程序中没有设定接受参数时采用的编码格式，则WEB容器会默认采用ISO-8859-1编码格式来接受传入的值并在JVM中转化为UNICODE格式的保存在WEB容器的内存中。Servlet运行后生成输出，输出的字符串是UNICODE格式的，紧接着，容器将Servlet运行产生的UNICODE格式的串（如html语法，用户输出的串等）直接发送到客户端浏览器上并输出给用户，如果此时指定了发送时输出的编码格式，则按指定的编码格式输出到浏览器上，如果没有指定，则默认按ISO-8859-1编码发送到客户的浏览器上。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;D、Java程序和数据库之间<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;对于几乎所有数据库的JDBC驱动程序，默认的在JAVA程序和数据库之间传递数据都是以ISO-8859-1为默认编码格式的，所以，我们的程序在向数据库内存储包含中文的数据时，JDBC首先是把程序内部的UNICODE编码格式的数据转化为ISO-8859-1的格式，然后传递到数据库中，在数据库保存数据时，它默认即以ISO-8859-1保存，所以，这是为什么我们常常在数据库中读出的中文数据是乱码。<br _extended="true" />
<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;3、分析常见的JAVA中文问题几个必须清楚的原则<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;首先，经过上面的详细分析，我们可以清晰地看到，任何JAVA程序的生命期中，其编码转换的关键过程是在于：最初编译成class文件的转码和最终向用户输出的转码过程。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;其次，我们必须了解JAVA在编译时支持的、常用的编码格式有以下几种：<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;*ISO-8859-1，8-bit,&nbsp;同8859_1,ISO-8859-1,ISO_8859_1等编码<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;*Cp1252，美国英语编码，同ANSI标准编码<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;*UTF-8，同unicode编码<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;*GB2312，同gb2312-80,gb2312-1980等编码<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;*GBK&nbsp;,&nbsp;同MS936，它是gb2312的扩充<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;及其它的编码，如韩文、日文、繁体中文等。同时，我们要注意这些编码间的兼容关体系如下：<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;unicode和UTF-8编码是一一对应的关系。GB2312可以认为是GBK的子集，即GBK编码是在gb2312上扩展来的。同时，GBK编码包含了20902个汉字，编码范围为：0x8140-0xfefe，所有的字符可以一一对应到UNICODE2.0中来。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;再次，对于放在操作系统中的.java源程序文件，在编译时，我们可以指定它内容的编码格式，具体来说用-encoding来指定。注意：如果源程序中含有中文字符，而你用-encoding指定为其它的编码字符，显然是要出错的。用-encoding指定源文件的编码方式为GBK或gb2312，无论我们在什么系统上编译含有中文字符的JAVA源程序都不会有问题，它都会正确地将中文转化为UNICODE存储在class文件中。<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp;<br _extended="true" />
然后，我们必须清楚，几乎所有的WEB容器在其内部默认的字符编码格式都是以ISO-8859-1为默认值的，同时，几乎所有的浏览器在传递参数时都是默认以UTF-8的方式来传递参数的。所以，虽然我们的Java源文件在出入口的地方指定了正确的编码方式，但其在容器内部运行时还是以ISO-8859-1来处理的。
<img src ="http://www.blogjava.net/jesenblog/aggbug/187280.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesenblog/" target="_blank">白露</a> 2008-03-19 16:13 <a href="http://www.blogjava.net/jesenblog/articles/187280.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jdk1.5泛型 </title><link>http://www.blogjava.net/jesenblog/articles/187279.html</link><dc:creator>白露</dc:creator><author>白露</author><pubDate>Wed, 19 Mar 2008 08:12:00 GMT</pubDate><guid>http://www.blogjava.net/jesenblog/articles/187279.html</guid><wfw:comment>http://www.blogjava.net/jesenblog/comments/187279.html</wfw:comment><comments>http://www.blogjava.net/jesenblog/articles/187279.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesenblog/comments/commentRss/187279.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesenblog/services/trackbacks/187279.html</trackback:ping><description><![CDATA[<p _extended="true"><strong _extended="true">数据类型转换和错误</strong></p>
<p _extended="true">为理解泛型类型为何如此有用，我们要将注意力转向 Java 语言中最容易引发错误的因素之一 －需要不断地将表达式向下类型转换（downcast）为比其静态类型更为具体的数据类型。</p>
<p _extended="true">程序中的每个向下类型转换对于 ClassCastException 而言都是潜在的危险，应当尽量避免它们。但是在 Java 语言中它们通常是无法避免的，即便在设计优良的程序中也是如此。</p>
<p _extended="true">在 Java 语言中进行向下类型转换最常见的原因在于，经常以专用的方式来使用类，这限制了方法调用所返回的参数可能的运行时类型。例如，假定往 Hashtable 中添加元素并从中检索元素。那么在给定的程序中，被用作键的元素类型和存储在散列表中的值类型，将不能是任意对象。通常，所有的键都是某一特定类型的实例。同样地，存储的值将共同具有比 Object 更具体的公共类型。</p>
<p _extended="true">但是在目前现有的 Java 语言版本中，不可能将散列表的特定键和元素声明为比 Object 更具体的类型。在散列表上执行插入和检索操作的类型特征符告诉我们只能插入和删除任意对象。例如， put 和 get 操作的说明如下所示：</p>
<p _extended="true">清单 1. 插入／检索类型说明表明只能是任意对象<br _extended="true" />
class Hashtable {<br _extended="true" />
&nbsp; Object put(Object key, Object value) {...}<br _extended="true" />
&nbsp; Object get(Object key) {...}<br _extended="true" />
&nbsp; ...<br _extended="true" />
}</p>
<p _extended="true">因此，当我们从类 Hashtable 的实例检索元素时，比如，即使我们知道在 Hashtable 中只放了 String ，而类型系统也只知道所检索的值是 Object 类型。在对检索到的值进行任何特定于 String 的操作之前，必须将它强制转换为 String ，即使是将检索到的元素添加到同一代码块中，也是如此！</p>
<p _extended="true">清单 2. 将检索到的值强制转换成 String<br _extended="true" />
import java.util.Hashtable;<br _extended="true" />
class Test {<br _extended="true" />
&nbsp; public static void main(String[] args) {<br _extended="true" />
&nbsp;&nbsp;&nbsp; Hashtable h = new Hashtable();<br _extended="true" />
&nbsp;&nbsp;&nbsp; h.put(new Integer(0), "value");<br _extended="true" />
&nbsp;&nbsp;&nbsp; String s = (String)h.get(new Integer(0));<br _extended="true" />
&nbsp;&nbsp;&nbsp; System.out.println(s);<br _extended="true" />
&nbsp; }<br _extended="true" />
}</p>
<p _extended="true">请注意 main 方法主体部分的第三行中需要进行的数据类型转换。因为 Java 类型系统相当薄弱，因此代码会因象上面那样的数据类型转换而漏洞百出。这些数据类型转换不仅使 Java 代码变得更加拖沓冗长，而且它们还降低了静态类型检查的价值（因为每个数据类型转换都是一个选择忽略静态类型检查的伪指令）。我们该如何扩展该类型系统，从而不必回避它呢？</p>
<p _extended="true"><strong _extended="true">用泛型类型来解决问题！</strong></p>
<p _extended="true">要消除如上所述的数据类型转换，有一种普遍的方法，就是用泛型类型来增大 Java 类型系统。可以将泛型类型看作是类型&#8220;函数&#8221;；它们通过类型变量进行参数化，这些类型变量可以根据上下文用各种类型参数进行实例化。</p>
<p _extended="true">例如，与简单地定义类 Hashtable 不同，我们可以定义泛型类 Hashtable&lt;Key, Value&gt; ，其中 Key 和 Value 是类型参数。除了类名后跟着尖括号括起来的一系列类型参数声明之外，在 Tiger 中定义这样的泛型类的语法和用于定义普通类的语法很相似。例如，可以按照如下所示的那样定义自己的泛型 Hashtable 类：</p>
<p _extended="true">清单 3. 定义泛型 Hashtable 类<br _extended="true" />
class Hashtable&lt;Key, Value&gt; { ... }</p>
<p _extended="true">然后可以引用这些类型参数，就像我们在类定义主体内引用普通类型那样，如下所示：</p>
<p _extended="true">清单 4. 像引用普通类型那样引用类型参数<br _extended="true" />
class Hashtable&lt;Key, Value&gt; {<br _extended="true" />
&nbsp; ...<br _extended="true" />
&nbsp; Value put(Key k, Value v) {...}<br _extended="true" />
&nbsp; Value get(Key k) {...}<br _extended="true" />
}</p>
<p _extended="true">类型参数的作用域就是相应类定义的主体部分（除了静态成员之外）（在下一篇文章中，我们将讨论为何 Tiger 实现中有这样的&#8220;怪习&#8221;，即必须对静态成员进行此项限制。请留意！）。</p>
<p _extended="true">创建一个新的 Hashtable 实例时，必须传递类型参数以指定 Key 和 Value 的类型。传递类型参数的方式取决于我们打算如何使用 Hashtable 。在上面的示例中，我们真正想要做的是创建 Hashtable 实例，它只将 Integer 映射为 String 。可以用新的 Hashtable 类来完成这件事：</p>
<p _extended="true">清单 5. 创建将 Integer 映射为 String 的实例<br _extended="true" />
import java.util.Hashtable;<br _extended="true" />
class Test {<br _extended="true" />
&nbsp; public static void main(String[] args) {<br _extended="true" />
&nbsp;&nbsp;&nbsp; Hashtable&lt;Integer, String&gt; h = new Hashtable&lt;Integer, String&gt;();<br _extended="true" />
&nbsp;&nbsp;&nbsp; h.put(new Integer(0), "value");<br _extended="true" />
&nbsp;&nbsp;&nbsp; ...<br _extended="true" />
&nbsp; }<br _extended="true" />
}<br _extended="true" />
&nbsp;<br _extended="true" />
现在不再需要数据类型转换了。请注意用来实例化泛型类 Hashtable 的语法。就像泛型类的类型参数用尖括号括起来那样，泛型类型应用程序的参数也是用尖括号括起来的。</p>
<p _extended="true">清单 6. 除去不必要的数据类型转换<br _extended="true" />
...<br _extended="true" />
String s = h.get("key");<br _extended="true" />
System.out.println(s);</p>
<p _extended="true">当然，程序员若只是为了能使用泛型类型而必须重新定义所有的标准实用程序类（比如 Hashtable 和 List ）的话，则可能会是一项浩大的工程。幸好，Tiger 为用户提供了所有 Java 集合类的泛型版本，因此我们不必自己动手来重新定义它们了。此外，这些类能与旧代码和新的泛型代码一起无缝工作。</p>
<p _extended="true"><strong _extended="true">Tiger 的基本类型限制</strong></p>
<p _extended="true">Tiger 中类型变量的限制之一就是，它们必须用引用类型进行实例化 －基本类型不起作用。因此，在上面这个示例中，无法完成创建从 int 映射到 String 的 Hashtable 。</p>
<p _extended="true">这很遗憾，因为这意味着只要您想把基本类型用作泛型类型的参数，您就必须把它们组装为对象。另一方面，当前的这种情况是最糟的；您不能将 int 作为键传递给 Hashtable ，因为所有的键都必须是 Object 类型。</p>
<p _extended="true">我们真正想看到的是，基本类型可以自动进行包装（boxing）和解包装（unboxing），类似于用 C# 所进行的操作（或者比后者更好）。遗憾的是，Tiger 不打算包括基本类型的自动包装（hiswing：jdk5.0已经带了自动装箱（Autoboxing）和自动拆箱（Auto-Unboxing））。</p>
<p _extended="true"><strong _extended="true">受限泛型</strong></p>
<p _extended="true">有时我们想限制可能出现的泛型类的类型实例化。在上面这个示例中，类 Hashtable 的类型参数可以用我们想用的任何类型参数进行实例化，但是对于其它某些类，我们或许想将可能的类型参数集限定为给定类型范围内的子类型。</p>
<p _extended="true">例如，我们可能想定义泛型 ScrollPane 类，它引用普通的带有滚动条功能的 Pane 。被包含的 Pane 的运行时类型通常会是类 Pane 的子类型，但是静态类型就只是 Pane 。</p>
<p _extended="true">有时我们想用 getter 检索被包含的 Pane ，但是希望 getter 的返回类型尽可能具体些。我们可能想将类型参数 MyPane 添加到 ScrollPane 中，该类型参数可以用 Pane 的任何子类进行实例化。然后可以用这种形式的子句： extends Bound 来说明 MyPane 的声明，从而来设定 MyPane 的范围：</p>
<p _extended="true">清单 7. 用 extends 子句来说明 MyPane 声明<br _extended="true" />
class ScrollPane&lt;MyPane extends Pane&gt; { ... }</p>
<p _extended="true">当然，我们可以完全不使用显式的范围，只要能确保没有用不适当的类型来实例化类型参数。</p>
<p _extended="true">为什么要自找麻烦在类型参数上设定范围呢？这里有两个原因。首先，范围使我们增加了静态类型检查功能。有了静态类型检查，就能保证泛型类型的每次实例化都符合所设定的范围。</p>
<p _extended="true">其次，因为我们知道类型参数的每次实例化都是这个范围之内的子类，所以可以放心地调用类型参数实例出现在这个范围之内的任何方法。如果没有对参数设定显式的范围，那么缺省情况下范围是 Object ，这意味着我们不能调用范围实例在 Object 中未曾出现的任何方法。</p>
<p _extended="true"><strong _extended="true">多态方法</strong></p>
<p _extended="true">除了用类型参数对类进行参数化之外，用类型参数对方法进行参数化往往也同样很有用。泛型 Java 编程用语中，用类型进行参数化的方法被称为 多态方法（Polymorphic method）。</p>
<p _extended="true">多态方法之所以有用，是因为有时候，在一些我们想执行的操作中，参数与返回值之间的类型相关性原本就是泛型的，但是这个泛型性质不依赖于任何类级的类型信息，而且对于各个方法调用都不相同。</p>
<p _extended="true">例如，假定想将 factory 方法添加到 List 类中。这个静态方法只带一个参数，也将是 List 唯一的元素（直到添加了其它元素）。因为我们希望 List 成为其所包含的元素类型的泛型，所以希望静态 factory 方法带有类型变量 T 这一参数并返回 List&lt;T&gt; 的实例。</p>
<p _extended="true">但是我们确实希望该类型变量 T 能在方法级别上进行声明，因为它会随每次单独的方法调用而发生改变（而且，正如我在下一篇文章中将讨论的那样，Tiger 设计的&#8220;怪习&#8221;规定静态成员不在类级类型参数的范畴之内）。Tiger 让我们通过将类型参数作为方法声明的前缀，从而在单独的方法级别上声明类型参数。例如，可以按照如下所示的那样为 factory 方法 make 添加前缀：</p>
<p _extended="true">清单 8. 将类型参数作为前缀添加到方法声明<br _extended="true" />
class Utilities {<br _extended="true" />
&nbsp;&nbsp; &lt;T extends Object&gt; public static List&lt;T&gt; make(T first) {<br _extended="true" />
&nbsp;&nbsp;&nbsp;&nbsp; return new List&lt;T&gt;(first);<br _extended="true" />
&nbsp;&nbsp; }<br _extended="true" />
}</p>
<p _extended="true">除了多态方法中所增加的灵活性之外，Tiger 中还增加了一个优点。Tiger 使用类型推断机制，根据参数类型来自动推断出多态方法的类型。这可以大大减少方法调用的繁琐和复杂性。例如，如果想调用 make 方法来构造包含 new Integer(0) 的 List&lt;Integer&gt; 新实例，那么只需编写：</p>
<p _extended="true">清单 9. 强制 make 构造新实例<br _extended="true" />
Utilities.make(Integer(0))</p>
<p _extended="true">然后会自动地从方法参数中推断出类型参数的实例化。</p>
<p _extended="true"><strong _extended="true">结束语</strong></p>
<p _extended="true">正如我们所见到的那样，在 Java 语言中添加泛型类型肯定会大大增强我们使用静态类型系统的能力。学习如何使用泛型类型相当简单，但是同样也需要避免一些缺陷。在接下来的文章中，我们将讨论如何充分使用将出现在 Tiger 中的泛型类型的特定表现，以及一些缺陷。我们还将研究对泛型 Java 类型工具的扩展，我们期盼这些工具可以出现在仍处于设计阶段的 Java 平台之中。</p>
&nbsp;
<p _extended="true"><strong _extended="true">泛型类型的限制</strong></p>
<p _extended="true">让我们先查阅一下 Tiger 和 JSR-14 中泛型类型的使用限制：</p>
<p _extended="true">不应在静态成员中引用封闭类型参数。<br _extended="true" />
不能用基本类型实例化泛型类型参数。<br _extended="true" />
不能在数据类型转换或 instanceof 操作中使用&#8220;外露&#8221;类型参数。<br _extended="true" />
不能在 new 操作中使用&#8220;外露&#8221;类型参数。<br _extended="true" />
不能在类定义的 implements 或 extends 子句中使用&#8220;外露&#8221;类型参数。<br _extended="true" />
为什么会有这些限制呢？这要归因于 Tiger 和 JSR-14 为在 JVM 上实现泛型类型所使用的机制。由于 JVM 根本不支持泛型类型，所以这些编译器&#8220;耍了个花招&#8221;，使得似乎存在对泛型类型的支持 ― 它们用泛型类型信息检查所有的代码，但随即&#8220;擦除&#8221;所有的泛型类型并生成只包含普通类型的类文件。</p>
<p _extended="true">例如，将象 List&lt;T&gt; 这样的泛型类型擦除得只剩下 List 。&#8220;外露&#8221;类型参数 ― 单独出现而不是位于某个类型中的类型参数（如类 List&lt;T&gt; 中的类型参数 T ）― 被简单地擦除成它们的上界（就 T 而言，其上界就是 Object ）。</p>
<p _extended="true">这一技术的功能极其强大；我们可以使几乎所有泛型类型的精度得到增强，但又与 JVM 保持兼容。事实上，我们甚至可以交替地使用非泛型的旧类（比如 List ）和其对应的泛型类（ List&lt;T&gt; ）；两者在运行时看起来是一样的。</p>
<p _extended="true">遗憾的是，正如以上的限制所示，获得这一功能是有代价的。以这种方式进行擦除在类型系统中引入了缺陷，这些缺陷限制我们使用泛型类型的安全性。</p>
<p _extended="true">为了帮助说明每种限制，我们查阅会出现这些限制的示例。在本文中，我们将讨论前三个限制。与后两个限制有关的问题过于复杂，因而需要更深入的研究，留待下一篇文章讨论。</p>
<p _extended="true"><strong _extended="true">静态成员中的封闭类型参数</strong></p>
<p _extended="true">编译器完全禁止在静态方法和静态内部类中引用封闭类型参数。所以，举例来说，以下代码在 Tiger 中就是非法的：</p>
<p _extended="true">清单 1. 在静态上下文中非法引用封闭类型参数<br _extended="true" />
class C&lt;T&gt; {<br _extended="true" />
&nbsp; static void m() {<br _extended="true" />
&nbsp;&nbsp;&nbsp; T t;<br _extended="true" />
&nbsp; }</p>
<p _extended="true">&nbsp; static class D {<br _extended="true" />
&nbsp;&nbsp;&nbsp; C&lt;T&gt; t;<br _extended="true" />
&nbsp; }<br _extended="true" />
}</p>
<p _extended="true">当编译这一代码时，会生成两个错误：</p>
<p _extended="true">在静态方法 m 中非法引用 T 的错误<br _extended="true" />
在静态类 D 中非法引用 T 的错误</p>
<p _extended="true">当定义静态字段时，情况变得更加复杂。在 JSR-14 和 Tiger 中，在泛型类的所有实例中共享该类中的静态字段。现在，在 JSR-14 编译器 1.0 和 1.2 中，如果您在静态字段声明中引用类型参数，编译器不会报错，但它本应该这么做。字段被共享这一事实很容易在运行时导致奇怪的错误，如在不包含数据类型转换的代码中出现 ClassCastException 。</p>
<p _extended="true">例如，以下程序将在这两个版本的 JSR-14 下通过编译而没有任何警告：</p>
<p _extended="true">清单 2. 在静态字段中对封闭类型参数的有问题的引用<br _extended="true" />
class C&lt;T&gt; {<br _extended="true" />
&nbsp; static T member;</p>
<p _extended="true">&nbsp; C(T t) { member = t; }</p>
<p _extended="true">&nbsp; T getMember() { return member; }</p>
<p _extended="true">&nbsp; public static void main(String[] args) {<br _extended="true" />
&nbsp;&nbsp;&nbsp; C&lt;String&gt; c = new C&lt;String&gt;("test");<br _extended="true" />
&nbsp;&nbsp;&nbsp; System.out.println(c.getMember().toString());<br _extended="true" />
&nbsp;&nbsp;&nbsp; new C&lt;Integer&gt;(new Integer(1));<br _extended="true" />
&nbsp;&nbsp;&nbsp; System.out.println(c.getMember().toString());<br _extended="true" />
&nbsp; }<br _extended="true" />
}</p>
<p _extended="true">请注意，每次分配类 C 的实例时，都要重新设置静态字段 member 。而且，它被设置成的对象类型取决于 C 的实例的类型！在所提供的 main 方法中，第一个实例 c 是 C&lt;String&gt; 类型。而第二个是 C&lt;Integer&gt; 类型。每当从 c 访问 member 这一共享静态字段时，总是假定 member 的类型是 String 。但是，在分配了类型为 C&lt;Integer&gt; 的第二个实例之后， member 的类型是 Integer 。</p>
<p _extended="true">运行 C 的 main 方法的结果可能会让您吃惊 ― 它将发出一个 ClassCastException ！源代码根本没有包含任何数据类型转换，怎么会这样呢？事实证明编译器确实在编译阶段将数据类型转换插入到代码中，这样做是为了解决类型擦除会降低某些表达式的类型的精度这一事实。这些数据类型转换被期望能够成功，但在本例中却没有成功。</p>
<p _extended="true">应该认为 JSR-14 1.0 和 1.2 的这一特殊&#8220;功能&#8221;是个错误。它破坏了类型系统的健全性，或者可以说，它破坏了类型系统应该和程序员达成的&#8220;基本契约&#8221;。象对静态方法和类所做的那样，只要防止程序员在静态字段中引用泛型类型，情况就会好很多。</p>
<p _extended="true">请注意允许这种有潜在&#8220;爆炸性&#8221;的代码存在所带来的问题并不是程序员有意在自己的代码中覆盖类型系统。问题是程序员可能会无意中编写这样的代码（比如，由于&#8220;复制和粘贴&#8221;操作，错误地在字段声明中包括静态修饰符）。</p>
<p _extended="true">类型检查器应该能帮助程序员从这些类型的错误中恢复，但对于静态字段而言，类型系统实际上会使程序员更迷惑。当未使用数据类型转换的代码中显示的唯一错误就是 ClassCastException 时，我们应如何诊断这样的错误？对于不清楚 Tiger 中泛型类型所用的实现方案而又恰好假定类型系统合理运行的程序员而言，情况更糟。因为在这样的情况下，类型系统不是合理地运行。</p>
<p _extended="true">幸运的是，JSR-14 的最新版本（1.3）宣布在静态字段中使用类型参数是不合法的。因此，我们有理由期待在 Tiger 的静态字段中使用类型参数也是不合法的。</p>
<p _extended="true"><strong _extended="true">泛型类型参数和基本类型</strong></p>
<p _extended="true">和我们刚才讨论的不同，这一限制没有同样的潜在缺陷，但它会使您的代码非常冗长。例如，在 java.util.Hashtable 的泛型版本中，有两种类型参数：用于 Key 类型的和用于 Value 类型的。因此，如果我们想要一个将 String 映射到 String 的 Hashtable ，我们可以用表达式 new Hashtable&lt;String, String&gt;() 指定新的实例。但是，如果我们想要一个将 String 映射到 int 的 Hashtable ，我们只能创建 Hashtable&lt;String, Integer&gt; 的实例，并将所有的 int 值包装在 Integer 中。</p>
<p _extended="true">同样，Tiger 在这方面当然也是由所用的实现方案得到的。既然类型参数被擦除为它们的界限，而界限不能是基本类型，所以一旦类型被擦除，则对基本类型的实例化会完全没有意义。</p>
<p _extended="true">数据类型转换或 instanceof 操作中的&#8220;外露&#8221;参数</p>
<p _extended="true">回想一下，对于&#8220;外露&#8221;类型参数，我们是指在词汇上单独出现的类型参数，而不是更大类型的语法子组件。例如， C&lt;T&gt; 不是&#8220;外露&#8221;类型参数，但（在 C 主体中） T 是。</p>
<p _extended="true">如果在代码中对&#8220;外露&#8221;类型参数进行数据类型转换或 instanceof 操作，则编译器将发出名为&#8220;unchecked&#8221;的警告。例如，以下代码将生成警告： Warning: unchecked cast to type T ：</p>
<p _extended="true">清单 3. 带 unchecked 操作的泛型代码<br _extended="true" />
import java.util.Hashtable;<br _extended="true" />
interface Registry {<br _extended="true" />
&nbsp; public void register(Object o);<br _extended="true" />
}<br _extended="true" />
class C&lt;T&gt; implements Registry {<br _extended="true" />
&nbsp; int counter = 0;<br _extended="true" />
&nbsp; Hashtable&lt;Integer, T&gt; values;</p>
<p _extended="true">&nbsp; public C() {<br _extended="true" />
&nbsp;&nbsp;&nbsp; values = new Hashtable&lt;Integer, T&gt;();<br _extended="true" />
&nbsp; }</p>
<p _extended="true">&nbsp; public void register(Object o) {<br _extended="true" />
&nbsp;&nbsp;&nbsp; values.put(new Integer(counter), (T)o);<br _extended="true" />
&nbsp;&nbsp;&nbsp; counter++;<br _extended="true" />
&nbsp; }<br _extended="true" />
}</p>
<p _extended="true">您应该严肃地对待这些警告，因为它们说明您的代码在运行时会表现得非常奇怪。事实上，它们会使得诊断代码变得极为困难。在以前的代码中，我们认为如果对实例 C&lt;JFrame&gt; 调用 register("test") ，会发出 ClassCastException 。但并非如此；计算将继续，就仿佛数据类型转换成功了一样，然后在进一步进行计算时发出错误，或者更糟：用遭破坏的数据完成计算，但不向外发出任何错误信号。同样，对&#8220;外露&#8221;类型参数的 instanceof 检查将在编译时产生&#8220;unchecked&#8221;警告，而且检查将不会如期在运行时进行。</p>
<p _extended="true"><strong _extended="true">双刃剑</strong></p>
<p _extended="true">那么，这里到底发生了什么？因为 Tiger 依靠类型擦除，所以数据类型转换和 instanceof 测试中的外露类型参数被&#8220;擦除&#8221;为它们的上界（在前面的例子中，那将是类型 Object ）。因此，对类型参数的数据类型转换将变成对参数上界的转换。</p>
<p _extended="true">同样， instanceof 将检查操作数是否是参数界限的 instanceof 。那根本不是我们打算做的，如果是的话，我们完全可以显式地强制转换为界限。因此，通常应避免对类型参数使用数据类型转换和 instanceof 检查。</p>
<p _extended="true">然而，有时为了编译代码，您必须依靠对类型参数的数据类型转换。如果是这样的情况，只要记住，在代码的那一部分中，类型检查不保险 ― 要靠自己。</p>
<p _extended="true">尽管泛型类型是制作健壮代码的强大武器，但我们已经演示了误用它们会使代码不再健壮而且极难诊断和修正。下次，我们将介绍 Tiger 中泛型类型的后两个限制，并讨论试图在泛型 Java 类型系统中包括它们时必定会出现的一些问题。</p>
<img src ="http://www.blogjava.net/jesenblog/aggbug/187279.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesenblog/" target="_blank">白露</a> 2008-03-19 16:12 <a href="http://www.blogjava.net/jesenblog/articles/187279.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中抽象类和接口的区别</title><link>http://www.blogjava.net/jesenblog/articles/187278.html</link><dc:creator>白露</dc:creator><author>白露</author><pubDate>Wed, 19 Mar 2008 08:11:00 GMT</pubDate><guid>http://www.blogjava.net/jesenblog/articles/187278.html</guid><wfw:comment>http://www.blogjava.net/jesenblog/comments/187278.html</wfw:comment><comments>http://www.blogjava.net/jesenblog/articles/187278.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesenblog/comments/commentRss/187278.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesenblog/services/trackbacks/187278.html</trackback:ping><description><![CDATA[Abstractclass和interface是Java语言中对于抽象类定义进行支持的两种机制，正是由于这两种机制的存在，才赋予了Java强大的面向对象能力。abstractclass和interface之间在对于抽象类定义的支持方面具有很大的相似性，甚至可以相互替换，因此很多开发者在进行抽象类定义时对于abstractclass和interface的选择显得比较随意。其实，两者之间还是有很大的区别的，对于它们的选择甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。本文将对它们之间的区别进行一番剖析，试图给开发者提供一个在二者之间进行选择的依据。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
理解抽象类<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
abstractclass和interface在Java语言中都是用来进行抽象类（本文中的抽象类并非从abstractclass翻译而来，它表示的是一个抽象体，而abstractclass为Java语言中用于定义抽象类的一种方法，请读者注意区分）定义的，那么什么是抽象类，使用抽象类能为我们带来什么好处呢？<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
在面向对象的概念中，我们知道所有的对象都是通过类来描绘的，但是反过来却不是这样。并不是所有的类都是用来描绘对象的，如果一个类中没有包含足够的信息来描绘一个具体的对象，这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、设计中得出的抽象概念，是对一系列看上去不同，但是本质上相同的具体概念的抽象。比如：如果我们进行一个图形编辑软件的开发，就会发现问题领域存在着圆、三角形这样一些具体概念，它们是不同的，但是它们又都属于形状这样一个概念，形状这个概念在问题领域是不存在的，它就是一个抽象概念。正是因为抽象的概念在问题领域没有对应的具体概念，所以用以表征抽象概念的抽象类是不能够实例化的。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
在面向对象领域，抽象类主要用来进行类型隐藏。我们可以构造出一个固定的一组行为的抽象描述，但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类，而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体，因此它可以是不允许修改的；同时，通过从这个抽象体派生，也可扩展此模块的行为功能。熟悉OCP的读者一定知道，为了能够实现面向对象设计的一个最核心的原则OCP(Open-ClosedPrinciple)，抽象类是其中的关键所在。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
从语法定义层面看abstractclass和interface<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
在语法层面，Java语言对于abstractclass和interface给出了不同的定义方式，下面以定义一个名为Demo的抽象类为例来说明这种不同。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
使用abstractclass的方式定义Demo抽象类的方式如下：<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
abstractclassDemo｛abstractvoidmethod1();abstractvoidmethod2();&#8230;｝<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
使用interface的方式定义Demo抽象类的方式如下：<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
interfaceDemo{voidmethod1();voidmethod2();&#8230;}<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
在abstractclass方式中，Demo可以有自己的数据成员，也可以有非abstarct的成员方法，而在interface方式的实现中，Demo只能够有静态的不能被修改的数据成员（也就是必须是staticfinal的，不过在interface中一般不定义数据成员），所有的成员方法都是abstract的。从某种意义上说，interface是一种特殊形式的abstractclass。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
从编程的角度来看，abstractclass和interface都可以用来实现"designbycontract"的思想。但是在具体的使用上面还是有一些区别的。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
首先，abstractclass在Java语言中表示的是一种继承关系，一个类只能使用一次继承关系。但是，一个类却可以实现多个interface。也许，这是Java语言的设计者在考虑Java对于多重继承的支持方面的一种折中考虑吧。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
其次，在abstractclass的定义中，我们可以赋予方法的默认行为。但是在interface的定义中，方法却不能拥有默认行为，为了绕过这个限制，必须使用委托，但是这会增加一些复杂性，有时会造成很大的麻烦。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
在抽象类中不能定义默认行为还存在另一个比较严重的问题，那就是可能会造成维护上的麻烦。因为如果后来想修改类的界面（一般通过abstractclass或者interface来表示）以适应新的情况（比如，添加新的方法或者给已用的方法中添加新的参数）时，就会非常的麻烦，可能要花费很多的时间（对于派生类很多的情况，尤为如此）。但是如果界面是通过abstractclass来实现的，那么可能就只需要修改定义在abstractclass中的默认行为就可以了。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
同样，如果不能在抽象类中定义默认行为，就会导致同样的方法实现出现在该抽象类的每一个派生类中，违反了"onerule，oneplace"原则，造成代码重复，同样不利于以后的维护。因此，在abstractclass和interface间进行选择时要非常的小心。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
从设计理念层面看abstractclass和interface<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
上面主要从语法定义和编程的角度论述了abstractclass和interface的区别，这些层面的区别是比较低层次的、非本质的。本小节将从另一个层面：abstractclass和interface所反映出的设计理念，来分析一下二者的区别。作者认为，从这个层面进行分析才能理解二者概念的本质所在。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
前面已经提到过，abstarctclass在Java语言中体现了一种继承关系，要想使得继承关系合理，父类和派生类之间必须存在"isa"关系，即父类和派生类在概念本质上应该是相同的（参考文献〔3〕中有关于"isa"关系的大篇幅深入的论述，有兴趣的读者可以参考）。对于interface来说则不然，并不要求interface的实现者和interface定义在概念本质上是一致的，仅仅是实现了interface定义的契约而已。为了使论述便于理解，下面将通过一个简单的实例进行说明。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
考虑这样一个例子，假设在我们的问题领域中有一个关于Door的抽象概念，该Door具有执行两个动作open和close，此时我们可以通过abstractclass或者interface来定义一个表示该抽象概念的类型，定义方式分别如下所示：<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
使用abstractclass方式定义Door：<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
abstractclassDoor{abstractvoidopen();abstractvoidclose()；}<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
使用interface方式定义Door：<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
interfaceDoor{voidopen();voidclose();}<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
其他具体的Door类型可以extends使用abstractclass方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstractclass和interface没有大的区别。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢（在本例中，主要是为了展示abstractclass和interface反映在设计理念上的区别，其他方面无关的问题都做了简化或者忽略）？下面将罗列出可能的解决方案，并从设计理念层面对这些不同的方案进行分析。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
解决方案一：<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
简单的在Door的定义中增加一个alarm方法，如下：<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
abstractclassDoor{abstractvoidopen();abstractvoidclose()；abstractvoidalarm();}<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
或者<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
interfaceDoor{voidopen();voidclose();voidalarm();}<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
那么具有报警功能的AlarmDoor的定义方式如下：<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
classAlarmDoorextendsDoor{voidopen(){&#8230;}voidclose(){&#8230;}voidalarm(){&#8230;}}<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
或者<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
classAlarmDoorimplementsDoor｛voidopen(){&#8230;}voidclose(){&#8230;}voidalarm(){&#8230;}｝<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
这种方法违反了面向对象设计中的一个核心原则ISP（InterfaceSegregationPriciple），在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变（比如：修改alarm方法的参数）而改变，反之依然。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
解决方案二：<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
既然open、close和alarm属于两个不同的概念，根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有：这两个概念都使用abstractclass方式定义；两个概念都使用interface方式定义；一个概念使用abstractclass方式定义，另一个概念使用interface方式定义。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
显然，由于Java语言不支持多重继承，所以两个概念都使用abstractclass方式定义是不可行的。后面两种方式都是可行的，但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
如果两个概念都使用interface方式来定义，那么就反映出两个问题：1、我们可能没有理解清楚问题领域，AlarmDoor在概念本质上到底是Door还是报警器？2、如果我们对于问题领域的理解没有问题，比如：我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的，那么我们在实现时就没有能够正确的揭示我们的设计意图，因为在这两个概念的定义上（均使用interface方式定义）反映不出上述含义。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
如果我们对于问题领域的理解是：AlarmDoor在概念本质上是Door，同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢？前面已经说过，abstractclass在Java语言中表示一种继承关系，而继承关系在本质上是"isa"关系。所以对于Door这个概念，我们应该使用abstarctclass方式来定义。另外，AlarmDoor又具有报警功能，说明它又能够完成报警概念中定义的行为，所以报警概念可以通过interface方式定义。如下所示：<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
abstractclassDoor{abstractvoidopen();abstractvoidclose()；}interfaceAlarm{voidalarm();}classAlarmDoorextendsDoorimplementsAlarm{voidopen(){&#8230;}voidclose(){&#8230;}voidalarm(){&#8230;}}<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
这种实现方式基本上能够明确的反映出我们对于问题领域的理解，正确的揭示我们的设计意图。其实abstractclass表示的是"isa"关系，interface表示的是"likea"关系，大家在选择时可以作为一个依据，当然这是建立在对问题领域的理解上的，比如：如果我们认为AlarmDoor在概念本质上是报警器，同时又具有Door的功能，那么上述的定义方式就要反过来了。
<img src ="http://www.blogjava.net/jesenblog/aggbug/187278.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesenblog/" target="_blank">白露</a> 2008-03-19 16:11 <a href="http://www.blogjava.net/jesenblog/articles/187278.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MyEclipse6.0配Eclipse3.3全新体验</title><link>http://www.blogjava.net/jesenblog/articles/187276.html</link><dc:creator>白露</dc:creator><author>白露</author><pubDate>Wed, 19 Mar 2008 08:10:00 GMT</pubDate><guid>http://www.blogjava.net/jesenblog/articles/187276.html</guid><wfw:comment>http://www.blogjava.net/jesenblog/comments/187276.html</wfw:comment><comments>http://www.blogjava.net/jesenblog/articles/187276.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesenblog/comments/commentRss/187276.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesenblog/services/trackbacks/187276.html</trackback:ping><description><![CDATA[<div class="tit" _extended="true">MyEclipse6.0配Eclipse3.3全新体验</div>
<div class="date" _extended="true">2007-08-27 22:28</div>
<table style="table-layout: fixed" _extended="true">
    <tbody _extended="true">
        <tr _extended="true">
            <td _extended="true">
            <div class="cnt" _extended="true">
            <p _extended="true">惊叹高手们的破解速度，今天装了MyEclipse6.0感觉还不错，找了两个注册码和大家分享，注意，注册的时候不要连网，为此我装了3遍MyEclipse，希望大家不要走我的弯路哦~</p>
            <p _extended="true"><a href="http://www.1cn.biz/" _extended="true">www.1cn.biz</a><br _extended="true" />
            jLR8ZC-655355-5450765457039125<br _extended="true" />
            jLR7ZL-655355-5450755330522962</p>
            <p _extended="true">本人用的是第一个，第二个没试，应该没问题，下面对Eclipse3.3和MyEclipse6.0的安装及简单应用做个介绍：</p>
            <p _extended="true">Eclipse是一个免费的开发工具，最早是由IBM开发，之后转送给Eclipse组织----免费滴<br _extended="true" />
            Eclipse的中文含义是日食的意思（遮盖一切光芒），sun公司对此有很大意见：）<br _extended="true" />
            可以从<a href="http://www.eclipse.org/" _extended="true">www.eclipse.org</a>下载到Eclipse最新版本哦~<br _extended="true" />
            Eclipse 3.3版本是一个绿色的免安装软件，把eclipse-SDK-3.3-win32直接解压，完成开始运行主程序<br _extended="true" />
            一上来它会让你选择你的工作区Workspace：你可以任意选择磁盘保存的位置，例如f:\xuexi点ok<br _extended="true" />
            可以在右边点图标切换各种视图，可以按照个人需要切换<br _extended="true" />
            Eclipse本身包含以下内容：<br _extended="true" />
            CVS的客户端、JAVA开发环境---JDT、Eclipse插件开发环境、Junit测试<br _extended="true" />
            1、建立java项目<br _extended="true" />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在开发中，应该按照以下的目录存放文件：<br _extended="true" />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; src 存放全部的java文件<br _extended="true" />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bin 存放全部的class文件<br _extended="true" />
            注：当删除java项目的时候第一个是彻底删除，第二个是只从目录结构中删除<br _extended="true" />
            window-Preferences...-java-Build Path中把Folders选中，ok<br _extended="true" />
            再次选择新建项目，命名后直接点Finish完成，在回到资源视图下，可以看见有bin和src目录了，下面开发java程序：<br _extended="true" />
            对src点右键，新建class，Package输入包名<br _extended="true" />
            Name输入类名，下面选择可以选择继承哪个父类，实现哪个接口等等点确定后进行编写代码<br _extended="true" />
            编写代码后可以点资源视图，会发现bin下自动生成class文件，可以直接右键点class文件，选择Run As-New Application运行class程序<br _extended="true" />
            在开发中，可以自动生成getter和setter方法<br _extended="true" />
            窍门：可以按ctrl+1自动对错误程序进行调整<br _extended="true" />
            Eclipse本身集成了JUnit测试工具<br _extended="true" />
            JUnit是一种专门的测试java程序运行结果的工具<br _extended="true" />
            著名的GreenBar:只要出现了GreenBar就表示测试成功，代码的运行正确<br _extended="true" />
            如果代码测试不正确，则表示redBar，代码不正确<br _extended="true" />
            </p>
            <p _extended="true">Eclipse本身不具备任何的j2ee开发环境，可以使用MyEclipse插件进行j2ee的程序开发<br _extended="true" />
            本文以MyEclipse5.0为例，下载地址为：<a href="http://www.myeclipseide.com/" _extended="true">http://www.myeclipseide.com</a><br _extended="true" />
            安装j2ee的开发插件，本身是收费的，安装插件时候注意选择Eclipse的安装目录<br _extended="true" />
            Choose install Folder会让你选择，MyEclipse的安装路径直到完成。。。<br _extended="true" />
            如果要使用MyEclipse必须注册：<br _extended="true" />
            窗口-&gt;首选项-&gt;MyEclipse-&gt;输入注册码：<br _extended="true" />
            可以进入Eclipse，选择帮助，查看是否安装好MyEclipse<br _extended="true" />
            window-Preferences...-MyEclipse-Subscription-Enter Subscription...输入验证码</p>
            <p _extended="true">通过MyEclipse可以直接建立好符合j2ee标准的web视图<br _extended="true" />
            窍门：按ctrl+d可以删除一行jsp代码<br _extended="true" />
            使用MyEclipse运行web程序有两种方法：<br _extended="true" />
            1、直接通过MyEclipse在服务器上运行部署<br _extended="true" />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在MyEclipse中是可以驱动tomcat服务器<br _extended="true" />
            2、通过外接服务器的方式运行程序（推荐！！）<br _extended="true" />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 通过修改conf/server.xml文件<br _extended="true" />
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 例如：&lt;Context path="renyiming" doBase="F:\web\WebRoot" reloadable="true"/&gt;</p>
            <div class="middleSize" _extended="true">
            <div _extended="true"><u _extended="true">My</u><a class="keyurl" href="http://www.it130.net/Eclipse/index.htm" target="_blank" _extended="true"><font color="#222266" _extended="true">Eclipse</font></a>打开已有工程：</div>
            <div _extended="true">首先要保证<u _extended="true">My</u><a class="keyurl" href="http://www.it130.net/Eclipse/index.htm" target="_blank" _extended="true"><font color="#222266" _extended="true">Eclipse</font></a>两个文件.classpath和.project还在，不然无法导入，就是说<u _extended="true">My</u><a class="keyurl" href="http://www.it130.net/Eclipse/index.htm" target="_blank" _extended="true"><font color="#222266" _extended="true">Eclipse</font></a>的import只认自己家的东西<br _extended="true" />
            然后打开<u _extended="true">My</u><a class="keyurl" href="http://www.it130.net/Eclipse/index.htm" target="_blank" _extended="true"><font color="#222266" _extended="true">Eclipse</font></a>File-&gt;Import...选择General下的Existing Projects into Workspaces<br _extended="true" />
            Next<br _extended="true" />
            选择Project所在的文件夹，只选文件夹就可以</div>
            </div>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/jesenblog/aggbug/187276.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesenblog/" target="_blank">白露</a> 2008-03-19 16:10 <a href="http://www.blogjava.net/jesenblog/articles/187276.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中入门需要掌握的30个基本概念</title><link>http://www.blogjava.net/jesenblog/articles/187275.html</link><dc:creator>白露</dc:creator><author>白露</author><pubDate>Wed, 19 Mar 2008 08:08:00 GMT</pubDate><guid>http://www.blogjava.net/jesenblog/articles/187275.html</guid><wfw:comment>http://www.blogjava.net/jesenblog/comments/187275.html</wfw:comment><comments>http://www.blogjava.net/jesenblog/articles/187275.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesenblog/comments/commentRss/187275.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesenblog/services/trackbacks/187275.html</trackback:ping><description><![CDATA[&nbsp;
<div class="toggleLink" id="articleBar" _extended="true" toggle="articleContentArea">
<div class="article_info" _extended="true"><a class="toggleLink collapseArraw center" id="collapseButton" title="展开或收缩" href="javascript:void(0)" _extended="true" toggle="articleContentArea"><input type="button" _extended="true" /></a> <!--class name of articleTime specify 'sun' icon or 'moon' icon
					in front of date-time text. Corresponding to class name 'sun' & 'moon'
					-->
<div class="articleTitleW" _extended="true"><a id="articleTitle" _extended="true">Java中入门需要掌握的30个基本概念</a></div>
</div>
</div>
<div id="articleContentArea" _extended="true">
<div id="articleTimeHolder" _extended="true"><span class="moon icon textIcon" id="articleTime" style="background-position: 2px 3px" _extended="true">2007-11-27 19:49:17</span> </div>
<div id="articleContentFontSize" _extended="true"><a onclick="setArticleContentSize('b')" href="javascript: void(0);" _extended="true">大</a> <a onclick="setArticleContentSize('m')" href="javascript: void(0);" _extended="true"  )>中</a> <a onclick="setArticleContentSize('s')" href="javascript: void(0);" _extended="true"  )>小</a> </div>
<div id="tagsArea" _extended="true">标签：<a class="tag" href="http://search.blog.sina.com.cn/blog/search?q=%D6%AA%CA%B6%2F%CC%BD%CB%F7&amp;tag=n&amp;t=tag" target="_blank" _extended="true">知识/探索</a> </div>
<div class="middleSize" id="articleContent" _extended="true">
<div _extended="true">&nbsp;
<div class="posthead" _extended="true">
<h2 _extended="true"><a class="singleposttitle" id="AjaxHolder_ctl01_TitleUrl" href="http://www.cnblogs.com/wjun530/archive/2007/09/17/895295.html" name="AjaxHolder_ctl01_TitleUrl" _extended="true">Java中入门需要掌握的30个基本概念</a></h2>
</div>
<div class="postbody" _extended="true">1.OOP中唯一关系的是对象的接口是什么，就像计算机的销售商她不管电源内部结构是怎样的，他只关系能否给你提供电就行了，也就是只要知道can or not而不是how and why.所有的程序是由一定的属性和行为对象组成的，不同的对象的访问通过函数调用来完成，对象间所有的交流都是通过方法调用，通过对封装对象数据，很大限度上提高复用率。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
2.OOP中最重要的思想是类，类是模板是蓝图，从类中构造一个对象，即创建了这个类的一个实例(instance)。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
3.封装:就是把数据和行为结合起在一个包中)并对对象使用者隐藏数据的实现过程，一个对象中的数据叫他的实例字段(instance field)。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
4.通过扩展一个类来获得一个新类叫继承(inheritance)，而所有的类都是由Object根超类扩展而得，根超类下文会做介绍。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
5.对象的3个主要特性<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
behavior---说明这个对象能做什么.<br _extended="true" />
<br _extended="true" />
state---当对象施加方法时对象的反映.<br _extended="true" />
<br _extended="true" />
identity---与其他相似行为对象的区分标志.<br _extended="true" />
<br _extended="true" />
每个对象有唯一的indentity 而这3者之间相互影响.<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
6.类之间的关系:<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
use-a :依赖关系<br _extended="true" />
<br _extended="true" />
has-a :聚合关系<br _extended="true" />
<br _extended="true" />
is-a :继承关系--例:A类继承了B类，此时A类不仅有了B类的方法，还有其自己的方法.(个性存在于共性中)<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
7.构造对象使用构造器:构造器的提出，构造器是一种特殊的方法，构造对象并对其初始化。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
例:Data类的构造器叫Data<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
new Data()---构造一个新对象，且初始化当前时间.<br _extended="true" />
<br _extended="true" />
Data happyday=new Data()---把一个对象赋值给一个变量happyday，从而使该对象能够多次使用，此处要声明的使变量与对象变量二者是不同的.new返回的值是一个引用。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
构造器特点:构造器可以有0个，一个或多个参数<br _extended="true" />
<br _extended="true" />
构造器和类有相同的名字<br _extended="true" />
<br _extended="true" />
一个类可以有多个构造器<br _extended="true" />
<br _extended="true" />
构造器没有返回值<br _extended="true" />
<br _extended="true" />
构造器总是和new运算符一起使用.<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
8.重载:当多个方法具有相同的名字而含有不同的参数时，便发生重载.编译器必须挑选出调用哪个方法。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
9.包(package)Java允许把一个或多个类收集在一起成为一组，称作包，以便于组织任务，标准Java库分为许多包.java.lang java.util java，net等，包是分层次的所有的java包都在java和javax包层次内。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
10.继承思想:允许在已经存在的类的基础上构建新的类，当你继承一个已经存在的类时，那么你就复用了这个类的方法和字段，同时你可以在新类中添加新的方法和字段。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
11.扩展类:扩展类充分体现了is-a的继承关系. 形式为:class (子类) extends (基类)。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
12.多态:在java中，对象变量是多态的.而java中不支持多重继承。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
13.动态绑定:调用对象方法的机制。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
(1)编译器检查对象声明的类型和方法名。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
(2)编译器检查方法调用的参数类型。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
(3)静态绑定:若方法类型为priavte static final 编译器会准确知道该调用哪个方法。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
(4)当程序运行并且使用动态绑定来调用一个方法时，那么虚拟机必须调用x所指向的对象的实际类型相匹配的方法版本。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
(5)动态绑定:是很重要的特性，它能使程序变得可扩展而不需要重编译已存代码。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
14.final类:为防止他人从你的类上派生新类，此类是不可扩展的。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
15.动态调用比静态调用花费的时间要长。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
16.抽象类:规定一个或多个抽象方法的类本身必须定义为abstract。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
例: public abstract string getDescripition<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
17.Java中的每一个类都是从Object类扩展而来的。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
18.object类中的equal和toString方法。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
equal用于测试一个对象是否同另一个对象相等。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
toString返回一个代表该对象的字符串，几乎每一个类都会重载该方法，以便返回当前状态的正确表示.<br _extended="true" />
<br _extended="true" />
(toString 方法是一个很重要的方法)<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
19.通用编程:任何类类型的所有值都可以同object类性的变量来代替。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
20.数组列表:ArrayList动态数组列表，是一个类库，定义在java.uitl包中，可自动调节数组的大小。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
21.class类 object类中的getclass方法返回ckass类型的一个实例，程序启动时包含在main方法的类会被加载，虚拟机要加载他需要的所有类，每一个加载的类都要加载它需要的类。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
22.class类为编写可动态操纵java代码的程序提供了强大的功能反射，这项功能为JavaBeans特别有用，使用反射Java能支持VB程序员习惯使用的工具。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
能够分析类能力的程序叫反射器，Java中提供此功能的包叫Java.lang.reflect反射机制十分强大.<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
1.在运行时分析类的能力。<br _extended="true" />
<br _extended="true" />
2.在运行时探察类的对象。<br _extended="true" />
<br _extended="true" />
3.实现通用数组操纵代码。<br _extended="true" />
<br _extended="true" />
4.提供方法对象。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
而此机制主要针对是工具者而不是应用及程序。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
反射机制中的最重要的部分是允许你检查类的结构.用到的API有:<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
java.lang.reflect.Field 返回字段.<br _extended="true" />
<br _extended="true" />
java.reflect.Method 返回方法.<br _extended="true" />
<br _extended="true" />
java.lang.reflect.Constructor 返回参数.<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
方法指针:java没有方法指针，把一个方法的地址传给另一个方法，可以在后面调用它，而接口是更好的解决方案。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
23.接口(Interface)说明类该做什么而不指定如何去做，一个类可以实现一个或多个interface。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
24.接口不是一个类，而是对符合接口要求的类的一套规范。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
若实现一个接口需要2个步骤:　<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
1.声明类需要实现的指定接口。<br _extended="true" />
<br _extended="true" />
2.提供接口中的所有方法的定义。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
声明一个类实现一个接口需要使用implements 关键字<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
class actionB implements Comparable 其actionb需要提供CompareTo方法，接口不是类，不能用new实例化一个接口.<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
25.一个类只有一个超类，但一个类能实现多个接口。Java中的一个重要接口：Cloneable<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
26.接口和回调.编程一个常用的模式是回调模式，在这种模式中你可以指定当一个特定时间发生时回调对象上的方法。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
例:ActionListener 接口监听.<br _extended="true" />
<br _extended="true" />
类似的API有:java.swing.JOptionPane<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
java.swing.Timer<br _extended="true" />
<br _extended="true" />
java.awt.Tookit<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
27.对象clone:clone方法是object一个保护方法，这意味着你的代码不能简单的调用它。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
28.内部类:一个内部类的定义是定义在另一个内部的类。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
原因是:<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
1.一个内部类的对象能够访问创建它的对象的实现，包括私有数据。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
2.对于同一个包中的其他类来说，内部类能够隐藏起来。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
3.匿名内部类可以很方便的定义回调。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
4.使用内部类可以非常方便的编写事件驱动程序。<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
29.代理类(proxy):<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
1.指定接口要求所有代码<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
2.object类定义的所有的方法(toString equals)<br _extended="true" />
<br _extended="true" />
<br _extended="true" />
30.数据类型:Java是强调类型的语言，每个变量都必须先申明它都类型，java中总共有8个基本类型.4种是整型，2种是浮点型，一种是字符型，被用于Unicode编码中的字符，布尔型</div>
</div>
<div class="invisible" id="reference" _extended="true">文章引用自：<a href=" _extended=" target="true"  ?></a> </div>
</div>
</div>
<img src ="http://www.blogjava.net/jesenblog/aggbug/187275.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesenblog/" target="_blank">白露</a> 2008-03-19 16:08 <a href="http://www.blogjava.net/jesenblog/articles/187275.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>编程需要注意的陷阱：Java继承是有害的</title><link>http://www.blogjava.net/jesenblog/articles/187274.html</link><dc:creator>白露</dc:creator><author>白露</author><pubDate>Wed, 19 Mar 2008 08:07:00 GMT</pubDate><guid>http://www.blogjava.net/jesenblog/articles/187274.html</guid><wfw:comment>http://www.blogjava.net/jesenblog/comments/187274.html</wfw:comment><comments>http://www.blogjava.net/jesenblog/articles/187274.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesenblog/comments/commentRss/187274.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesenblog/services/trackbacks/187274.html</trackback:ping><description><![CDATA[<div id="articledot" _extended="true">
<pre _extended="true">大多数好的设计者象躲避瘟疫一样来避免使用实现继承(extends 关系)。实际上80%的代码应该完全用interfaces写，而不是通过extends。&#8220;JAVA设计模式&#8221;一书详细阐述了怎样用接口继承代替实现继承。这篇文章描述设计者为什么会这么作。
Extends是有害的；也许对于Charles Manson这个级别的不是，但是足够糟糕的它应该在任何可能的时候被避开。&#8220;JAVA设计模式&#8221;一书花了很大的部分讨论用interface继承代替实现继承。
好的设计者在他的代码中，大部分用interface，而不是具体的基类。本文讨论为什么设计者会这样选择，并且也介绍一些基于interface的编程基础。
接口（Interface）和类（Class）?
Jams Gosling（Java之父）做发起人讲话。有人问他：&#8220;如果你重新构造Java，你想改变什么？&#8221;。&#8220;我想抛弃classes&#8221;他回答。在笑声平息后，它解释说，真正的问题不是由于class本身，而是实现继承(extends 关系)。接口继承（implements关系）是更好的。你应该尽可能的避免实现继承。
失去了灵活性
为什么你应该避免实现继承呢？第一个问题是明确的使用具体类名将你固定到特定的实现，给底层的改变增加了不必要的困难。
在当前的敏捷编程方法中，核心是并行的设计和开发的概念。在你详细设计程序前，你开始编程。这个技术不同于传统方法的形式----传统的方式是设计应该在编码开始前完成----但是许多成功的项目已经证明你能够更快速的开发高质量代码，相对于传统的按部就班的方法。但是在并行开发的核心是主张灵活性。你不得不以某一种方式写你的代码以至于最新发现的需求能够尽可能没有痛苦的合并到已有的代码中。
胜于实现你也许需要的特征，你只需实现你明确需要的特征，而且适度的对变化的包容。如果你没有这种灵活，并行的开发，那简直不可能。
对于Inteface的编程是灵活结构的核心。为了说明为什么，让我们看一下当使用它们的时候，会发生什么。考虑下面的代码：
f()
{ LinkedList list = new LinkedList();
//...
g( list );
}
g( LinkedList list )
{
list.add( ... );
g2( list )
}
现在，假设一个对于快速查询的需求被提出，以至于这个LinkedList不能够解决。你需要用HashSet来代替它。在已有代码中，变化不能够局部化，因为你不仅仅需要修改f()也需要修改g()（它带有LinkedList参数），并且还有g()把列表传递给的任何代码。象下面这样重写代码:
f()
{ Collection list = new LinkedList();
//...
g( list );
}
g( Collection list )
{
list.add( ... );
g2( list )
}
这样修改Linked list成hash，可能只是简单的用new HashSet()代替new LinkedList()。就这样。没有其他的需要修改的地方。
作为另一个例子，比较下面两段代码：
f()
{ Collection c = new HashSet();
//...
g( c );
}
g( Collection c )
{
for( Iterator i = c.iterator(); i.hasNext() )
do_something_with( i.next() );
}
和
f2()
{ Collection c = new HashSet();
//...
g2( c.iterator() );
}
g2( Iterator i )
{ while( i.hasNext() )
do_something_with( i.next() );
}
g2()方法现在能够遍历Collection的派生，就像你能够从Map中得到的键值对。事实上，你能够写iterator，它产生数据，代替遍历一个Collection。你能够写iterator，它从测试的框架或者文件中得到信息。这会有巨大的灵活性。
耦合
对于实现继承，一个更加关键的问题是耦合---令人烦躁的依赖，就是那种程序的一部分对于另一部分的依赖。全局变量提供经典的例子，证明为什么强耦合会引起麻烦。例如，如果你改变全局变量的类型，那么所有用到这个变量的函数也许都被影响，所以所有这些代码都要被检查，变更和重新测试。而且，所有用到这个变量的函数通过这个变量相互耦合。也就是，如果一个变量值在难以使用的时候被改变，一个函数也许就不正确的影响了另一个函数的行为。这个问题显著的隐藏于多线程的程序。
作为一个设计者，你应该努力最小化耦合关系。你不能一并消除耦合，因为从一个类的对象到另一个类的对象的方法调用是一个松耦合的形式。你不可能有一个程序，它没有任何的耦合。然而，你能够通过遵守OO规则，最小化一定的耦合（最重要的是，一个对象的实现应该完全隐藏于使用他的对象）。例如，一个对象的实例变量（不是常量的成员域），应该总是private。我意思是某段时期的，无例外的，不断的。（你能够偶尔有效地使用protected方法，但是protected实例变量是可憎的事）同样的原因你应该不用get/set函数---他们对于是一个域公用只是使人感到过于复杂的方式（尽管返回修饰的对象而不是基本类型值的访问函数是在某些情况下是由原因的，那种情况下，返回的对象类是一个在设计时的关键抽象）。
这里，我不是书生气。在我自己的工作中，我发现一个直接的相互关系在我OO方法的严格之间，快速代码开发和容易的代码实现。无论什么时候我违反中心的OO原则，如实现隐藏，我结果重写那个代码（一般因为代码是不可调试的）。我没有时间重写代码，所以我遵循那些规则。我关心的完全实用?我对干净的原因没有兴趣。
脆弱的基类问题
现在，让我们应用耦合的概念到继承。在一个用extends的继承实现系统中，派生类是非常紧密的和基类耦合，当且这种紧密的连接是不期望的。设计者已经应用了绰号&#8220;脆弱的基类问题&#8221;去描述这个行为。基础类被认为是脆弱的是，因为你在看起来安全的情况下修改基类，但是当从派生类继承时，新的行为也许引起派生类出现功能紊乱。你不能通过简单的在隔离下检查基类的方法来分辨基类的变化是安全的；而是你也必须看（和测试）所有派生类。而且，你必须检查所有的代码，它们也用在基类和派生类对象中，因为这个代码也许被新的行为所打破。一个对于基础类的简单变化可能导致整个程序不可操作。
让我们一起检查脆弱的基类和基类耦合的问题。下面的类extends了Java的ArrayList类去使它像一个stack来运转：
class Stack extends ArrayList
{ private int stack_pointer = 0;
public void push( Object article )
{ add( stack_pointer++, article );
}
public Object pop()
{ return remove( --stack_pointer );
}
public void push_many( Object[] articles )
{ for( int i = 0; i &lt; articles.length; ++i )
push( articles[i] );
}
}
甚至一个象这样简单的类也有问题。思考当一个用户平衡继承和用ArrayList的clear()方法去弹出堆栈时：
Stack a_stack = new Stack();
a_stack.push("1");
a_stack.push("2");
a_stack.clear();
这个代码成功编译，但是因为基类不知道关于stack指针堆栈的情况,这个stack对象当前在一个未定义的状态。下一个对于push（）调用把新的项放入索引2的位置。（stack_pointer的当前值），所以stack有效地有三个元素-下边两个是垃圾。（Java的stack类正是有这个问题，不要用它）.
对这个令人讨厌的继承的方法问题的解决办法是为Stack覆盖所有的ArrayList方法，那能够修改数组的状态，所以覆盖正确的操作Stack指针或者抛出一个例外。(removeRange()方法对于抛出一个例外一个好的候选方法)。
这个方法有两个缺点。第一，如果你覆盖了所有的东西，这个基类应该真正的是一个interface，而不是一个class。如果你不用任何继承方法，在实现继承中就没有这一点。第二，更重要的是，你不能够让一个stack支持所有的ArrayList方法。例如，令人烦恼的removeRange()没有什么作用。唯一实现无用方法的合理的途径是使它抛出一个例外，因为它应该永远不被调用。这个方法有效的把编译错误成为运行错误。不好的方法是，如果方法只是不被定义，编译器会输出一个方法未找到的错误。如果方法存在，但是抛出一个例外，你只有在程序真正的运行时，你才能够发现调用错误。
对于这个基类问题的一个更好的解决办法是封装数据结构代替用继承。这是新的和改进的Stack的版本：
class Stack
{
private int stack_pointer = 0;
private ArrayList the_data = new ArrayList();
public void push( Object article )
{
the_data.add( stack_poniter++, article );
}
public Object pop()
{
return the_data.remove( --stack_pointer );
}
public void push_many( Object[] articles )
{
for（ int i = 0; i &lt; o.length; ++i ）
push( articles[i] );
}
}
到现在为止，一直都不错，但是考虑脆弱的基类问题，我们说你想要在stack创建一个变量， 用它在一段周期内跟踪最大的堆栈尺寸。一个可能的实现也许象下面这样:
class Monitorable_stack extends Stack
{
private int high_water_mark = 0;
private int current_size;
public void push( Object article )
{
if( ++current_size &gt; high_water_mark )
high_water_mark = current_size;
super.push( article );
}
publish Object pop()
{
--current_size;
return super.pop();
}
public int maximum_size_so_far()
{
return high_water_mark;
}
}
这个新类运行的很好，至少是一段时间。不幸的是，这个代码发掘了一个事实，push_many()通过调用push()来运行。首先，这个细节看起来不是一个坏的选择。它简化了代码，并且你能够得到push()的派生类版本，甚至当Monitorable_stack通过Stack的参考来访问的时候，以至于high_water_mark能够正确的更新
</pre>
</div>
<div id="articlebody" _extended="true">大多数好的设计者象躲避瘟疫一样来避免使用实现继承(extends 关系)。实际上80%的代码应该完全用interfaces写，而不是通过extends。&#8220;JAVA设计模式&#8221;一书详细阐述了怎样用接口继承代替实现继承。这篇文章描述设计者为什么会这么作。 Extends是有害的；也许对于Charles Manson这个级别的不是，但是足够糟糕的它应该在任何可能的时候被避开。&#8220;JAVA设计模式&#8221;一书花了很大的部分讨论用interface继承代替实现继承。好的设计者在他的代码中，大部分用interface，而不是具体的基类。本文讨论为什么设计者会这样选择，并且也介绍一些基于interface的编程基础。接口（Interface）和类（Class）? Jams Gosling（Java之父）做发起人讲话。有人问他：&#8220;如果你重新构造Java，你想改变什么？&#8221;。&#8220;我想抛弃classes&#8221;他回答。在笑声平息后，它解释说，真正的问题不是由于class本身，而是实现继承(extends 关系)。接口继承（implements关系）是更好的。你应该尽可能的避免实现继承。失去了灵活性为什么你应该避免实现继承呢？第一个问题是明确的使用具体类名将你固定到特定的实现，给底层的改变增加了不必要的困难。在当前的敏捷编程方法中，核心是并行的设计和开发的概念。在你详细设计程序前，你开始编程。这个技术不同于传统方法的形式----传统的方式是设计应该在编码开始前完成----但是许多成功的项目已经证明你能够更快速的开发高质量代码，相对于传统的按部就班的方法。但是在并行开发的核心是主张灵活性。你不得不以某一种方式写你的代码以至于最新发现的需求能够尽可能没有痛苦的合并到已有的代码中。胜于实现你也许需要的特征，你只需实现你明确需要的特征，而且适度的对变化的包容。如果你没有这种灵活，并行的开发，那简直不可能。对于Inteface的编程是灵活结构的核心。为了说明为什么，让我们看一下当使用它们的时候，会发生什么。考虑下面的代码： f() { LinkedList list = new LinkedList(); //... g( list ); } g( LinkedList list ) { list.add( ... ); g2( list ) } 现在，假设一个对于快速查询的需求被提出，以至于这个LinkedList不能够解决。你需要用HashSet来代替它。在已有代码中，变化不能够局部化，因为你不仅仅需要修改f()也需要修改g()（它带有LinkedList参数），并且还有g()把列表传递给的任何代码。象下面这样重写代码: f() { Collection list = new LinkedList(); //... g( list ); } g( Collection list ) { list.add( ... ); g2( list ) } 这样修改Linked list成hash，可能只是简单的用new HashSet()代替new LinkedList()。就这样。没有其他的需要修改的地方。作为另一个例子，比较下面两段代码： f() { Collection c = new HashSet(); //... g( c ); } g( Collection c ) { for( Iterator i = c.iterator(); i.hasNext() ) do_something_with( i.next() ); } 和 f2() { Collection c = new HashSet(); //... g2( c.iterator() ); } g2( Iterator i ) { while( i.hasNext() ) do_something_with( i.next() ); } g2()方法现在能够遍历Collection的派生，就像你能够从Map中得到的键值对。事实上，你能够写iterator，它产生数据，代替遍历一个Collection。你能够写iterator，它从测试的框架或者文件中得到信息。这会有巨大的灵活性。耦合对于实现继承，一个更加关键的问题是耦合---令人烦躁的依赖，就是那种程序的一部分对于另一部分的依赖。全局变量提供经典的例子，证明为什么强耦合会引起麻烦。例如，如果你改变全局变量的类型，那么所有用到这个变量的函数也许都被影响，所以所有这些代码都要被检查，变更和重新测试。而且，所有用到这个变量的函数通过这个变量相互耦合。也就是，如果一个变量值在难以使用的时候被改变，一个函数也许就不正确的影响了另一个函数的行为。这个问题显著的隐藏于多线程的程序。作为一个设计者，你应该努力最小化耦合关系。你不能一并消除耦合，因为从一个类的对象到另一个类的对象的方法调用是一个松耦合的形式。你不可能有一个程序，它没有任何的耦合。然而，你能够通过遵守OO规则，最小化一定的耦合（最重要的是，一个对象的实现应该完全隐藏于使用他的对象）。例如，一个对象的实例变量（不是常量的成员域），应该总是private。我意思是某段时期的，无例外的，不断的。（你能够偶尔有效地使用protected方法，但是protected实例变量是可憎的事）同样的原因你应该不用get/set函数---他们对于是一个域公用只是使人感到过于复杂的方式（尽管返回修饰的对象而不是基本类型值的访问函数是在某些情况下是由原因的，那种情况下，返回的对象类是一个在设计时的关键抽象）。这里，我不是书生气。在我自己的工作中，我发现一个直接的相互关系在我OO方法的严格之间，快速代码开发和容易的代码实现。无论什么时候我违反中心的OO原则，如实现隐藏，我结果重写那个代码（一般因为代码是不可调试的）。我没有时间重写代码，所以我遵循那些规则。我关心的完全实用?我对干净的原因没有兴趣。脆弱的基类问题现在，让我们应用耦合的概念到继承。在一个用extends的继承实现系统中，派生类是非常紧密的和基类耦合，当且这种紧密的连接是不期望的。设计者已经应用了绰号&#8220;脆弱的基类问题&#8221;去描述这个行为。基础类被认为是脆弱的是，因为你在看起来安全的情况下修改基类，但是当从派生类继承时，新的行为也许引起派生类出现功能紊乱。你不能通过简单的在隔离下检查基类的方法来分辨基类的变化是安全的；而是你也必须看（和测试）所有派生类。而且，你必须检查所有的代码，它们也用在基类和派生类对象中，因为这个代码也许被新的行为所打破。一个对于基础类的简单变化可能导致整个程序不可操作。让我们一起检查脆弱的基类和基类耦合的问题。下面的类extends了Java的ArrayList类去使它像一个stack来运转： class Stack extends ArrayList { private int stack_pointer = 0; public void push( Object article ) { add( stack_pointer++, article ); } public Object pop() { return remove( --stack_pointer ); } public void push_many( Object[] articles ) { for( int i = 0; i &lt; articles.length; ++i ) push( articles[i] ); } } 甚至一个象这样简单的类也有问题。思考当一个用户平衡继承和用ArrayList的clear()方法去弹出堆栈时： Stack a_stack = new Stack(); a_stack.push("1"); a_stack.push("2"); a_stack.clear(); 这个代码成功编译，但是因为基类不知道关于stack指针堆栈的情况,这个stack对象当前在一个未定义的状态。下一个对于push（）调用把新的项放入索引2的位置。（stack_pointer的当前值），所以stack有效地有三个元素-下边两个是垃圾。（Java的stack类正是有这个问题，不要用它）. 对这个令人讨厌的继承的方法问题的解决办法是为Stack覆盖所有的ArrayList方法，那能够修改数组的状态，所以覆盖正确的操作Stack指针或者抛出一个例外。(removeRange()方法对于抛出一个例外一个好的候选方法)。这个方法有两个缺点。第一，如果你覆盖了所有的东西，这个基类应该真正的是一个interface，而不是一个class。如果你不用任何继承方法，在实现继承中就没有这一点。第二，更重要的是，你不能够让一个stack支持所有的ArrayList方法。例如，令人烦恼的removeRange()没有什么作用。唯一实现无用方法的合理的途径是使它抛出一个例外，因为它应该永远不被调用。这个方法有效的把编译错误成为运行错误。不好的方法是，如果方法只是不被定义，编译器会输出一个方法未找到的错误。如果方法存在，但是抛出一个例外，你只有在程序真正的运行时，你才能够发现调用错误。对于这个基类问题的一个更好的解决办法是封装数据结构代替用继承。这是新的和改进的Stack的版本： class Stack { private int stack_pointer = 0; private ArrayList the_data = new ArrayList(); public void push( Object article ) { the_data.add( stack_poniter++, article ); } public Object pop() { return the_data.remove( --stack_pointer ); } public void push_many( Object[] articles ) { for（ int i = 0; i &lt; o.length; ++i ） push( articles[i] ); } } 到现在为止，一直都不错，但是考虑脆弱的基类问题，我们说你想要在stack创建一个变量，用它在一段周期内跟踪最大的堆栈尺寸。一个可能的实现也许象下面这样: class Monitorable_stack extends Stack { private int high_water_mark = 0; private int current_size; public void push( Object article ) { if( ++current_size &gt; high_water_mark ) high_water_mark = current_size; super.push( article ); } publish Object pop() { --current_size; return super.pop(); } public int maximum_size_so_far() { return high_water_mark; } } 这个新类运行的很好，至少是一段时间。不幸的是，这个代码发掘了一个事实，push_many()通过调用push()来运行。首先，这个细节看起来不是一个坏的选择。它简化了代码，并且你能够得到push()的派生类版本，甚至当Monitorable_stack通过Stack的参考来访问的时候，以至于high_water_mark能够正确的更新</div>
<div class="invisible" id="reference" _extended="true">文章引用自：<a href=" _extended=" target="true"  ?></a> </div>
<img src ="http://www.blogjava.net/jesenblog/aggbug/187274.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesenblog/" target="_blank">白露</a> 2008-03-19 16:07 <a href="http://www.blogjava.net/jesenblog/articles/187274.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>品味Java子类型多态的魅力</title><link>http://www.blogjava.net/jesenblog/articles/187272.html</link><dc:creator>白露</dc:creator><author>白露</author><pubDate>Wed, 19 Mar 2008 08:01:00 GMT</pubDate><guid>http://www.blogjava.net/jesenblog/articles/187272.html</guid><wfw:comment>http://www.blogjava.net/jesenblog/comments/187272.html</wfw:comment><comments>http://www.blogjava.net/jesenblog/articles/187272.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesenblog/comments/commentRss/187272.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesenblog/services/trackbacks/187272.html</trackback:ping><description><![CDATA[<table cellspacing="0" cellpadding="0" width="98%" align="center" border="0" _extended="true">
    <tbody _extended="true">
        <tr _extended="true">
            <td height="50" _extended="true">
            <div align="center" _extended="true"><strong _extended="true"><font color="#990000" size="3" _extended="true">品味Java子类型多态的魅力</font></strong></div>
            </td>
        </tr>
        <tr _extended="true">
            <td valign="top" height="30" _extended="true">
            <div align="center" _extended="true">&nbsp;</div>
            </td>
        </tr>
        <tr _extended="true">
            <td _extended="true">
            <table cellspacing="0" cellpadding="0" width="95%" align="center" border="0" _extended="true">
                <tbody _extended="true">
                    <tr _extended="true">
                        <td valign="top" _extended="true">
                        <p _extended="true"><span class="font10" id="zoom" _extended="true">　　摘要：</span></p>
                        <p _extended="true">　　Java程序员经常运用对象的多态性使其在适当的地方调用适当的方法，显得很神奇。这种方法通过继承机制来实现。然而，一个严谨的实验可以使其变得很明白，并揭示了，把多态性理解为与类型相关的概念更为合适，比继承机制的解释更好。这种理解可以帮助程序员更好的运用多态。</p>
                        <p _extended="true">　　　　　　　　　　　　　　　　　------WM.保罗 罗格斯</p>
                        <p _extended="true">　　&#8220;polymorphism(多态）&#8221;一词来自希腊语，意为&#8220;多种形式&#8221;。多数Java程序员把多态看作对象的一种能力，使其能调用正确的方法版本。尽管如此，这种面向实现的观点导致了多态的神奇功能，胜于仅仅把多态看成纯粹的概念。</p>
                        <p _extended="true">　　Java中的多态总是子类型的多态。几乎是机械式产生了一些多态的行为，使我们不去考虑其中涉及的类型问题。本文研究了一种面向类型的对象观点，分析了如何将对象能够表现的行为和对象即将表现的行为分离开来。抛开Java中的多态都是来自继承的概念，我们仍然可以感到，Java中的接口是一组没有公共代码的对象共享实现。</p>
                        <p _extended="true">　　多态的分类</p>
                        <p _extended="true">　　多态在面向对象语言中是个很普遍的概念.虽然我们经常把多态混为一谈，但实际上有四种不同类型的多态。在开始正式的子类型多态的细节讨论前，然我们先来看看普通面向对象中的多态。</p>
                        <p _extended="true">　　Luca Cardelli和Peter Wegner（"On Understanding Types, Data Abstraction, and Polymorphism"一文的作者，文章参考资源链接）把多态分为两大类----特定的和通用的----四小类：强制的，重载的，参数的和包含的。他们的结构如下：</p>
                        <p align="center" _extended="true"><img src="http://www.kupage.com/src/20040116/1137140000027kige000.gif" _extended="true"  alt="" /></img _extended="true"></p>
                        <p _extended="true">　　在这样一个体系中，多态表现出多种形式的能力。通用多态引用有相同结构类型的大量对象，他们有着共同的特征。特定的多态涉及的是小部分没有相同特征的对象。四种多态可做以下描述：</p>
                        <p _extended="true">　　强制的：一种隐式做类型转换的方法。</p>
                        <p _extended="true">　　重载的：将一个标志符用作多个意义。</p>
                        <p _extended="true">　　参数的：为不同类型的参数提供相同的操作。</p>
                        <p _extended="true">　　包含的：类包含关系的抽象操作。</p>
                        <p _extended="true">　　我将在讲述子类型多态前简单介绍一下这几种多态。<br _extended="true" />
                        <br _extended="true" />
                        　　强制的多态</p>
                        <p _extended="true">　　强制多态隐式的将参数按某种方法，转换成编译器认为正确的类型以避免错误。在以下的表达式中，编译器必须决定二元运算符&#8216;+&#8217;所应做的工作：</p>
                        <p _extended="true">　　2.0 + 2.0</p>
                        <p _extended="true">　　2.0 + 2</p>
                        <p _extended="true">　　2.0 + "2"</p>
                        <p _extended="true">　　第一个表达式将两个double的操作数相加；Java中特别声明了这种用法。</p>
                        <p _extended="true">　　第二个表达式将double型和int相加。Java中没有明确定义这种运算。不过，编译器隐式的将第二个操作数转换为double型，并作double型的加法。做对程序员来说十分方便，否则将会抛出一个编译错误，或者强制程序员显式的将int转换为double。</p>
                        <p _extended="true">　　第三个表达式将double与一个String相加。Java中同样没有定义这样的操作。所以，编译器将double转换成String类型，并将他们做串联。</p>
                        <p _extended="true">　　强制多态也会发生在方法调用中。假设类Derived继承了类Base，类C有一个方法，原型为m(Base)，在下面的代码中，编译器隐式的将Derived类的对象derived转化为Base类的对象。这种隐式的转换使m(Base)方法使用所有能转换成Base类的所有参数。</p>
                        <p _extended="true">C c = new C();</p>
                        <p _extended="true">Derived derived = new Derived();</p>
                        <p _extended="true">c.m( derived );</p>
                        <p _extended="true">　　并且，隐式的强制转换，可以避免类型转换的麻烦，减少编译错误。当然，编译器仍然会优先验证符合定义的对象类型。</p>
                        <p _extended="true">　　重载的多态</p>
                        <p _extended="true">　　重载允许用相同的运算符或方法，去表示截然不同的意义。&#8216;+&#8217;在上面的程序中有两个意思：两个double型的数相加；两个串相连。另外还有整型相加，长整型，等等。这些运算符的重载，依赖于编译器根据上下文做出的选择。以往的编译器会把操作数隐式转换为完全符合操作符的类型。虽然Java明确支持重载，但不支持用户定义的操作符重载。</p>
                        <p _extended="true">　　Java支持用户定义的函数重载。一个类中可以有相同名字的方法，这些方法可以有不同的意义。这些重载的方法中，必须满足参数数目不同，相同位置上的参数类型不同。这些不同可以帮助编译器区分不同版本的方法。</p>
                        <p _extended="true">　　编译器以这种唯一表示的特征来表示不同的方法，比用名字表示更为有效。据此，所有的多态行为都能编译通过。</p>
                        <p _extended="true">　　强制和重载的多态都被分类为特定的多态，因为这些多态都是在特定的意义上的。这些被划入多态的特性给程序员带来了很大的方便。强制多态排除了麻烦的类型和编译错误。重载多态像一块糖，允许程序员用相同的名字表示不同的方法，很方便。</p>
                        <p _extended="true">　　参数的多态</p>
                        <p _extended="true">　　参数多态允许把许多类型抽象成单一的表示。例如，List抽象类中，描述了一组具有同样特征的对象，提供了一个通用的模板。你可以通过指定一种类型以重用这个抽象类。这些参数可以是任何用户定义的类型，大量的用户可以使用这个抽象类，因此参数多态毫无疑问的成为最强大的多态。</p>
                        <p _extended="true">　　乍一看，上面抽象类好像是java.util.List的功能。然而，Java实际上并不支持真正的安全类型风格的参数多态，这也是java.util.List和java.util的其他集合类是用原始的java.lang.Object写的原因（参考我的文章"A Primordial Interface?" 以获得更多细节）。Java的单根继承方式解决了部分问题，但没有发挥出参数多态的全部功能。Eric Allen有一篇精彩的文章&#8220;Behold the Power of Parametric Polymorphism&#8221;，描述了Java通用类型的需求，并建议给Sun的Java规格需求#000014号文档"Add Generic Types to the Java Programming Language."（参考资源链接）</p>
                        <p _extended="true">　　包含的多态</p>
                        <p _extended="true">　　包含多态通过值的类型和集合的包含关系实现了多态的行为.在包括Java在内的众多面向对象语言中，包含关系是子类型的。所以，Java的包含多态是子类型的多态。</p>
                        <p _extended="true">　　在早期，Java开发者们所提及的多态就特指子类型的多态。通过一种面向类型的观点，我们可以看到子类型多态的强大功能。以下的文章中我们将仔细探讨这个问题。为简明起见，下文中的多态均指包含多态。<br _extended="true" />
                        <br _extended="true" />
                        　　面向类型观点</p>
                        <p _extended="true">　　图1的UML类图给出了类和类型的简单继承关系，以便于解释多态机制。模型中包含5种类型，4个类和一个接口。虽然UML中称为类图，我把它看成类型图。如"Thanks Type and Gentle Class," 一文中所述，每个类和接口都是一种用户定义的类型。按独立实现的观点（如面向类型的观点），下图中的每个矩形代表一种类型。从实现方法看，四种类型运用了类的结构，一种运用了接口的结构。</p>
                        <p _extended="true"><img src="http://www.kupage.com/src/20040116/11431100000276v3y001.gif" _extended="true"  alt="" /></img _extended="true"><br _extended="true" />
                        <br _extended="true" />
                        图1：示范代码的UML类图</p>
                        <p _extended="true">　　以下的代码实现了每个用户定义的数据类型，我把实现写得很简单。</p>
                        <p _extended="true">public class Base<br _extended="true" />
                        {<br _extended="true" />
                        　public String m1()<br _extended="true" />
                        　{<br _extended="true" />
                        　　return "Base.m1()";<br _extended="true" />
                        　}</p>
                        <p _extended="true">　public String m2( String s )<br _extended="true" />
                        　{<br _extended="true" />
                        　　return "Base.m2( " + s + " )";<br _extended="true" />
                        　}<br _extended="true" />
                        }</p>
                        <p _extended="true">interface IType<br _extended="true" />
                        {<br _extended="true" />
                        　String m2( String s );<br _extended="true" />
                        　String m3();<br _extended="true" />
                        }</p>
                        <p _extended="true">public class Derived<br _extended="true" />
                        extends Base<br _extended="true" />
                        implements IType<br _extended="true" />
                        {<br _extended="true" />
                        　public String m1()<br _extended="true" />
                        　{<br _extended="true" />
                        　　return "Derived.m1()";<br _extended="true" />
                        　}</p>
                        <p _extended="true">　public String m3()<br _extended="true" />
                        　{<br _extended="true" />
                        　　return "Derived.m3()";<br _extended="true" />
                        　}<br _extended="true" />
                        }</p>
                        <p _extended="true">public class Derived2<br _extended="true" />
                        extends Derived<br _extended="true" />
                        {<br _extended="true" />
                        　public String m2( String s )<br _extended="true" />
                        　{<br _extended="true" />
                        　　return "Derived2.m2( " + s + " )";<br _extended="true" />
                        　}<br _extended="true" />
                        　public String m4()<br _extended="true" />
                        　{<br _extended="true" />
                        　　return "Derived2.m4()";<br _extended="true" />
                        　}<br _extended="true" />
                        }</p>
                        <p _extended="true">public class Separate<br _extended="true" />
                        implements IType<br _extended="true" />
                        {<br _extended="true" />
                        　public String m1()<br _extended="true" />
                        　{<br _extended="true" />
                        　　return "Separate.m1()";<br _extended="true" />
                        　}<br _extended="true" />
                        　public String m2( String s )<br _extended="true" />
                        　{<br _extended="true" />
                        　　return "Separate.m2( " + s + " )";<br _extended="true" />
                        　}</p>
                        <p _extended="true">　public String m3()<br _extended="true" />
                        　{<br _extended="true" />
                        　　return "Separate.m3()";<br _extended="true" />
                        　}<br _extended="true" />
                        }<br _extended="true" />
                        &nbsp;</p>
                        <p _extended="true">　　用这样的类型声明和类的定义，图2从概念的观点描述了Java指令。</p>
                        <p _extended="true">Derived2 derived2 = new Derived2();</p>
                        <p align="center" _extended="true"><img src="http://www.kupage.com/src/20040116/11431100000276v3y002.gif" _extended="true"  alt="" /></img _extended="true"></p>
                        <p _extended="true"><br _extended="true" />
                        <br _extended="true" />
                        图2 ：Derived2 对象上的引用</p>
                        <p _extended="true">　　上文中声明了derived2这个对象，它是Derived2类的。图2种的最顶层把Derived2引用描述成一个集合的窗口，虽然其下的Derived2对象是可见的。这里为每个Derived2类型的操作留了一个孔。Derived2对象的每个操作都去映射适当的代码，按照上面的代码所描述的那样。例如，Derived2对象映射了在Derived中定义的m1()方法。而且还重载了Base类的m1()方法。一个Derived2的引用变量无权访问Base类中被重载的m1()方法。但这并不意味着不可以用super.m1()的方法调用去使用这个方法。关系到derived2这个引用的变量，这个代码是不合适的。Derived2的其他的操作映射同样表明了每种类型操作的代码执行。</p>
                        <p _extended="true">　　既然你有一个Derived2对象，可以用任何一个Derived2类型的变量去引用它。如图1所示，Derived, Base和IType都是Derived2的基类。所以，Base类的引用是很有用的。图3描述了以下语句的概念观点。</p>
                        <p _extended="true">Base base = derived2;</p>
                        <p align="center" _extended="true"><img src="http://www.kupage.com/src/20040116/11431100000276v3y003.gif" _extended="true"  alt="" /></img _extended="true"></p>
                        <p _extended="true"><br _extended="true" />
                        <br _extended="true" />
                        图3：Base类引用附于Derived2对象之上<br _extended="true" />
                        　<br _extended="true" />
                        　　虽然Base类的引用不用再访问m3()和m4()，但是却不会改变它Derived2对象的任何特征及操作映射。无论是变量derived2还是base，其调用m1()或m2(String)所执行的代码都是一样的。</p>
                        <p _extended="true">String tmp;<br _extended="true" />
                        // Derived2 reference (Figure 2)<br _extended="true" />
                        tmp = derived2.m1(); // tmp is "Derived.m1()"<br _extended="true" />
                        tmp = derived2.m2( "Hello" ); // tmp is "Derived2.m2( Hello )"</p>
                        <p _extended="true">// Base reference (Figure 3)</p>
                        <p _extended="true">tmp = base.m1(); // tmp is "Derived.m1()"<br _extended="true" />
                        tmp = base.m2( "Hello" ); // tmp is "Derived2.m2( Hello )"</p>
                        <p _extended="true">　　两个引用之所以调用同一个行为，是因为Derived2对象并不知道去调用哪个方法。对象只知道什么时候调用，它随着继承实现的顺序去执行。这样的顺序决定了Derived2对象调用Derived里的m1()方法，并调用Derived2里的m2(String)方法。这种结果取决于对象本身的类型，而不是引用的类型。</p>
                        <p _extended="true">　　尽管如此，但不意味着你用derived2和base引用的效果是完全一样的。如图3所示，Base的引用只能看到Base类型拥有的操作。所以，虽然Derived2有对方法m3()和m4()的映射，但是变量base不能访问这些方法。</p>
                        <p _extended="true">String tmp;<br _extended="true" />
                        // Derived2 reference (Figure 2)<br _extended="true" />
                        tmp = derived2.m3(); // tmp is "Derived.m3()"<br _extended="true" />
                        tmp = derived2.m4(); // tmp is "Derived2.m4()"</p>
                        <p _extended="true">// Base reference (Figure 3)</p>
                        <p _extended="true">tmp = base.m3(); // Compile-time error<br _extended="true" />
                        tmp = base.m4(); // Compile-time error</p>
                        <p _extended="true">　　运行期的Derived2对象保持了接受m3()和m4()方法的能力。类型的限制使Base的引用不能在编译期调用这些方法。编译期的类型检查像一套铠甲，保证了运行期对象只能和正确的操作进行相互作用。换句话说，类型定义了对象间相互作用的边界。<br _extended="true" />
                        <br _extended="true" />
                        　　多态的依附性</p>
                        <p _extended="true">　　类型的一致性是多态的核心。对象上的每一个引用，静态的类型检查器都要确认这样的依附和其对象的层次是一致的。当一个引用成功的依附于另一个不同的对象时，有趣的多态现象就产生了。（严格的说，对象类型是指类的定义。）你也可以把几个不同的引用依附于同一个对象。在开始更有趣的场景前，我们先来看一下下面的情况为什么不会产生多态。</p>
                        <p _extended="true">　　多个引用依附于一个对象</p>
                        <p _extended="true">　　图2和图3描述的例子是把两个及两个以上的引用依附于一个对象。虽然Derived2对象在被依附之后仍保持了变量的类型，但是，图3中的Base类型的引用依附之后，其功能减少了。结论很明显：把一个基类的引用依附于派生类的对象之上会减少其能力。</p>
                        <p _extended="true">　　一个开发这怎么会选择减少对象能力的方案呢？这种选择是间接的。假设有一个名为ref的引用依附于一个包含如下方法的类的对象：</p>
                        <p _extended="true">public String poly1( Base base )<br _extended="true" />
                        {<br _extended="true" />
                        　return base.m1();<br _extended="true" />
                        }&nbsp;</p>
                        <p _extended="true">　　用一个Derived2的参数调用poly(Base)是符合参数类型检查的：</p>
                        <p _extended="true">ref.poly1( derived2 );</p>
                        <p _extended="true">　　方法调用把一个本地Base类型的变量依附在一个引入的对象上。所以，虽然这个方法只接受Base类型的参数，但Derived2对象仍是允许的。开发这就不必选择丢失功能的方案。从人眼在通过Derived2对象时所看到的情况，Base类型引用的依附导致了功能的丧失。但从执行的观点看，每一个传入poly1(Base)的参数都认为是Base的对象。执行机并不在乎有多个引用指向同一个对象，它只注重把指向另一个对象的引用传给方法。这些对象的类型不一致并不是主要问题。执行器只关心给运行时的对象找到适当的实现。面向类型的观点展示了多态的巨大能力。</p>
                        <p _extended="true">　　附于多个对象的引用</p>
                        <p _extended="true">　　让我们来看一下发生在poly1(Base)中的多态行为。下面的代码创建了三个对象，并通过引用传给poly1(Base):</p>
                        <p _extended="true">Derived2 derived2 = new Derived2();<br _extended="true" />
                        Derived derived = new Derived();<br _extended="true" />
                        Base base = new Base();</p>
                        <p _extended="true">String tmp;</p>
                        <p _extended="true">tmp = ref.poly1( derived2 ); // tmp is "Derived.m1()"<br _extended="true" />
                        tmp = ref.poly1( derived ); // tmp is "Derived.m1()"<br _extended="true" />
                        tmp = ref.poly1( base ); // tmp is "Base.m1()"</p>
                        <p _extended="true">　　poly1(Base)的实现代码是调用传进来的参数的m1()方法。图3和图4展示了把三个类的对象传给方法时，面向类型的所使用的体系结构。</p>
                        <p align="center" _extended="true"><img src="http://www.kupage.com/src/20040116/1147510000027w8va004.gif" _extended="true"  alt="" /></img _extended="true"></p>
                        <p _extended="true"><br _extended="true" />
                        <br _extended="true" />
                        图4：将Base引用指向Derived类，以及Base对象</p>
                        <p _extended="true">　　请注意每个图中方法m1()的映射。图3中，m1()调用了Derived类的代码；上面代码中的注释标明了ploy1(Base)调用Derived.m1()。图4中Derived对象调用的仍然是Derived类的m1()方法。最后，图4中，Base对象调用的m1()是Base类中定义的代码。</p>
                        <p _extended="true">　　多态的魅力何在？再来看一下poly1(Base)的代码，它可以接受任何属于Base类范畴的参数。然而，当他收到一个Derived2的对象时，它实际上却调用了Derived版本的方法。当你根据Base类派生出其他类时，如Derived，Derived2，poly1(Base)都可以接受这些参数，并作出选择调用合适的方法。多态允许你在完成poly1(Base)后扩展它的用途。</p>
                        <p _extended="true">　　这看起来当然很神奇。基本的理解展示了多态的内部工作原理。在面向类型的观点中，底层的对象所实现的代码是非实质性的。重要的是，类型检查器会在编译期间为每个引用选择合适的代码以实现其方法。多态使开发者运用面向类型的观点，不考虑实现的细节。这样有助于把类型和实现分离（实际用处是把接口和实现分离）<br _extended="true" />
                        <br _extended="true" />
                        　　对象接口</p>
                        <p _extended="true">　　多态依赖于类型和实现的分离，多用来把接口和实现分离。但下面的观点好像把Java的关键字interface搞得很糊涂。</p>
                        <p _extended="true">　　更为重要的使开发者们怎样理解短语&#8220;the interface to an object"，典型地，根据上下文，这个短语的意思是指一切对象类中所定义的方法，至一切对象公开的方法。这种倾向于以实现为中心的观点较之于面向类型的观点来说，使我们更加注重于对象在运行期的能力。图3中，引用面板的对象表面被标志成"Derived2 Object"。这个面板上列出了Derived2对象的所有可用的方法。但是要理解多态，我们必须从实现这一层次上解放出来，并注意面向类型的透视图中被标为"Base Reference"的面板。在这一层意思上，引用变量的类型指明了一个对象的表面。这只是一个表面，不是接口。在类型一致的原则下，我们可以用面向类型的观点，为一个对象依附多个引用。对interface to an object这个短语的理解没有确定的理解。</p>
                        <p _extended="true">　　在类型概念中，the interface to an object refers 引用了面向类型观点的最大可能----如图2的情形。把一个基类的引用指向相同的对象缩小了这样的观点----如图3所示。类型概念能使人获得把对象间的相互作用同实现细节分离的要领。相对于一个对象的接口，面向类型的观点更鼓励人们去使用一个对象的引用。引用类型规定了对象间的相互作用。当你考虑一个对象能做什么的时候，只需搞明白他的类型，而不需要去考虑他的实现细节。</p>
                        <p _extended="true">　　Java接口</p>
                        <p _extended="true">　　以上所谈到的多态行为用到了类的继承关系所建立起来的子类型关系。Java接口同样支持用户定义的类型，相对地，Java的接口机制启动了建立在类型层次结构上的多态行为。假设一个名为ref的引用变量，并使其指向一个包含一下方法的类对象：</p>
                        <p _extended="true">public String poly2( IType iType )<br _extended="true" />
                        {<br _extended="true" />
                        　return iType.m3();<br _extended="true" />
                        }</p>
                        <p _extended="true">　　为了弄明白poly2(IType)中的多态，以下的代码从不同的类创建两个对象，并分别把他们传给poly2(IType)：</p>
                        <p _extended="true">Derived2 derived2 = new Derived2();<br _extended="true" />
                        Separate separate = new Separate();</p>
                        <p _extended="true">String tmp;</p>
                        <p _extended="true">tmp = ref.poly2( derived2 ); // tmp is "Derived.m3()"<br _extended="true" />
                        tmp = ref.poly2( separate ); // tmp is "Separate.m3()"</p>
                        <p _extended="true">　　上面的代码类似于关于poly1(Base)中的多态的讨论。poly2(IType)的实现代码是调用每个对象的本地版本的m3()方法。如同以前，代码的注释表明了每次调用所返回的CString类型的结果。图5表明了两次调用poly2(IType)的概念结构：</p>
                        <p align="center" _extended="true"><img src="http://www.kupage.com/src/20040116/11513100000277zam005.jpg" _extended="true"  alt="" /></img _extended="true"></p>
                        <p _extended="true"><br _extended="true" />
                        <br _extended="true" />
                        图5：指向Derived2和Separate对象的IType引用</p>
                        <p _extended="true">　　方法poly1(Base)和poly2(IType)中所表现的多态行为的相似之处可以从透视图中直接看出来。把我们在实现在一层上的理解再提高一层，就可以看到这两段代码的技巧。基类的引用指向了作为参数传进的类，并且按照类型的限制调用对象的方法。引用既不知道也不关心执行哪一段代码。编译期间的子类型关系检查保证了通过的对象有能力在被调用的时候选择合适的实现代码。</p>
                        <p _extended="true">　　然而，他们在实现层上有一个重要的差别。在poly1(Base)的例子中（图3和图4），Base-Derived-Derived2的类继承结构为子类型关系的建立提供了条件，并决定了方法去调用哪段代码。在poly2(IType)的例子中（如图5），则是完全不同的动态发生的。Derived2和Separate不共享任何实现的层次，但是他们还是通过IType的引用展示了多态的行为。</p>
                        <p _extended="true">　　这样的多态行为使Java的接口的功能的重大意义显得很明显。图1中的UML类图说明了Derived是Base和IType的子类型。通过完全脱离实现细节的类型的定义方法，Java实现了多类型继承，并且不存在Java所禁止的多继承所带来的烦人的问题。完全脱离实现层次的类可以按照Java接口实现分组。在图1中，接口IType和Derived,Separate以及这类型的其他子类型应该划为一组。</p>
                        <p _extended="true">　　按照这种完全不同于实现层次的分类方法，Java的接口机制是多态变得很方便，哪怕不存在任何共享的实现或者复写的方法。如图5所示，一个IType的引用，用多态的方法访问到了Derived2和Separate对象的m3()方法。</p>
                        <p _extended="true">　　再次探讨对象的接口</p>
                        <p _extended="true">　　注意图5中的Derived2和Separate对象的对m1()的映射方法。如前所述，每一个对象的接口都包含方法m1()。但却没有办法用这两个对象使方法m1()表现出多态的行为。每一个对象占有一个m1()方法是不够的。必须存在一个可以操作m1()方法的类型，通过这个类型可以看到对象。这些对象似乎是共享了m1()方法，但在没有共同基类的条件下，多态是不可能的。通过对象的接口来看多态，会把这个概念搞混。</p>
                        <p _extended="true">　　结论</p>
                        <p _extended="true">　　从全文所述的面向对象多态所建立起来的子类型多态，你可以清楚地认识到这种面向类型的观点。如果你想理解子类型多态的思想，就应该把注意力从实现的细节转移到类型的上。类型把对象分成组，并且管理着这些对象的接口。类型的继承层次结构决定了实现多态所需的类型关系。</p>
                        <p _extended="true">　　有趣的是，实现的细节并不影响子类型多态的层次结构。类型决定了对象调用什么方法，而实现则决定了对象怎么执行这个方法。也就是说，类型表明了责任，而负责实施的则是具体的实现。将实现和类型分离后，我们好像看到了这两个部分在一起跳舞，类型决定了他的舞伴和舞蹈的名字，而实现则是舞蹈动作的设计师。</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/jesenblog/aggbug/187272.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesenblog/" target="_blank">白露</a> 2008-03-19 16:01 <a href="http://www.blogjava.net/jesenblog/articles/187272.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java字符编码简介 </title><link>http://www.blogjava.net/jesenblog/articles/187271.html</link><dc:creator>白露</dc:creator><author>白露</author><pubDate>Wed, 19 Mar 2008 07:53:00 GMT</pubDate><guid>http://www.blogjava.net/jesenblog/articles/187271.html</guid><wfw:comment>http://www.blogjava.net/jesenblog/comments/187271.html</wfw:comment><comments>http://www.blogjava.net/jesenblog/articles/187271.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jesenblog/comments/commentRss/187271.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jesenblog/services/trackbacks/187271.html</trackback:ping><description><![CDATA[<div _extended="true"><strong _extended="true"><font size="6" _extended="true"><span _extended="true">1<span _extended="true"><font size="6" _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span> 基本信息</font></strong></div>
<div _extended="true"><strong _extended="true">摘要：</strong>在<span _extended="true">Java应用程序特别是Web应用中，经常遇到字符的编码问题。为了防止出现乱码，首先需要了解字符编码的基本概念以及Java是如何处理字符编码的，这样就可以有目的地在输入/输出环节中增加必要的转码。本文将分以下几部分介绍：</span></div>
<div _extended="true"><span _extended="true">1.<span _extended="true">&nbsp;&nbsp;</span></span> 什么是字符集？什么是编码？</div>
<div _extended="true"><span _extended="true">2.<span _extended="true">&nbsp;&nbsp;</span></span> 常用字符集有哪些？</div>
<div _extended="true"><span _extended="true">3.<span _extended="true">&nbsp;&nbsp;</span></span> 为什么会有乱码？</div>
<div _extended="true"><span _extended="true">4.<span _extended="true">&nbsp;&nbsp;</span></span> Java字符编码</div>
<div _extended="true"><span _extended="true">5.<span _extended="true">&nbsp;&nbsp;</span></span> JSP编码</div>
<div _extended="true"><span _extended="true">6.<span _extended="true">&nbsp;&nbsp;</span></span> 有没有万金油？</div>
<div _extended="true"><span _extended="true">7.<span _extended="true">&nbsp;&nbsp;</span></span> 参考资料和推荐参阅</div>
<div align="left" _extended="true"><strong _extended="true">作者：</strong>贾继东&nbsp;创建于2007-9-17</div>
<div _extended="true"><strong _extended="true"><font size="6" _extended="true"><span _extended="true">2<span _extended="true"><font size="6" _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span> 什么是字符集?什么是编码?</font></strong></div>
<div _extended="true"><span _extended="true">l&nbsp;</span><strong _extended="true">字符(Character)</strong><strong _extended="true">：</strong>是文字与符号的总称，包括文字、图形符号、数学符号等。</div>
<div _extended="true"><span _extended="true">l&nbsp;</span><strong _extended="true">字符集(Charset)</strong><strong _extended="true">：</strong>就是一组抽象字符的集合。</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> 字符集常常和一种具体的语言文字对应起来，该文字中的所有字符或者大部分常用字符就构成了该文字的字符集，比如英文字符集。</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> 一组有共同特征的字符也可以组成字符集，比如繁体汉字字符集、日文汉字字符集。</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> 字符集的子集也是字符集。</div>
<div _extended="true">计算机要处理各种字符，就需要将字符和二进制内码对应起来，这种对应关系就是字符</div>
<div _extended="true"><span _extended="true">l&nbsp;</span><strong _extended="true">编码(Encoding)</strong><strong _extended="true">：</strong></div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> 制定编码首先要确定字符集，并将字符集内的字符排序，然后和二进制数字对应起来。根据字符集内字符的多少，会确定用几个字节来编码。</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> 每种编码都限定了一个明确的字符集合，叫做被编码过的字符集(Coded Character Set)，这是字符集的另外一个含义。通常所说的字符集大多是这个含义。</div>
<div _extended="true">&nbsp;</div>
<div _extended="true">&nbsp;</div>
<div _extended="true"><strong _extended="true"><font size="6" _extended="true"><span _extended="true">3<span _extended="true"><font size="6" _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span> 常用字符集有哪些?</font></strong></div>
<div _extended="true"><strong _extended="true">ASCII</strong><strong _extended="true">：</strong></div>
<div _extended="true"><strong _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></strong> American Standard Code for Information Interchange，美国信息交换标准码。</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> 目前计算机中用得最广泛的字符集及其编码，由美国国家标准局(ANSI)制定。它已被国际标准化组织(ISO)定为国际标准，称为ISO 646标准。 ASCII字符集由控制字符和图形字符组成。在计算机的存储单元中，一个ASCII码值占一个字节(8个二进制位)，其最高位(b7)用作奇偶校验位。所谓奇偶校验，是指在代码传送过程中用来检验是否出现错误的一种方法，一般分<span _extended="true">奇校验和偶校验两种。奇校验规定：正确的代码一个字节中</span>1的个数必须是奇数，若非奇数，则在最高位b7添1。偶校验规定：正确的代码一个字节中1的个数必须是偶数，若非偶数，则在最高位b7添1。</div>
<div _extended="true"><strong _extended="true">ISO 8859-1</strong><strong _extended="true">：</strong></div>
<div _extended="true"><strong _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></strong> 全称ISO/IEC 8859，是国际标准化组织(ISO)及国际电工委员会(IEC)联合制定的一系列8位字符集的标准，现时定义了15个字符集。</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASCII</span>收录了空格及94个&#8220;可印刷字符&#8221;，足以给英语使用。但是，其他使用拉丁字母的语言(主要是欧洲国家的语言)，都有一定数量的变音字母，故可以使用ASCII及控制字符以外的区域来储存及表示。除了使用拉丁字母的语言外，使用西里尔字母的东欧语言、希腊语、泰语、现代阿拉伯语、希伯来语等，都可以使用这个形式来储存及表示。</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-1 (Latin-1) - 西欧语言</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-2 (Latin-2) - 中欧语言</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-3 (Latin-3) - 南欧语言。世界语也可用此字符集显示。</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-4 (Latin-4) - 北欧语言</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-5 (Cyrillic) - 斯拉夫语言</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-6 (Arabic) - 阿拉伯语</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-7 (Greek) - 希腊语</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-8 (Hebrew) - 希伯来语(视觉顺序)</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-8-I - 希伯来语(逻辑顺序)</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-9 (Latin-5 或 Turkish) - 它把Latin-1的冰岛语字母换走，加入土耳其语字母。</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-10 (Latin-6 或 Nordic) - 北日耳曼语支，用来代替Latin-4。</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-11 (Thai) - 泰语，从泰国的 TIS620 标准字集演化而来。</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-13 (Latin-7 或 Baltic Rim) - 波罗的语族</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-14 (Latin-8 或 Celtic) - 凯尔特语族</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-15 (Latin-9) - 西欧语言，加入Latin-1欠缺的法语及芬兰语重音字母，以及欧元符号。</div>
<div _extended="true"><span _extended="true">&nbsp;</span>ISO 8859-16 (Latin-10) - 东南欧语言。主要供罗马尼亚语使用，并加入欧元符号。</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> 很明显，iso8859-1编码表示的字符范围很窄，无法表示中文字符。但是，由于是单字节编码，和计算机最基础的表示单位一致，所以很多时候，仍旧使用iso8859-1编码来表示。而且在很多协议上，默认使用该编码。</div>
<div _extended="true">&nbsp;</div>
<div _extended="true"><strong _extended="true">UCS</strong><strong _extended="true">：</strong></div>
<div _extended="true"><strong _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></strong> 通用字符集(Universal Character Set，UCS)是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义的字符编码方式，采用4字节编码。</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UCS</span>包含了已知语言的所有字符。除了拉丁语、希腊语、斯拉夫语、希伯来语、阿拉伯语、亚美尼亚语、格鲁吉亚语，还包括中文、日文、韩文这样的象形文字，UCS还包括大量的图形、印刷、数学、科学符号。</div>
<div _extended="true"><span _extended="true">&nbsp;</span>UCS-2：与unicode的2byte编码基本一样。</div>
<div _extended="true"><span _extended="true">&nbsp;</span>UCS-4： 4byte编码, 目前是在UCS-2前加上2个全零的byte。</div>
<div _extended="true">&nbsp;</div>
<div _extended="true"><strong _extended="true">Unicode</strong><strong _extended="true">：</strong></div>
<div _extended="true"><strong _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></strong> Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Unicode</span>是<a href="http://www.unicode.org/" target="blank" _extended="true"><span _extended="true">http<span _extended="true">：</span>//www.unicode.org</span>制定的编码机制，要将全世界常用文字都函括进去。它为每种语言中的每个字符设定了统一并且唯一的二进制编码，以满足跨语言、跨平台进行文本转换、处理的要求。 1990年开始研发，1994年正式公布。随着计算机工作能力的增强，Unicode也在面世以来的十多年里得到普及。但自从unicode2.0开始，unicode采用了与ISO 10646-1相同的字库和字码，ISO也承诺ISO10646将不会给超出0x10FFFF的UCS-4编码赋值，使得两者保持一致。</a></div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Unicode</span>的编码方式与ISO 10646的通用字符集(Universal Character Set，UCS)概念相对应，目前的用于实用的Unicode版本对应于UCS-2，使用16位的编码空间。也就是每个字符占用2个字节，基本满足各种语言的使用。实际上目前版本的Unicode尚未填充满这16位编码，保留了大量空间作为特殊使用或将来扩展。</div>
<div _extended="true">&nbsp;</div>
<div _extended="true"><strong _extended="true">UTF</strong><strong _extended="true">：</strong> Unicode 的实现方式不同于编码方式。</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> 一个字符的Unicode编码是确定的，但是在实际传输过程中，由于不同系统平台的设计不一定一致，以及出于节省空间的目的，对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式(Unicode Translation Format，简称为 UTF)。</div>
<div _extended="true"><span _extended="true">&nbsp;</span>UTF-8： 8bit变长编码，对于大多数常用字符集(ASCII中0~127字符)它只使用单字节，而对其它常用字符(特别是朝鲜和汉语会意文字)，它使用3字节。</div>
<div _extended="true"><span _extended="true">&nbsp;</span>UTF-16： 16bit编码，是变长码，大致相当于20位编码，值在0到0x10FFFF之间，基本上就是unicode编码的实现，与CPU字序有关。</div>
<div _extended="true">&nbsp;</div>
<div _extended="true"><strong _extended="true">汉字编码：</strong></div>
<div _extended="true"><span _extended="true">&nbsp;</span>GB2312字集是简体字集，全称为GB2312(80)字集，共包括国标简体汉字6763个。</div>
<div _extended="true"><span _extended="true">&nbsp;</span>BIG5字集是台湾繁体字集，共包括国标繁体汉字13053个。</div>
<div _extended="true"><span _extended="true">&nbsp;</span>GBK字集是简繁字集，包括了GB字集、BIG5字集和一些符号，共包括21003个字符。</div>
<div _extended="true"><span _extended="true">&nbsp;</span>GB18030是国家制定的一个强制性大字集标准，全称为GB18030-2000，它的推出使汉字集有了一个&#8220;大一统&#8221;的标准。</div>
<div _extended="true">&nbsp;</div>
<div _extended="true"><strong _extended="true">ANSI</strong><strong _extended="true">和Unicode big endia</strong><strong _extended="true">：</strong></div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> 我们在Windows系统中保存文本文件时通常可以选择编码为ANSI、Unicode、Unicode big endian和UTF-8，这里的ANSI和Unicode big endia是什么编码呢?</div>
<div _extended="true"><span _extended="true">&nbsp;</span><strong _extended="true">ANSI</strong><strong _extended="true">：</strong>使用2个字节来代表一个字符的各种汉字延伸编码方式，称为ANSI编码。在简体中文系统下，ANSI编码代表GB2312编码，在日文操作系统下，ANSI编码代表JIS编码。</div>
<div _extended="true"><span _extended="true">&nbsp;</span><strong _extended="true">Unicode big endia</strong><strong _extended="true">：</strong> UTF-8以字节为编码单元，没有字节序的问题。UTF-16以两个字节为编码单元，在解释一个UTF-16文本前，首先要弄清楚每个编码单元的字节序。 Unicode规范中推荐的标记字节顺序的方法是BOM(即Byte Order Mark)。在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符，它的编码是FEFF。而FFFE在UCS中是不存在的字符，所以不应该出现在实际传输中。 UCS规范建议我们在传输字节流前，先传输字符"ZERO WIDTH NO-BREAK SPACE"。这样如果接收者收到FEFF，就表明这个字节流是Big-Endian的；如果收到FFFE，就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。 Windows就是使用BOM来标记文本文件的编码方式的。</div>
<div _extended="true"><strong _extended="true"><font size="6" _extended="true"><span _extended="true">4<span _extended="true"><font size="6" _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span> 为什么会乱码?</font></strong></div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> 乱码是个老问题，字符在保存时的编码格式如果和要显示的编码格式不一样的话，就会出现乱码问题。在我们的Web应用中，从底层数据库编码、Web应用程序编码到HTML页面编码，如果有一项不一致的话，就会出现乱码。所以，解决乱码问题说难也难说简单也简单，关键是让交互系统之间编码一致。</div>
<div _extended="true"><strong _extended="true"><font size="6" _extended="true"><span _extended="true">5<span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span> Java字符编码</font></strong></div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Java</span>在运行期一律以unicode来存储字符,这样有利的支持了多语言环境。</div>
<div _extended="true">&nbsp;&nbsp; <span _extended="true">&nbsp;&nbsp; Java</span>在读取文件的时候默认是按照系统默认语言（字符集）编码来解码文件，读取和保存时候的编码不一致也导致程序中参数值错误，用FileInputStream类读取文件可以指定编码读取。</div>
<div _extended="true">&nbsp;&nbsp; <span _extended="true">&nbsp;&nbsp; Java</span>在输出到系统显示时，会把内存中变量字符再通过系统默认语言（字符集）编码去转换，所以在输出过程中也会碰到一系列的编码问题。</div>
<div _extended="true"><strong _extended="true"><font size="6" _extended="true"><span _extended="true">6<span _extended="true"><font size="6" _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font> 编码</span></span>JSP</font></strong></div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> 这里我们主要是介绍JSP页面的两个重要属性：<strong _extended="true">pageEncoding</strong>、<strong _extended="true">contentType</strong>、<strong _extended="true">charset</strong></div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong _extended="true">pageEncoding</strong></span>是jsp文件本身的编码</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong _extended="true">contentType</strong></span>的charset是指服务器发送给客户端时的内容编码</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong _extended="true">charset</strong></span>有两个作用：一是JSP文件的编码方式：在读取JSP文件、生成JAVA类时，源JSP文件中汉字的编码；二是JSP输出流的编码方式：在执行JSP时，往response流里面写入数据的编码方式</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> 当应用服务器将JSP编译成.java文件时，会根据pageEncoding的设定读取jsp，结果是由指定的编码方案翻译成统一的UTF-8 JAVA源码（即.java），如果pageEncoding设定错了，或没有设定，出来的就是中文乱码。</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> 当应用服务器利用已经编译为二进制的JSP(.class)输出页面时，contentType这时则决定了输出页面的编码。</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jsp</span>文件不像.java，.java在被编译器读入的时候默认采用的是操作系统所设定的locale所对应的编码，比如中国大陆就是GBK，台湾就是BIG5或者MS950。而一般我们不管是在记事本还是在ue中写代码，如果没有经过特别转码的话，写出来的都是本地编码格式的内容。所以编译器采用的方法刚好可以让虚拟机得到正确的资料。</div>
<div _extended="true">但是jsp文件不是这样，它没有这个默认转码过程，但是指定了pageEncoding就可以实现正确转码了。</div>
<div _extended="true">举个例子：</div>
<div align="center" _extended="true">
<table cellspacing="0" cellpadding="0" width="400" border="1" _extended="true">
    <tbody _extended="true">
        <tr _extended="true">
            <td _extended="true">
            <div _extended="true">&lt;%@ page contentType="text/html;charset=utf-8" %&gt;</div>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div _extended="true">大都会打印出乱码，因为我输入的&#8220;你好吗&#8221;是gbk的，但是服务器是否正确抓到&#8220;你好吗&#8221;不得而知。</div>
<div _extended="true">但是如果更改为</div>
<div align="center" _extended="true">
<table cellspacing="0" cellpadding="0" width="400" border="1" _extended="true">
    <tbody _extended="true">
        <tr _extended="true">
            <td _extended="true">
            <div _extended="true">&lt;%@ page contentType="text/html;charset=utf-8" pageEncoding="GBK"%&gt;</div>
            </td>
        </tr>
    </tbody>
</table>
</div>
<div _extended="true">这样就服务器一定会是正确抓到&#8220;你好吗&#8221;了。</div>
<div _extended="true"><strong _extended="true"><font size="6" _extended="true"><span _extended="true">7<span _extended="true"><font size="6" _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span> 有没有万金油?</font></strong></div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; J2EE</span>应用程序是运行在J2EE容器中。在这个系统中，输入途径有很多种：一种是通过页面表单打包成请求（request）发往服务器的；第二种是通过数据库读入；还有第3种输入比较复杂，JSP在第一次运行时总是被编译成Servlet，JSP中常常包含中文字符，那么编译使用javac时，Java将根据默认的操作系统编码作为初始编码。除非特别指定，如在Jbuilder/eclipse中可以指定默认的字符集。</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> 输出途径也有几种：第一种是JSP页面的输出。由于JSP页面已经被编译成Servlet，那么在输出时，也将根据操作系统的默认编码来选择输出编码，除非指定输出编码方式；还有输出途径是数据库，将字符串输出到数据库。</div>
<div _extended="true">由此看来，一个J2EE系统的输入输出是非常复杂，而且是动态变化的，而Java是跨平台运行的，在实际编译和运行中，都可能涉及到不同的操作系统，如果任由Java自由根据操作系统来决定输入输出的编码字符集，这将不可控制地出现乱码。</div>
<div _extended="true">正是由于Java的跨平台特性，使得字符集问题必须由具体系统来统一解决，所以在一个Java应用系统中，解决中文乱码的根本办法是明确指定整个应用系统统一字符集。</div>
<div _extended="true">指定统一字符集时，到底是指定ISO8859_1 、GBK还是UTF-8呢？</div>
<div _extended="true">1）如统一指定为ISO8859_1，因为目前大多数软件都是西方人编制的，他们默认的字符集就是ISO8859_1，包括操作系统Linux和数据库MySQL等。这样，如果指定Jive统一编码为ISO8859_1，那么就有下面3个环节必须把握：</div>
<div _extended="true">开发和编译代码时指定字符集为ISO8859_1。</div>
<div _extended="true">运行操作系统的默认编码必须是ISO8859_1，如Linux。</div>
<div _extended="true">在JSP头部声明。</div>
<div _extended="true">2）如果统一指定为GBK中文字符集，上述3个环节同样需要做到，不同的是只能运行在默认编码为GBK的操作系统，如中文Windows。</div>
<div _extended="true">统一编码为ISO8859_1和GBK虽然带来编制代码的方便，但是各自只能在相应的操作系统上运行。但是也破坏了Java跨平台运行的优越性，只在一定范围内行得通。例如，为了使得GBK编码在linux上运行，设置Linux编码为GBK。</div>
<div _extended="true">那么有没有一种除了应用系统以外不需要进行任何附加设置的中文编码根本解决方案呢？</div>
<div _extended="true">将Java/J2EE系统的统一编码定义为UTF-8。UTF-8编码是一种兼容所有语言的编码方式，惟一比较麻烦的就是要找到应用系统的所有出入口，然后使用UTF-8去&#8220;结扎&#8221;它。</div>
<div _extended="true">一个J2EE应用系统需要做下列几步工作：</div>
<div _extended="true">开发和编译代码时指定字符集为UTF-8。JBuilder和<a href="http://gocom.primeton.com/modules/gSpace/about_onetag.php?tagid=103&amp;tagname=Eclipse&amp;PHPSESSID=8adba887c717699c206a09c755d0bbb0" target="_blank" _extended="true">Eclipse</a>都可以在项目属性中设置。</div>
<div _extended="true">使用过滤器，如果所有请求都经过一个Servlet控制分配器，那么使用Servlet的filter执行语句，将所有来自浏览器的请求（request）转换为UTF-8，因为浏览器发过来的请求包根据浏览器所在的操作系统编码，可能是各种形式编码。关键一句：</div>
<div _extended="true">request.setCharacterEncoding("UTF-8")</div>
<div _extended="true">在JSP头部声明：</div>
<div _extended="true">＜%@ page contentType="text/html;charset= UTF-8" %＞</div>
<div _extended="true">在Jsp的html代码中，声明UTF-8：</div>
<div _extended="true">＜ＭＥＴＡ http-equiv="Content-Type" ＣＯＮＴＥＴ="text/html; charset=utf-8"＞</div>
<div _extended="true">设定数据库连接方式是UTF-8。例如连接MYSQL时配置URL如下：</div>
<div _extended="true"><span _extended="true">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jdbc</span>：mysql：//localhost：3306/test?useUnicode=true&amp;characterEncoding=UTF-8</div>
<div _extended="true">一般数据库都可以通过管理设置设定UTF-8。其他和外界交互时能够设定编码时就设定UTF-8，例如读取文件，操作XML等。</div>
<img src ="http://www.blogjava.net/jesenblog/aggbug/187271.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jesenblog/" target="_blank">白露</a> 2008-03-19 15:53 <a href="http://www.blogjava.net/jesenblog/articles/187271.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>