﻿<?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-桔子园-随笔分类-J2EE</title><link>http://www.blogjava.net/orangelizq/category/24143.html</link><description>orangelizq</description><language>zh-cn</language><lastBuildDate>Mon, 28 Jan 2008 04:17:14 GMT</lastBuildDate><pubDate>Mon, 28 Jan 2008 04:17:14 GMT</pubDate><ttl>60</ttl><item><title>消息中间件原理及JMS简介(2) </title><link>http://www.blogjava.net/orangelizq/archive/2008/01/27/178030.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sun, 27 Jan 2008 08:17:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2008/01/27/178030.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/178030.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2008/01/27/178030.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/178030.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/178030.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 消息中间件原理及JMS简介之二&nbsp;作者：orangelizq&nbsp; &nbsp;2.3 消息中间件的传递模式消息中间件一般有两种传递模型：点对点模型（PTP）和发布-订阅模型（Pub/Sub）[2]。1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 点对点模型（PTP）点对点模型用于消息生产者和消息消费者之间点到...&nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2008/01/27/178030.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/178030.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2008-01-27 16:17 <a href="http://www.blogjava.net/orangelizq/archive/2008/01/27/178030.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>消息中间件原理及JMS简介(1)</title><link>http://www.blogjava.net/orangelizq/archive/2008/01/27/178026.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sun, 27 Jan 2008 08:10:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2008/01/27/178026.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/178026.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2008/01/27/178026.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/178026.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/178026.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp; 消息中间件原理及JMS简介之一&nbsp;作者：orangelizq摘要：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 现今，越来越多的企业面临着各种各样的数据集成和系统整合，CORBA、DCOM、RMI等RPC中间件技术也应运而生，但由于采用RPC同步处理技术，在性能、健壮性、可扩展性上都存在着诸多缺点。而基于消息的异步处理模型采用非...&nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2008/01/27/178026.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/178026.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2008-01-27 16:10 <a href="http://www.blogjava.net/orangelizq/archive/2008/01/27/178026.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]了解Java ClassLoader</title><link>http://www.blogjava.net/orangelizq/archive/2007/09/02/142069.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sun, 02 Sep 2007 06:38:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/09/02/142069.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/142069.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/09/02/142069.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/142069.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/142069.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 什么是 ClassLoader?在流行的商业化编程语言中，Java 语言由于在 Java 虚拟机 (JVM) 上运行而显得与众不同。这意味着已编译的程序是一种特殊的、独立于平台的格式，并非依赖于它们所运行的机器。在很大程度上，这种格式不同于传统的可执行程序格式。 与 C 或 C++ 编写的程序不同，Java 程序并不是一个可执行文件，而是由许多独立的类文件组成，每一个文件对应于一个 Jav...&nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2007/09/02/142069.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/142069.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-09-02 14:38 <a href="http://www.blogjava.net/orangelizq/archive/2007/09/02/142069.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]Java泛型</title><link>http://www.blogjava.net/orangelizq/archive/2007/08/30/141452.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Thu, 30 Aug 2007 09:25:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/08/30/141452.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/141452.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/08/30/141452.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/141452.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/141452.html</trackback:ping><description><![CDATA[<p><strong>一 泛型简介</strong><br><br>什么是泛型？</p>
<p>泛型（Generic type 或者 generics）是对 Java 语言的类型系统的一种扩展，以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符，就像方法的形式参数是运行时传递的值的占位符一样。 </p>
<p>可以在集合框架（Collection framework）中看到泛型的动机。例如，Map 类允许您向一个 Map 添加任意类的对象，即使最常见的情况是在给定映射（map）中保存某个特定类型（比如 String）的对象。</p>
<p>因为 Map.get() 被定义为返回 Object，所以一般必须将 Map.get() 的结果强制类型转换为期望的类型，如下面的代码所示：</p>
<p>Map m = new HashMap();<br>m.put("key", "blarg");<br>String s = (String) m.get("key");</p>
<p>要让程序通过编译，必须将 get() 的结果强制类型转换为 String，并且希望结果真的是一个 String。但是有可能某人已经在该映射中保存了不是 String 的东西，这样的话，上面的代码将会抛出 ClassCastException。 </p>
<p>理想情况下，您可能会得出这样一个观点，即 m 是一个 Map，它将 String 键映射到 String 值。这可以让您消除代码中的强制类型转换，同时获得一个附加的类型检查层，该检查层可以防止有人将错误类型的键或值保存在集合中。这就是泛型所做的工作。</p>
<p>&nbsp;</p>
<p>泛型的好处</p>
<p>Java 语言中引入泛型是一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化，以支持泛型，而且类库也进行了大翻修，所以许多重要的类，比如集合框架，都已经成为泛型化的了。这带来了很多好处：</p>
<p>类型安全。 泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制，编译器可以在一个高得多的程度上验证类型假设。没有泛型，这些假设就只存在于程序员的头脑中（或者如果幸运的话，还存在于代码注释中）。</p>
<p>Java 程序中的一种流行技术是定义这样的集合，即它的元素或键是公共类型的，比如&#8220;String 列表&#8221;或者&#8220;String 到 String 的映射&#8221;。通过在变量声明中捕获这一附加的类型信息，泛型允许编译器实施这些附加的类型约束。类型错误现在就可以在编译时被捕获了，而不是在运行时当作 ClassCastException 展示出来。将类型检查从运行时挪到编译时有助于您更容易找到错误，并可提高程序的可靠性。 </p>
<p>消除强制类型转换。 泛型的一个附带好处是，消除源代码中的许多强制类型转换。这使得代码更加可读，并且减少了出错机会。</p>
<p>尽管减少强制类型转换可以降低使用泛型类的代码的罗嗦程度，但是声明泛型变量会带来相应的罗嗦。比较下面两个代码例子。</p>
<p>该代码不使用泛型： </p>
<p>List li = new ArrayList();<br>li.put(new Integer(3));<br>Integer i =&nbsp; (Integer) li.get(0);</p>
<p><br>该代码使用泛型： </p>
<p>List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br>li.put(new Integer(3));<br>Integer i =&nbsp; li.get(0);</p>
<p><br>在简单的程序中使用一次泛型变量不会降低罗嗦程度。但是对于多次使用泛型变量的大型程序来说，则可以累积起来降低罗嗦程度。</p>
<p>潜在的性能收益。 泛型为较大的优化带来可能。在泛型的初始实现中，编译器将强制类型转换（没有泛型的话，程序员会指定这些强制类型转换）插入生成的字节码中。但是更多类型信息可用于编译器这一事实，为未来版本的 JVM 的优化带来可能。</p>
<p>由于泛型的实现方式，支持泛型（几乎）不需要 JVM 或类文件更改。所有工作都在编译器中完成，编译器生成类似于没有泛型（和强制类型转换）时所写的代码，只是更能确保类型安全而已。 </p>
<p><br>泛型用法的例子</p>
<p>泛型的许多最佳例子都来自集合框架，因为泛型让您在保存在集合中的元素上指定类型约束。考虑这个使用 Map 类的例子，其中涉及一定程度的优化，即 Map.get() 返回的结果将确实是一个 String：</p>
<p><br>Map m = new HashMap();<br>m.put("key", "blarg");<br>String s = (String) m.get("key");</p>
<p><br>如果有人已经在映射中放置了不是 String 的其他东西，上面的代码将会抛出 ClassCastException。泛型允许您表达这样的类型约束，即 m 是一个将 String 键映射到 String 值的 Map。这可以消除代码中的强制类型转换，同时获得一个附加的类型检查层，这个检查层可以防止有人将错误类型的键或值保存在集合中。 </p>
<p>下面的代码示例展示了 JDK 5.0 中集合框架中的 Map 接口的定义的一部分： </p>
<p><br>public interface Map&lt;K, V&gt; {<br>&nbsp; public void put(K key, V value);<br>&nbsp; public V get(K key);<br>}</p>
<p>注意该接口的两个附加物： </p>
<p>类型参数 K 和 V 在类级别的规格说明，表示在声明一个 Map 类型的变量时指定的类型的占位符。</p>
<p>在 get()、put() 和其他方法的方法签名中使用的 K 和 V。 </p>
<p>为了赢得使用泛型的好处，必须在定义或实例化 Map 类型的变量时为 K 和 V 提供具体的值。以一种相对直观的方式做这件事：</p>
<p>Map&lt;String, String&gt; m = new HashMap&lt;String, String&gt;();<br>m.put("key", "blarg");<br>String s = m.get("key");</p>
<p>当使用 Map 的泛型化版本时，您不再需要将 Map.get() 的结果强制类型转换为 String，因为编译器知道 get() 将返回一个 String。</p>
<p>在使用泛型的版本中并没有减少键盘录入；实际上，比使用强制类型转换的版本需要做更多键入。使用泛型只是带来了附加的类型安全。因为编译器知道关于您将放进 Map 中的键和值的类型的更多信息，所以类型检查从执行时挪到了编译时，这会提高可靠性并加快开发速度。 </p>
<p><br>向后兼容</p>
<p>在 Java 语言中引入泛型的一个重要目标就是维护向后兼容。尽管 JDK 5.0 的标准类库中的许多类，比如集合框架，都已经泛型化了，但是使用集合类（比如 HashMap 和 ArrayList）的现有代码将继续不加修改地在 JDK 5.0 中工作。当然，没有利用泛型的现有代码将不会赢得泛型的类型安全好处。 </p>
<p>&nbsp;</p>
<p><br><strong>二 泛型基础</strong></p>
<p>类型参数</p>
<p>在定义泛型类或声明泛型类的变量时，使用尖括号来指定形式类型参数。形式类型参数与实际类型参数之间的关系类似于形式方法参数与实际方法参数之间的关系，只是类型参数表示类型，而不是表示值。 </p>
<p>泛型类中的类型参数几乎可以用于任何可以使用类名的地方。例如，下面是 java.util.Map 接口的定义的摘录：</p>
<p>public interface Map&lt;K, V&gt; {<br>&nbsp; public void put(K key, V value);<br>&nbsp; public V get(K key);<br>}</p>
<p>Map 接口是由两个类型参数化的，这两个类型是键类型 K 和值类型 V。（不使用泛型）将会接受或返回 Object 的方法现在在它们的方法签名中使用 K 或 V，指示附加的类型约束位于 Map 的规格说明之下。 </p>
<p>当声明或者实例化一个泛型的对象时，必须指定类型参数的值： </p>
<p>Map&lt;String, String&gt; map = new HashMap&lt;String, String&gt;();</p>
<p>注意，在本例中，必须指定两次类型参数。一次是在声明变量 map 的类型时，另一次是在选择 HashMap 类的参数化以便可以实例化正确类型的一个实例时。</p>
<p>编译器在遇到一个 Map&lt;String, String&gt; 类型的变量时，知道 K 和 V 现在被绑定为 String，因此它知道在这样的变量上调用 Map.get() 将会得到 String 类型。</p>
<p>除了异常类型、枚举或匿名内部类以外，任何类都可以具有类型参数。 </p>
<p><br>命名类型参数</p>
<p>推荐的命名约定是使用大写的单个字母名称作为类型参数。这与 C++ 约定有所不同（参阅 附录 A：与 C++ 模板的比较），并反映了大多数泛型类将具有少量类型参数的假定。对于常见的泛型模式，推荐的名称是： </p>
<p>K —— 键，比如映射的键。 <br>V —— 值，比如 List 和 Set 的内容，或者 Map 中的值。 <br>E —— 异常类。 <br>T —— 泛型。 </p>
<p><br>泛型不是协变的 </p>
<p>关于泛型的混淆，一个常见的来源就是假设它们像数组一样是协变的。其实它们不是协变的。List&lt;Object&gt; 不是 List&lt;String&gt; 的父类型。 </p>
<p>如果 A 扩展 B，那么 A 的数组也是 B 的数组，并且完全可以在需要 B[] 的地方使用 A[]： </p>
<p>Integer[] intArray = new Integer[10];&nbsp; <br>Number[] numberArray = intArray;</p>
<p>上面的代码是有效的，因为一个 Integer 是 一个 Number，因而一个 Integer 数组是 一个 Number 数组。但是对于泛型来说则不然。下面的代码是无效的： </p>
<p>List&lt;Integer&gt; intList = new ArrayList&lt;Integer&gt;();<br>List&lt;Number&gt; numberList = intList; // invalid</p>
<p>最初，大多数 Java 程序员觉得这缺少协变很烦人，或者甚至是&#8220;坏的（broken）&#8221;，但是之所以这样有一个很好的原因。如果可以将 List&lt;Integer&gt; 赋给 List&lt;Number&gt;，下面的代码就会违背泛型应该提供的类型安全： </p>
<p>List&lt;Integer&gt; intList = new ArrayList&lt;Integer&gt;();<br>List&lt;Number&gt; numberList = intList; // invalid<br>numberList.add(new Float(3.1415));</p>
<p>因为 intList 和 numberList 都是有别名的，如果允许的话，上面的代码就会让您将不是 Integers 的东西放进 intList 中。但是，正如下一屏将会看到的，您有一个更加灵活的方式来定义泛型。</p>
<p><br>类型通配符</p>
<p>假设您具有该方法： </p>
<p>void printList(List l) { <br>&nbsp; for (Object o : l) <br>&nbsp;&nbsp;&nbsp; System.out.println(o); <br>}</p>
<p>上面的代码在 JDK 5.0 上编译通过，但是如果试图用 List&lt;Integer&gt; 调用它，则会得到警告。出现警告是因为，您将泛型（List&lt;Integer&gt;）传递给一个只承诺将它当作 List（所谓的原始类型）的方法，这将破坏使用泛型的类型安全。</p>
<p>如果试图编写像下面这样的方法，那么将会怎么样？ </p>
<p>void printList(List&lt;Object&gt; l) { <br>&nbsp; for (Object o : l) <br>&nbsp;&nbsp;&nbsp; System.out.println(o); <br>}</p>
<p>它仍然不会通过编译，因为一个 List&lt;Integer&gt; 不是 一个 List&lt;Object&gt;（正如前一屏 泛型不是协变的 中所学的）。这才真正烦人 —— 现在您的泛型版本还没有普通的非泛型版本有用！</p>
<p>解决方案是使用类型通配符： </p>
<p>void printList(List&lt;?&gt; l) { <br>&nbsp; for (Object o : l) <br>&nbsp;&nbsp;&nbsp; System.out.println(o); <br>}</p>
<p>上面代码中的问号是一个类型通配符。它读作&#8220;问号&#8221;。List&lt;?&gt; 是任何泛型 List 的父类型，所以您完全可以将 List&lt;Object&gt;、List&lt;Integer&gt; 或 List&lt;List&lt;List&lt;Flutzpah&gt;&gt;&gt; 传递给 printList()。 </p>
<p><br>类型通配符的作用</p>
<p>前一屏 类型通配符 中引入了类型通配符，这让您可以声明 List&lt;?&gt; 类型的变量。您可以对这样的 List 做什么呢？非常方便，可以从中检索元素，但是不能添加元素。原因不是编译器知道哪些方法修改列表哪些方法不修改列表，而是（大多数）变化的方法比不变化的方法需要更多的类型信息。下面的代码则工作得很好： </p>
<p>List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br>li.add(new Integer(42));<br>List&lt;?&gt; lu = li;<br>System.out.println(lu.get(0));</p>
<p>为什么该代码能工作呢？对于 lu，编译器一点都不知道 List 的类型参数的值。但是编译器比较聪明，它可以做一些类型推理。在本例中，它推断未知的类型参数必须扩展 Object。（这个特定的推理没有太大的跳跃，但是编译器可以作出一些非常令人佩服的类型推理，后面就会看到（在 底层细节 一节中）。所以它让您调用 List.get() 并推断返回类型为 Object。 </p>
<p>另一方面，下面的代码不能工作： </p>
<p>List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br>li.add(new Integer(42));<br>List&lt;?&gt; lu = li;<br>lu.add(new Integer(43));&nbsp; // error</p>
<p>在本例中，对于 lu，编译器不能对 List 的类型参数作出足够严密的推理，以确定将 Integer 传递给 List.add() 是类型安全的。所以编译器将不允许您这么做。</p>
<p>以免您仍然认为编译器知道哪些方法更改列表的内容哪些不更改列表内容，请注意下面的代码将能工作，因为它不依赖于编译器必须知道关于 lu 的类型参数的任何信息：</p>
<p>List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br>li.add(new Integer(42));<br>List&lt;?&gt; lu = li;<br>lu.clear();</p>
<p><br>泛型方法</p>
<p>（在 类型参数 一节中）您已经看到，通过在类的定义中添加一个形式类型参数列表，可以将类泛型化。方法也可以被泛型化，不管它们定义在其中的类是不是泛型化的。</p>
<p>泛型类在多个方法签名间实施类型约束。在 List&lt;V&gt; 中，类型参数 V 出现在 get()、add()、contains() 等方法的签名中。当创建一个 Map&lt;K, V&gt; 类型的变量时，您就在方法之间宣称一个类型约束。您传递给 add() 的值将与 get() 返回的值的类型相同。</p>
<p>类似地，之所以声明泛型方法，一般是因为您想要在该方法的多个参数之间宣称一个类型约束。例如，下面代码中的 ifThenElse() 方法，根据它的第一个参数的布尔值，它将返回第二个或第三个参数：</p>
<p>public &lt;T&gt; T ifThenElse(boolean b, T first, T second) {<br>&nbsp; return b ? first : second;<br>}</p>
<p>注意，您可以调用 ifThenElse()，而不用显式地告诉编译器，您想要 T 的什么值。编译器不必显式地被告知 T 将具有什么值；它只知道这些值都必须相同。编译器允许您调用下面的代码，因为编译器可以使用类型推理来推断出，替代 T 的 String 满足所有的类型约束：</p>
<p>String s = ifThenElse(b, "a", "b");</p>
<p>类似地，您可以调用： </p>
<p>Integer i = ifThenElse(b, new Integer(1), new Integer(2));</p>
<p>但是，编译器不允许下面的代码，因为没有类型会满足所需的类型约束： </p>
<p>String s = ifThenElse(b, "pi", new Float(3.14));</p>
<p>为什么您选择使用泛型方法，而不是将类型 T 添加到类定义呢？（至少）有两种情况应该这样做： </p>
<p>当泛型方法是静态的时，这种情况下不能使用类类型参数。</p>
<p>当 T 上的类型约束对于方法真正是局部的时，这意味着没有在相同类的另一个 方法签名中使用相同 类型 T 的约束。通过使得泛型方法的类型参数对于方法是局部的，可以简化封闭类型的签名。 </p>
<p><br>有限制类型 </p>
<p>在前一屏 泛型方法 的例子中，类型参数 V 是无约束的或无限制的 类型。有时在还没有完全指定类型参数时，需要对类型参数指定附加的约束。</p>
<p>考虑例子 Matrix 类，它使用类型参数 V，该参数由 Number 类来限制：</p>
<p>public class Matrix&lt;V extends Number&gt; { ... }</p>
<p>编译器允许您创建 Matrix&lt;Integer&gt; 或 Matrix&lt;Float&gt; 类型的变量，但是如果您试图定义 Matrix&lt;String&gt; 类型的变量，则会出现错误。类型参数 V 被判断为由 Number 限制 。在没有类型限制时，假设类型参数由 Object 限制。这就是为什么前一屏 泛型方法 中的例子，允许 List.get() 在 List&lt;?&gt; 上调用时返回 Object，即使编译器不知道类型参数 V 的类型。</p>
<p>&nbsp;</p>
<p><strong>三 一个简单的泛型类</strong></p>
<p>编写基本的容器类</p>
<p>此时，您可以开始编写简单的泛型类了。到目前为止，泛型类最常见的用例是容器类（比如集合框架）或者值持有者类（比如 WeakReference 或 ThreadLocal）。我们来编写一个类，它类似于 List，充当一个容器。其中，我们使用泛型来表示这样一个约束，即 Lhist 的所有元素将具有相同类型。为了实现起来简单，Lhist 使用一个固定大小的数组来保存值，并且不接受 null 值。</p>
<p>Lhist 类将具有一个类型参数 V（该参数是 Lhist 中的值的类型），并将具有以下方法：</p>
<p>public class Lhist&lt;V&gt; { <br>&nbsp; public Lhist(int capacity) { ... }<br>&nbsp; public int size() { ... }<br>&nbsp; public void add(V value) { ... }<br>&nbsp; public void remove(V value) { ... }<br>&nbsp; public V get(int index) { ... }<br>}</p>
<p>要实例化 Lhist，只要在声明时指定类型参数和想要的容量： </p>
<p>Lhist&lt;String&gt; stringList = new Lhist&lt;String&gt;(10);</p>
<p><br>实现构造函数 </p>
<p>在实现 Lhist 类时，您将会遇到的第一个拦路石是实现构造函数。您可能会像下面这样实现它： </p>
<p>public class Lhist&lt;V&gt; { <br>&nbsp; private V[] array;<br>&nbsp; public Lhist(int capacity) {<br>&nbsp;&nbsp;&nbsp; array = new V[capacity]; // illegal<br>&nbsp; }<br>}</p>
<p>这似乎是分配后备数组最自然的一种方式，但是不幸的是，您不能这样做。具体原因很复杂，当学习到 底层细节 一节中的&#8220;擦除&#8221;主题时，您就会明白。分配后备数组的实现方式很古怪且违反直觉。下面是构造函数的一种可能的实现（该实现使用集合类所采用的方法）：</p>
<p>public class Lhist&lt;V&gt; { <br>&nbsp; private V[] array;<br>&nbsp; public Lhist(int capacity) {<br>&nbsp;&nbsp;&nbsp; array = (V[]) new Object[capacity];<br>&nbsp; }<br>}</p>
<p><br>另外，也可以使用反射来实例化数组。但是这样做需要给构造函数传递一个附加的参数 —— 一个类常量，比如 Foo.class。后面在 Class&lt;T&gt; 一节中将讨论类常量。</p>
<p><br>实现方法 </p>
<p>实现 Lhist 的方法要容易得多。下面是 Lhist 类的完整实现：</p>
<p>public class Lhist&lt;V&gt; {<br>&nbsp;&nbsp;&nbsp; private V[] array;<br>&nbsp;&nbsp;&nbsp; private int size;</p>
<p>&nbsp;&nbsp;&nbsp; public Lhist(int capacity) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; array = (V[]) new Object[capacity];<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public void add(V value) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (size == array.length)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new IndexOutOfBoundsException(Integer.toString(size));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (value == null)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new NullPointerException();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; array[size++] = value;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public void remove(V value) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int removalCount = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i=0; i&lt;size; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (array[i].equals(value))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ++removalCount;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (removalCount &gt; 0) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; array[i-removalCount] = array[i];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; array[i] = null;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size -= removalCount;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public int size() { return size; }</p>
<p>&nbsp;&nbsp;&nbsp; public V get(int i) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (i &gt;= size)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new IndexOutOfBoundsException(Integer.toString(i));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return array[i];<br>&nbsp;&nbsp;&nbsp; }<br>}</p>
<p>注意，您在将会接受或返回 V 的方法中使用了形式类型参数 V，但是您一点也不知道 V 具有什么样的方法或域，因为这些对泛型代码是不可知的。 </p>
<p><br>使用 Lhist 类</p>
<p>使用 Lhist 类很容易。要定义一个整数 Lhist，只需要在声明和构造函数中为类型参数提供一个实际值即可：</p>
<p>Lhist&lt;Integer&gt; li = new Lhist&lt;Integer&gt;(30);</p>
<p>编译器知道，li.get() 返回的任何值都将是 Integer 类型，并且它还强制传递给 li.add() 或 li.remove() 的任何东西都是 Integer。除了实现构造函数的方式很古怪之外，您不需要做任何十分特殊的事情以使 Lhist 是一个泛型类。 </p>
<p>&nbsp;</p>
<p><strong>四 Java类库中的泛型</strong></p>
<p>集合类</p>
<p>到目前为止，Java 类库中泛型支持存在最多的地方就是集合框架。就像容器类是 C++ 语言中模板的主要动机一样（参阅 附录 A：与 C++ 模板的比较）（尽管它们随后用于很多别的用途），改善集合类的类型安全是 Java 语言中泛型的主要动机。集合类也充当如何使用泛型的模型，因为它们演示了泛型的几乎所有的标准技巧和方言。 </p>
<p>所有的标准集合接口都是泛型化的 —— Collection&lt;V&gt;、List&lt;V&gt;、Set&lt;V&gt; 和 Map&lt;K,V&gt;。类似地，集合接口的实现都是用相同类型参数泛型化的，所以 HashMap&lt;K,V&gt; 实现 Map&lt;K,V&gt; 等。</p>
<p>集合类也使用泛型的许多&#8220;技巧&#8221;和方言，比如上限通配符和下限通配符。例如，在接口 Collection&lt;V&gt; 中，addAll 方法是像下面这样定义的： </p>
<p>interface Collection&lt;V&gt; {<br>&nbsp; boolean addAll(Collection&lt;? extends V&gt;);<br>}</p>
<p>该定义组合了通配符类型参数和有限制类型参数，允许您将 Collection&lt;Integer&gt; 的内容添加到 Collection&lt;Number&gt;。</p>
<p>如果类库将 addAll() 定义为接受 Collection&lt;V&gt;，您就不能将 Collection&lt;Integer&gt; 的内容添加到 Collection&lt;Number&gt;。不是限制 addAll() 的参数是一个与您将要添加到的集合包含相同类型的集合，而有可能建立一个更合理的约束，即传递给 addAll() 的集合的元素 适合于添加到您的集合。有限制类型允许您这样做，并且使用有限制通配符使您不需要使用另一个不会用在其他任何地方的占位符名称。 </p>
<p>应该可以将 addAll() 的类型参数定义为 Collection&lt;V&gt;。但是，这不但没什么用，而且还会改变 Collection 接口的语义，因为泛型版本的语义将会不同于非泛型版本的语义。这阐述了泛型化一个现有的类要比定义一个新的泛型类难得多，因为您必须注意不要更改类的语义或者破坏现有的非泛型代码。 </p>
<p>作为泛型化一个类（如果不小心的话）如何会更改其语义的一个更加微妙的例子，注意 Collection.removeAll() 的参数的类型是 Collection&lt;?&gt;，而不是 Collection&lt;? extends V&gt;。这是因为传递混合类型的集合给 removeAll() 是可接受的，并且更加限制地定义 removeAll 将会更改方法的语义和有用性。 </p>
<p>&nbsp;</p>
<p>其他容器类 </p>
<p>除了集合类之外，Java 类库中还有几个其他的类也充当值的容器。这些类包括 WeakReference、SoftReference 和 ThreadLocal。它们都已经在其包含的值的类型上泛型化了，所以 WeakReference&lt;T&gt; 是对 T 类型的对象的弱引用，ThreadLocal&lt;T&gt; 则是到 T 类型的线程局部变量的句柄。 </p>
<p><br>泛型不止用于容器 </p>
<p>泛型最常见最直观的使用是容器类，比如集合类或引用类（比如 WeakReference&lt;T&gt;）。Collection&lt;V&gt; 中类型参数的含义很明显 —— &#8220;一个所有值都是 V 类型的集合&#8221;。类似地，ThreadLocal&lt;T&gt; 也有一个明显的解释 —— &#8220;一个其类型是 T 的线程局部变量&#8221;。但是，泛型规格说明中没有指定容积。 </p>
<p>像 Comparable&lt;T&gt; 或 Class&lt;T&gt; 这样的类中类型参数的含义更加微妙。有时，就像 Class&lt;T&gt; 中一样，类型变量主要是帮助编译器进行类型推理。有时，就像隐含的 Enum&lt;E extends Enum&lt;E&gt;&gt; 中一样，类型变量只是在类层次结构上加一个约束。</p>
<p><br>Comparable&lt;T&gt; </p>
<p>Comparable 接口已经泛型化了，所以实现 Comparable 的对象声明它可以与什么类型进行比较。（通常，这是对象本身的类型，但是有时也可能是父类。）</p>
<p>public interface Comparable&lt;T&gt; { <br>&nbsp; public boolean compareTo(T other);<br>} </p>
<p>所以 Comparable 接口包含一个类型参数 T，该参数是一个实现 Comparable 的类可以与之比较的对象的类型。这意味着如果定义一个实现 Comparable 的类，比如 String，就必须不仅声明类支持比较，还要声明它可与什么比较（通常是与它本身比较）： </p>
<p>public class String implements Comparable&lt;String&gt; { ... }</p>
<p>现在来考虑一个二元 max() 方法的实现。您想要接受两个相同类型的参数，二者都是 Comparable，并且相互之间是 Comparable。幸运的是，如果使用泛型方法和有限制类型参数的话，这相当直观： </p>
<p>public static &lt;T extends Comparable&lt;T&gt;&gt; T max(T t1, T t2) {<br>&nbsp; if (t1.compareTo(t2) &gt; 0)<br>&nbsp;&nbsp;&nbsp; return t1;<br>&nbsp; else <br>&nbsp;&nbsp;&nbsp; return t2;<br>}</p>
<p>在本例中，您定义了一个泛型方法，在类型 T 上泛型化，您约束该类型扩展（实现） Comparable&lt;T&gt;。两个参数都必须是 T 类型，这表示它们是相同类型，支持比较，并且相互可比较。容易！ </p>
<p>更好的是，编译器将使用类型推理来确定当调用 max() 时 T 的值表示什么意思。所以根本不用指定 T，下面的调用就能工作：</p>
<p>String s = max("moo", "bark");</p>
<p>编译器将计算出 T 的预定值是 String，因此它将进行编译和类型检查。但是如果您试图用不实现 Comparable&lt;X&gt; 的 类 X 的参数调用 max()，那么编译器将不允许这样做。 </p>
<p>&nbsp;</p>
<p>Class&lt;T&gt; </p>
<p>类 Class 已经泛型化了，但是很多人一开始都感觉其泛型化的方式很混乱。Class&lt;T&gt; 中类型参数 T 的含义是什么？事实证明它是所引用的类接口。怎么会是这样的呢？那是一个循环推理？如果不是的话，为什么这样定义它？</p>
<p>在以前的 JDK 中，Class.newInstance() 方法的定义返回 Object，您很可能要将该返回类型强制转换为另一种类型：</p>
<p>class Class { <br>&nbsp; Object newInstance();<br>}</p>
<p>但是使用泛型，您定义 Class.newInstance() 方法具有一个更加特定的返回类型： </p>
<p>class Class&lt;T&gt; { <br>&nbsp; T newInstance();<br>}</p>
<p>如何创建一个 Class&lt;T&gt; 类型的实例？就像使用非泛型代码一样，有两种方式：调用方法 Class.forName() 或者使用类常量 X.class。Class.forName() 被定义为返回 Class&lt;?&gt;。另一方面，类常量 X.class 被定义为具有类型 Class&lt;X&gt;，所以 String.class 是 Class&lt;String&gt; 类型的。 </p>
<p>让 Foo.class 是 Class&lt;Foo&gt; 类型的有什么好处？大的好处是，通过类型推理的魔力，可以提高使用反射的代码的类型安全。另外，还不需要将 Foo.class.newInstance() 强制类型转换为 Foo。</p>
<p>考虑一个方法，它从数据库检索一组对象，并返回 JavaBeans 对象的一个集合。您通过反射来实例化和初始化创建的对象，但是这并不意味着类型安全必须完全被抛至脑后。考虑下面这个方法：</p>
<p>public static&lt;T&gt; List&lt;T&gt; getRecords(Class&lt;T&gt; c, Selector s) {<br>&nbsp; // Use Selector to select rows<br>&nbsp; List&lt;T&gt; list = new ArrayList&lt;T&gt;();<br>&nbsp; for (/* iterate over results */) {<br>&nbsp;&nbsp;&nbsp; T row = c.newInstance();<br>&nbsp;&nbsp;&nbsp; // use reflection to set fields from result<br>&nbsp;&nbsp;&nbsp; list.add(row);&nbsp; <br>&nbsp; }<br>&nbsp; return list;<br>}</p>
<p>可以像下面这样简单地调用该方法： </p>
<p>List&lt;FooRecord&gt; l = getRecords(FooRecord.class, fooSelector);</p>
<p>编译器将会根据 FooRecord.class 是 Class&lt;FooRecord&gt; 类型的这一事实，推断 getRecords() 的返回类型。您使用类常量来构造新的实例并提供编译器在类型检查中要用到的类型信息。</p>
<p>&nbsp;</p>
<p>用 Class&lt;T&gt; 替换 T[]</p>
<p>Collection 接口包含一个方法，用于将集合的内容复制到一个调用者指定类型的数组中： </p>
<p>public Object[] toArray(Object[] prototypeArray) { ... }</p>
<p>toArray(Object[]) 的语义是，如果传递的数组足够大，就会使用它来保存结果，否则，就会使用反射分配一个相同类型的新数组。一般来说，单独传递一个数组作为参数来提供想要的返回类型是一个小技巧，但是在引入泛型之前，这是与方法交流类型信息最方便的方式。</p>
<p>有了泛型，就可以用一种更加直观的方式来做这件事。不像上面这样定义 toArray()，泛型 toArray() 可能看起来像下面这样：</p>
<p>public&lt;T&gt; T[] toArray(Class&lt;T&gt; returnType)</p>
<p>调用这样一个 toArray() 方法很简单： </p>
<p>FooBar[] fba = something.toArray(FooBar.class);</p>
<p>Collection 接口还没有改变为使用该技术，因为这会破坏许多现有的集合实现。但是如果使用泛型从新构建 Collection，则当然会使用该方言来指定它想要返回值是哪种类型。 </p>
<p>&nbsp;</p>
<p>Enum&lt;E&gt;</p>
<p>JDK 5.0 中 Java 语言另一个增加的特性是枚举。当您使用 enum 关键字声明一个枚举时，编译器就会在内部为您生成一个类，用于扩展 Enum 并为枚举的每个值声明静态实例。所以如果您说： </p>
<p>public enum Suit {HEART, DIAMOND, CLUB, SPADE};</p>
<p>编译器就会在内部生成一个叫做 Suit 的类，该类扩展 java.lang.Enum&lt;Suit&gt; 并具有叫做 HEART、DIAMOND、CLUB 和 SPADE 的常量（public static final）成员，每个成员都是 Suit 类。 </p>
<p>与 Class 一样，Enum 也是一个泛型类。但是与 Class 不同，它的签名稍微更复杂一些： </p>
<p>class Enum&lt;E extends Enum&lt;E&gt;&gt; { . . . }</p>
<p>这究竟是什么意思？这难道不会导致无限递归？ </p>
<p>我们逐步来分析。类型参数 E 用于 Enum 的各种方法中，比如 compareTo() 或 getDeclaringClass()。为了这些方法的类型安全，Enum 类必须在枚举的类上泛型化。 </p>
<p>所以 extends Enum&lt;E&gt; 部分如何理解？该部分又具有两个部分。第一部分指出，作为 Enum 的类型参数的类本身必须是 Enum 的子类型，所以您不能声明一个类 X 扩展 Enum&lt;Integer&gt;。第二部分指出，任何扩展 Enum 的类必须传递它本身 作为类型参数。您不能声明 X 扩展 Enum&lt;Y&gt;，即使 Y 扩展 Enum。 </p>
<p>总之，Enum 是一个参数化的类型，只可以为它的子类型实例化，并且这些子类型然后将根据子类型来继承方法。幸运的是，在 Enum 情况下，编译器为您做这些工作，一切都很好。 </p>
<p>&nbsp;</p>
<p>与非泛型代码相互操作</p>
<p>数百万行现有代码使用已经泛型化的 Java 类库中的类，比如集合框架、Class 和 ThreadLocal。JDK 5.0 中的改进不要破坏所有这些代码是很重要的，所以编译器允许您在不指定其类型参数的情况下使用泛型类。</p>
<p>当然，以&#8220;旧方式&#8221;做事没有新方式安全，因为忽略了编译器准备提供的类型安全。如果您试图将 List&lt;String&gt; 传递给一个接受 List 的方法，它将能够工作，但是编译器将会发出一个可能丧失类型安全的警告，即所谓的&#8220;unchecked conversion（不检查转换）&#8221;警告。 </p>
<p>没有类型参数的泛型，比如声明为 List 类型而不是 List&lt;Something&gt; 类型的变量，叫做原始类型。原始类型与参数化类型的任何实例化是赋值兼容的，但是这样的赋值会生成 unchecked-conversion 警告。</p>
<p>为了消除一些 unchecked-conversion 警告，假设您不准备泛型化所有的代码，您可以使用通配符类型参数。使用 List&lt;?&gt; 而不使用 List。List 是原始类型；List&lt;?&gt; 是具有未知类型参数的泛型。编译器将以不同的方式对待它们，并很可能发出更少的警告。 </p>
<p>无论在哪种情况下，编译器在生成字节码时都会生成强制类型转换，所以生成的字节码在每种情况下都不会比没有泛型时更不安全。如果您设法通过使用原始类型或类文件来破坏类型安全，就会得到与不使用泛型时得到的相同的 ClassCastException 或 ArrayStoreException。 </p>
<p><br>已检查集合</p>
<p>作为从原始集合类型迁移到泛型集合类型的帮助，集合框架添加了一些新的集合包装器，以便为一些类型安全 bug 提供早期警告。就像 Collections.unmodifiableSet() 工厂方法用一个不允许任何修改的 Set 包装一个现有 Set 一样，Collections.checkedSet()（以及 checkedList() 和 checkedMap()）工厂方法创建一个包装器（或者视图）类，以防止您将错误类型的变量放在集合中。</p>
<p>checkedXxx() 方法都接受一个类常量作为参数，所以它们可以（在运行时）检查这些修改是允许的。典型的实现可能像下面这样： </p>
<p><br>public class Collections {&nbsp; <br>&nbsp; public static &lt;E&gt; Collection&lt;E&gt; checkedCollection(Collection&lt;E&gt; c, Class&lt;E&gt; type ) { <br>&nbsp;&nbsp;&nbsp; return new CheckedCollection&lt;E&gt;(c, type); <br>&nbsp; } </p>
<p>&nbsp; private static class CheckedCollection&lt;E&gt; implements Collection&lt;E&gt; { <br>&nbsp;&nbsp;&nbsp; private final Collection&lt;E&gt; c; <br>&nbsp;&nbsp;&nbsp; private final Class&lt;E&gt; type; </p>
<p>&nbsp;&nbsp;&nbsp; CheckedCollection(Collection&lt;E&gt; c, Class&lt;E&gt; type) { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.c = c; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.type = type; <br>&nbsp;&nbsp;&nbsp; } </p>
<p>&nbsp;&nbsp;&nbsp; public boolean add(E o) { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!type.isInstance(o)) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new ClassCastException(); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return c.add(o); <br>&nbsp;&nbsp;&nbsp; } <br>&nbsp; } <br>}&nbsp; </p>
<p>&nbsp;</p>
<p><strong>五 底层细节</strong></p>
<p><br>擦除</p>
<p>也许泛型最具挑战性的方面是擦除（erasure），这是 Java 语言中泛型实现的底层技术。擦除意味着编译器在生成类文件时基本上会抛开参数化类的大量类型信息。编译器用它的强制类型转换生成代码，就像程序员在泛型出现之前手工所做的一样。区别在于，编译器开始已经验证了大量如果没有泛型就不会验证的类型安全约束。</p>
<p>通过擦除实现泛型的含意是很重要的，并且初看也是混乱的。尽管不能将 List&lt;Integer&gt; 赋给 List&lt;Number&gt;，因为它们是不同的类型，但是 List&lt;Integer&gt; 和 List&lt;Number&gt; 类型的变量是相同的类！要明白这一点，请评价下面的代码：</p>
<p>new List&lt;Number&gt;().getClass() == new List&lt;Integer&gt;().getClass()</p>
<p>编译器只为 List 生成一个类。当生成了 List 的字节码时，将很少剩下其类型参数的的跟踪。</p>
<p>当生成泛型类的字节码时，编译器用类型参数的擦除 替换类型参数。对于无限制类型参数（&lt;V&gt;），它的擦除是 Object。对于上限类型参数（&lt;K extends Comparable&lt;K&gt;&gt;），它的擦除是其上限（在本例中是 Comparable）的擦除。对于具有多个限制的类型参数，使用其最左限制的擦除。 </p>
<p>如果检查生成的字节码，您无法说出 List&lt;Integer&gt; 和 List&lt;String&gt; 的代码之间的区别。类型限制 T 在字节码中被 T 的上限所取代，该上限一般是 Object。</p>
<p>&nbsp;</p>
<p>多重限制</p>
<p>一个类型参数可以具有多个限制。当您想要约束一个类型参数比如说同时为 Comparable 和 Serializable 时，这将很有用。多重限制的语法是用&#8220;与&#8221;符号分隔限制： </p>
<p><br>class C&lt;T extends Comparable&lt;? super T&gt; &amp; Serializable&gt; </p>
<p>通配符类型可以具有单个限制 —— 上限或者下限。一个指定的类型参数可以具有一个或多个上限。具有多重限制的类型参数可以用于访问它的每个限制的方法和域。 </p>
<p><br>类型形参和类型实参 </p>
<p>在参数化类的定义中，占位符名称（比如 Collection&lt;V&gt; 中的 V）叫做类型形参（type parameter），它们类似于方法定义中的形式参数。在参数化类的变量的声明中，声明中指定的类型值叫做类型实参（type argument），它们类似于方法调用中的实际参数。但是实际中二者一般都通称为&#8220;类型参数&#8221;。所以给出定义： </p>
<p>interface Collection&lt;V&gt; { ... }</p>
<p>和声明： </p>
<p>Collection&lt;String&gt; cs = new HashSet&lt;String&gt;();</p>
<p>那么，名称 V（它可用于整个 Collection 接口体内）叫做一个类型形参。在 cs 的声明中，String 的两次使用都是类型实参（一次用于 Collection&lt;V&gt;，另一次用于 HashSet&lt;V&gt;）。</p>
<p>关于何时可以使用类型形参，存在一些限制。大多数时候，可以在能够使用实际类型定义的任何地方使用类型形参。但是有例外情况。不能使用它们创建对象或数组，并且不能将它们用于静态上下文中或者处理异常的上下文中。还不能将它们用作父类型（class Foo&lt;T&gt; extends T），不能用于 instanceof 表达式中，不能用作类常量。 </p>
<p>类似地，关于可以使用哪些类型作为类型实参，也存在一些限制。类型实参必须是引用类型（不是基本类型）、通配符、类型参数，或者其他参数化类型的实例化。所以您可以定义 List&lt;String&gt;（引用类型）、List&lt;?&gt;（通配符）或者 List&lt;List&lt;?&gt;&gt;（其他参数化类型的实例化）。在带有类型形参 T 的参数化类型的定义中，您也可以声明 List&lt;T&gt;（类型形参）。</p>
<p>&nbsp;</p>
<p><strong>六 结束语</strong> </p>
<p>泛型的引入是对 Java 语言和 Java 类库的一个主要改变。泛型可以提高 Java 应用程序的类型安全、可维护性和可靠性，代价是一些附加的复杂性。</p>
<p>已经做了非常小心的处理，以确保现有的类将继续与 JDK 5.0 中的泛型化类库一起工作，所以您可以根据自己的意愿选择从什么时候开始使用泛型。</p>
<p><br>&nbsp;</p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/141452.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-08-30 17:25 <a href="http://www.blogjava.net/orangelizq/archive/2007/08/30/141452.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]世界各地程序开发高手谈Java</title><link>http://www.blogjava.net/orangelizq/archive/2007/08/28/140661.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Tue, 28 Aug 2007 13:17:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/08/28/140661.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/140661.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/08/28/140661.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/140661.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/140661.html</trackback:ping><description><![CDATA[<p><strong>一、简介</strong> </p>
<p>2005年5月标记着自从Sun第一个引入Java技术以来经历了整整10个年头。在过去的10年中，Java语言已经变为一个平台，一个社团和一个生态系统。在这些环境下，软件用户、开源工程以及个体开发者等茁壮成长。今天，大约有四百五十万Java开发者和大约1.4亿台设备上使用着Java。 </p>
<p>我们不空谈Java的成功故事。代替的是通过分发调查问卷（每发展一年提问一个问题）来搜集个人的Java使用经验，这些人大都是精于Java技术的开发者。回答者包括各个层面的人，从咨询师、作家到BEA、IBM、Sun等大公司的CTO和资深技术人员。 </p>
<p>下面是一些我们收到的令人瞠目的回答。 </p>
<p>1. 你是如何开始使用Java编程的？ </p>
<p>Java吸引了每个人的注意，从程序开发人员到&#8230;&#8230; </p>
<p>当Java还称为Oak的时候我就用它进行编程，大约是在1993年的下半年或者是1994年的上半年。当时我在Sun Labs率领一个研究小组。一旦我们使用Java开发，我们就注意到使用Java的最大优点是，它能以一种合理的安全的方式从一个虚拟机迁移到另一台虚拟机。，这是Jim Waldo（SunLabs的杰出工程师）的回答。 </p>
<p>Rod Smith，作为IBM软件开发小组中处理突发技术的副主席，写道：我们看到了，Java平台是一种足够好的技术，它具有在计算机工业界成为一支重大的独成系列的力量的潜力。我们认为，我们最好要赶上这班时代列车并采纳Sun的Java技术而不再依赖于以前的模式-自己开发每一件东西。 </p>
<p>Ed Cobb，BEA Systems公司CTO办公室架构标准的副主席，写道：我们可以选择其它主流的面向对象语言，但是Java提供了一种更好的组合特点使它适合于团队环境下的大规模开发。 </p>
<p>在过去十年中的大部分时间里，我受雇于Sun。因此，我不得不说，Java突然来到我的身边。如果不是这一突然性的到来，我们也会需要另外一种似Java的东西来叩开业界中正迅速发展的网络计算环境的大门。-Rob Gingell，Cassatt Corporation的执行副主席和CTO。 </p>
<p>...对那些勤奋的计算机系的大学生们... </p>
<p>Michael Pilone，作为Blueprint Technologies的资深软件工程师，他的回答是：当时我盘算着我将来得找一份体面的工作，所以虽然我在大学中所学全部是C++，但是我还是另外自学了Java。 </p>
<p>在我上硕士期间，我的指导教授让我用Java工作，结果我用Java 1.0 beta版完成了我所有的功课。-Raghu Donepudi，环球计算机公司的系统开发领导者。 </p>
<p>...对那些热衷于编程的狂热者... </p>
<p>我一毕业即用Java开始工作，我惊喜于Java的WORA的前景和Applets。-Jack Herrington,作为Code Generation in Action (Manning)的作者和Code Generation Network的编辑。 </p>
<p>我甚至在1.0版本之前就开始学习Java了，因为其开发目标是作为微软工具（VB和Visual C++）与Pascal语言的可选替代者，Laurent Ploix写道，他是SunGard-Finance公司的工程总管和技术架构师。 </p>
<p>在1997年，我带着一本手册作为度假读物，在海滩度假的日子里，我沉浸在Java的优美之中。我转向了Java，并宣布C++是一种传统的语言，并发誓一旦选择了Java，永不回头。-Vlad Patryshev,Borland公司Java Business Unit的前任R&amp;D工程师。 </p>
<p>2.Java宣称的编写一次，到处运行效果怎么样（WORA）？一直以来，WORA的重要性改变了多少？ </p>
<p>Java虚拟机，至少在概念上，是Java背后最强有力的思想。它的确实现了它许诺的轻便性。-Bruce Tate，J2Life, LLC（一家Java技术咨询公司）的主席。 </p>
<p>应用服务器和J2EE应用程序可以在多种平台上良好地迁移。我认为在客户端上实现WORA还是相当值得怀疑的，也许永远不会实现。-Michael Pilone </p>
<p>它对我简直是一个不可捉摸的平台。-Vlad Patryshev </p>
<p>Java的早期成功根本上在于WORA。与其它可选择工具相比，Java带给了（并将进一步带给）SI（系统集成商），ISV（独立软件开发商）和软件工程师们一个根本不同的经济环境。-Rod Smith </p>
<p>Java在WORA方面的性能：a)比任何它之前的工具要好；b)就目前而言，与另外一些可选工具相比仍然要好得多；c)将作为Java价值的一个关键部分继续保持下去。-Rob Gingell </p>
<p>WORA每次都带给我极大的便利。我总是在Windows平台上进行我的Java开发；但是，我总是毫不费劲地把这些代码发布到Solaris 或者Linux 环境中去。-Eric Bruno，一个独立的咨询师，擅长于软件架构，企业Java和C++开发。 </p>
<p>你可以把字节代码转换成MSIL（MS中间语言），而且你可以在J#中运行Java程序。这使我们可以针对我们产品的Java和.NET环境只保留一份代码即可。-Michael R. Smialek，Knowledge Dynamics的主席和CEO </p>
<p>我经常跨Windows，Linux和Sun Solaris开发，测试和发布Java代码，而仅需对XML配置文件作较少的改动。但是，随着面向服务的结构的出现，WORA的重要性已经减弱了。-Kyle Gabhart，作家和独立咨询师 </p>
<p>象Perl，Ruby和Python等语言一样轻便。-Jack Herrington </p>
<p>一些人认为，由于通用操作系统的数目的下降，WORA将变得不再如以前那么重要。事实上，即使只有两种合理的可能的目标平台存在，WORA就一直是重要的。-Ed Cobb </p>
<p><strong>二、客户端Java及开发工具选择</strong> </p>
<p>3.你用Java编写过多少代码？估计你将来的工作有多少会用Java实现？ </p>
<p>几乎所的的回答者声称Java是他们主要的产品编码工具，大多数人选择他们的Java使用率超过70%。没有人认为他们下一步的Java开发使用率会降低。 </p>
<p>去年，我们利用Java 技术开发出了800多个商业产品。几乎我们所有的中间件都依赖于Java运行时刻库。-IBM的Rod Smith </p>
<p>在我们的顾客中，我们仍看到具有可以预料的潜在需求的大量C/C++功能第一型的应用程序。他们经常愿意用Java进行开发，而Java虚拟机技术目前正发展到正好能够处理这些类型系统的时候。 </p>
<p>在Web应用程序开发中，我想，当人们的应用程序变得越来越大且越复杂时，我们将看到针对动态类型语言会出现一点后推力作用。他们将经受运行时刻类型异常--事实上，他们早已认识到，如果采用象Java一样的强类型语言的话，这是可以避免的。-BEA公司的Ed Cobb </p>
<p>4.你用Java开发桌面应用和服务器应用的比例为多少？如果你开发过桌面应用程序，你更喜欢用SWT还是Swing，为什么？ </p>
<p>Bruce Tate对这一问题的回答总结了所有其他人的观点：服务器端Java正是它应有的位置。 </p>
<p>至于，客户端Java开发，众说纷纭... </p>
<p>Swing太复杂，太不可预测，太难学。SWT则好一些，但一般而言，Java在用户接口设计上很不成功。-Tate，《Better, Faster, Lighter Java》和《Bitter Java》的作者 </p>
<p>我认为SWT有更好的方法，它链接到本地lib库文件以达到加快速度和一致性的目的，但是我并不喜欢这些API，因为它们暴露出太多的老式的编码技术。相比之下，Swing有一个更好些的API，但是其中充满错误、性能低下且设计糟糕。-Michael Pilone </p>
<p>我更喜欢SWT...它比Swing更具本地化，Swing而可以说只是粗略地实现了本地化。-Ed Cobb </p>
<p>我写Swing应用程序，然后使用Java Web Start来进行发布。我还没有出卖过我的SWT型程序，因为它仅有有限的跨平台支持并缺乏可靠的MVC设计。-Kyle Gabhart </p>
<p>5.你使用的Java开发环境是什么？ </p>
<p>很明显，当前流行的Eclipse框架和集成开发环境是大多数对这一问题的回答，因为其是Windows和Linux平台的主流环境。只有另外少数的回答者指定了其它几个选择： </p>
<p>我选择的平台是WinXP Pro。在安装了Cygwin和另外几个开发工具后，你就可以得到一个具有硬件支持的非常有用的系统了。-Michael Pilone </p>
<p>我特别喜欢J2SE 5。说到IDE，我更喜欢Jbuilder，其次是IDEA。IDEA中有一些巧妙的实现，但是良好的经典的Jbuilder具有我需要的任何东西。-Vlad Patryshev </p>
<p>我一直使用emacs开发而用println进行调试。最近我在使用NetBeans，已惊奇于它给我带来的巨大帮助。-Jim Waldo </p>
<p>请不要使用EJB！-Laurent Ploix </p>
<p>Caf&#233;。-Smialek </p>
<p>在必要的时候，我都使用vi进行开发。-Kyle Gabhart </p>
<p><strong>三、开源，JCP和对Java的希望列举</strong> </p>
<p>6.JCP和开源社群谁在Java更新上的贡献更大？ </p>
<p>到目前为止，应该说是开源的贡献更大。而JCP在进行实际的开发实践之前，推崇标准化的作用。EJB,日志以及持久性一直是JCP中的灾难。实际上JCP在抛弃着Java的根基。很难的问题在拐弯抹角变得易于解决，而容易的问题反而在变得越来越难于解决了。-Bruce Tate </p>
<p>开放源码的执行领导着开发过程，而JCP仅仅是定义了一些标准。-Laurent Ploix </p>
<p>如果说纯粹的革新，我将选择开源。当工程中存在漏洞需要补全时，开源是能够迅速得到响应的。而JCP目前是一种太慢的方式，以至于根本跟不上工业发展的步伐。-Michael Pilone </p>
<p>多数的革新经历了JCP模式。但是，在过去的几年中，我们看到了在开源模式中的活动不断增加的迹象。-Rod Smith (IBM) </p>
<p>为使得开源运动进行下去，JCP值得广泛的信任，它对于Java生态系统的发展起到提供一个群落中心的作用。任何一些非JCP标准的开源工程已经探索了各式各样的思想-一些是糟糕的，一些却取得了令人惊喜的成绩。JCP可以说是一场伟大的创新运动的火车头。-Ed Cobb (BEA) </p>
<p>JCP本身仅仅是定义了一些标准及相应的说明书，这在已有的工程实现中被得到支持。作为编程者，我们不想根据一段现成的Java编码来指导我们编程，而要根据一套成熟的说明书进行开发。真正有用的编程最开始往往是零碎的代码片断，经常经历一个先有代码然后有规范的说明书的过程。-Rob Gingell </p>
<p>我找到了开源的主动性，特别是从Apache到当前最具创新性的有用的软件中找到的。-Eric Bruno </p>
<p>7.Sun应该开放Java的源代码吗？ </p>
<p>你知道这个问题肯定会出现在问卷之中的。 </p>
<p>这一点并不重要。Java在走自己路的过程中，自身已经建设得足够强大。-Bruce Tate </p>
<p>如果Sun在做这样一件有益的工作，为什么要打扰他呢？-Raghu Donepudi </p>
<p>不应该。如果它实现开源，那么我们能够看到Java的许多技巧，这最终将导致一些问题的出现，例如我们现在使用应用程序服务器时所遇到的问题。-Rahul Kumar Gupta </p>
<p>是的。Sun拒绝这样做的唯一原因就是，Java完全是他们自己的产品，他们使用它来使自己的公司得以维持下去。-Jack Herrington </p>
<p>一方面，开源的思想是很有吸引力的，因为它将导致更多的错误在很短的时间内就得以修改。另一方面，它可能导致分支分派的不匹配的JAVA虚拟机的出现。-Michael Pilone </p>
<p>不。我不相信一群普普通通的所谓天才人物就能取代那些负责管理极为健全的基本概念的大家们的科学思想-其中的大多数人甚至还不能明白这个问题。-Vlad Patryshev </p>
<p>开源社团的加入将会加速创新并推动该平台的竞争性。-Rod Smith(IBM) </p>
<p>我们需要一个开源Java的主要原因是确保该平台的生命力。如果将来Sun公司发生什么事情的话，开源的Java将列入我们的保险计划中。-Ed Cobb(BEA) </p>
<p>我认为是应该的。&#8216;开源&#8217;Java并不要求Sun做什么事情，它只是要求另外一些公司或个人做一些事情。&#8216;开源&#8217;Java是不可避免的。我建议Sun积极地实现这一不可避免并为此带来的益处做一些工作。-Rob Gingell </p>
<p>我对此并不关心-Kyle Gabhart </p>
<p>8.你希望Java有怎样的改进？ </p>
<p>从较低层次上，Java需要代码的模块化，扩展和一个更具动态化的模型，以及还需要增加很多的功能以使得应用程序开发更为容易，因为你不可能仅仅利用库来实现一切。-Bruce Tate </p>
<p>内省机制（用一个类来分析JavaBean的特性）使用起来太难且过于繁重。-Laurent Ploix </p>
<p>需要加入代码使用许可证机制。-Greg Magnusson，Cyborg Spiders的Web技术开发的奠基者 </p>
<p>应该提供由开发者来进行内存管理的功能。-Raghu Donepudi </p>
<p>应该加入运算符重载功能。-Jack Herrington </p>
<p>目前迫切需要在Java中加入Jar版本机制。我记不清有多少次遇到XML分析库冲突或者日志库冲突了。-Michael Pilone </p>
<p>类对象。十年了，没有任何改变。有大量的类对象需要加上去。-Vlad Patryshev </p>
<p>Java平台目前已经变得过于复杂。我们认为Java社团需要做出更好的工作来满足独立的和中小型的商业需要，这也是为了Java继续发展、繁荣和成功的目的。-Rod Smith（IBM） </p>
<p>Java非常需要一个更为强壮的模块化的系统。当前，我们所拥有的是一些.jar文件，其结果是成了&#8216;.jar文件地狱&#8217;。但是今天还很难描述一个互有联系的模块化的系统。-Ed Cobb </p>
<p>我原先希望Java能够实现的，过去的Java并没有做到：关于日期和时间问题上有好几处从一开始就是错误的；RMI/IIOP的引入在理论上是正确的但完全没有必要；它自一开始就是自我封闭的。-Rob Gingell </p>
<p>对于类的加载和对象的Java运行时刻类型的关系的处理是一个错误，现在我们还在为之付出代价。你不可能真正确定出是否你的程序在编译时刻是类型安全的。而且，如果你在做适当动态的任何操作，你经常需要对一个给定类的正确加载作出猜测分析。-Jim Waldo </p>
<p>垃圾回收简直是个噩梦。它有可能使得没有经过良好训练的，懒散的编程人员进入到这个工业领域中。 </p>
<p>Java需要增加的另外一些功能有：操作符重载；预编译指令（#define，等等）；把声明与定义（头文件和源文件）相分离的能力；唯一的、非本机的机器标志符（用于认证之目的）。-Michael Smialek </p>
<p>迫切需要加入Code-Behind（页面代码分离）技术！！！ASP.NET和页面代码分离技术所带来的重用性与灵活性是巨大的。我希望JSP 3.0能够朝这个方向发展。-Kyle Gabhart </p>
<p>我更愿意使用Java对象来存取操作系统而不是用JNI（Java本机接口），因为大多数的Win32/Linux API都包含在Java中了。-Alexi Jordanov，OSGi技术公司ProSyst Bulgaria的项目领导者 </p>
<p><strong>四、Java带给人们的最伟大的礼物与Java的未来</strong> </p>
<p>9.Java对软件社群最大的贡献是什么？ </p>
<p>我们收到的大部分反映结果认为是平台独立性，Java平台和该语言本身的创建以及他们创建的各种社团。 </p>
<p>有两点：它使得更广泛的用户群可以接受垃圾收集语言；围绕该语言涌现出了各种集成开发环境。-Jack Herrington </p>
<p>Java真正地震撼了许多东西，这包括迫使微软设计出新的产品，诸如Visual Studio的进一步改进和.NET产品的出现等。由于Netscape选择了Java,这使得客户浏览器能够执行JavaScript。它使当今世界服务器端开发也迈出了巨大的一步，受此技术影响的站点数以百万计。-Michael Pilone </p>
<p>数百万的学生不需要学习C++。-Rob Gingell </p>
<p>能使多家厂商贯彻实施的标准化API的建立。很多厂家竞相提供该标准化接口的最好实现，这给业界创造高质量的解决方案带来了强大发展动力。-Kyle Gabhart </p>
<p>.NET-Michael Smialek </p>
<p>10.Java的未来会如何？ </p>
<p>任何一种语言总是存在其有限的技术领先周期，Java也不例外。在某种意义上看，能超过Java技术会是一种非常有趣的事情。-Bruce Tate </p>
<p>当桌面和膝上电脑要被手持设备取代时，我们可能需要一种更简单的更强有力的语言。-Raghu Donepudi </p>
<p>C#具有挤垮Java的潜力，它是一种更好的语言。-Jack Herrington </p>
<p>微软在通过.NET以一种令人难以置信的速度发展。由于拥有Windows操作系统的大量客户端用户，所以他们比Java具有更大的优势。-Michael Pilone </p>
<p>在一些新技术被编程世界接受之前，至少要经过几年的时间。例如，看起来不超过5%的Java程序员能掌握Java泛型编程。请问，又有多少人可以掌握Lock/Condition？-Vladimir Patryshev </p>
<p>当技术朝着围绕交互性的结构化（如面向服务的结构化）方向发展的时候，Java语言将继续作为一种语言在计算机工业发展中占有重要地位。-Rod Smith </p>
<p>在某种意义上看，Java 虚拟机体系所要求的限制可能有点太严肃了。但是，任何一种新的挑战者虚拟机登上舞台并用之取代Java平台都是一件很好的事情。-Ed Cobb </p>
<p>实际上我们可以开发出许多种不同的语言，其中一些可能与我们熟知的Java根本不同，但是仍旧保持应用程序二进制接口（ABI）。这是为什么Java将会保持长时期的重要性的原因。-Rob Gingell </p>
<p>Java将继续作为一股巨大的力量存在于IT业中，但是一些瞄准市场的脚本语言将会在某些行业中进一步发展壮大。-Doug Tillman，Grainger.com站点Java和Python技术的开发者 </p>
<p>任何一个人，只要他在该界业干过多年，都不会相信，存在一种技术会对要推翻自己的更新、更快或者更有效的技术产生免疫力。-Kyle Gabhart </p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/140661.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-08-28 21:17 <a href="http://www.blogjava.net/orangelizq/archive/2007/08/28/140661.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]Java Web层的下一个王者是谁？</title><link>http://www.blogjava.net/orangelizq/archive/2007/08/28/140329.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Tue, 28 Aug 2007 02:04:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/08/28/140329.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/140329.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/08/28/140329.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/140329.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/140329.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 经过数年的&#8220;框架大战&#8221;，Java</a>界的各种框架找到了自己应有的位置。
<p>　　Spring+Hibernate</a>+Struts已成为Java开发的主流体系。在这个体系中，Spring+Hibernate的地位应该说短期内是难以撼动了。除了新兴的Jboss Seam作为挑战者之外，几乎难有劲敌。有趣的是当初Spring、Hibernate作为挑战者，将官方的EJB</a>成功挑落马下;这次反倒是官方的EBJ3成了挑战者，不知结局如何。</p>
<p>　　Java B/S编程中历来战火最激烈的其实还在Web</a>层，框架的数量最多，争议最大。</p>
<p>　　一切由Struts而起，而Struts最终也坐稳了第一个时代的王座。在技术层面，Struts 1.x已经被无数人抱怨过、批评过，但终于还是稳坐王位，这充分说明了习惯的力量。&#8220;稳定压倒一切&#8221;，这句话在IT技术领域仍旧适用。</p>
<p>　　其实IT应用技术，什么新鲜玩意并不难学。难的是标准化和规范化。每个程序员都有自己的思路和习惯，写出来的代码自然是五花八门。Java何以成为编程界的老大，很重要的一点在于Java的规范化。这种规范化很高的语言适用于多人合作的大型项目，便于沟通和理解，也就便于集成和维护。Java世界为什么会框架横飞，说到底还是规范化的需要。纯JSP</a>和Struts写Web谁快，摆明了是JSP。那撑饱了用Struts?原因在于100个人写出来的JSP，有100种写法;而100个人写出来的Struts，基本相似。Struts之成功，正缘于其在Java Web层的规范化方面所做出的贡献。</p>
<p>　　然而长江后浪推前浪，Struts 1.x的技术缺陷毕竟是隐患。</p>
<p>　　Sun力推JSF，打算一雪Web层框架缺失之耻。可惜JSF既要沿用Swing</a>的技术路线，又要学ASP</a>.net</a>，还要照顾产商的IDE，结果搞了个四不象，弄得里外不是人。当然Sun的技术实力毕竟是超强的，只要别重蹈EJB的覆辙，拿出点专断的精神(像这两年的NetBeans)，做出像Swing那样水准的东西，JSF当大有作为。JSF现在比较有优势的是对Ajax的集成，这一点走在了其他框架的前面。</p>
<p>　　而Struts就更没有志气了，把WebWork换了个标签，凑出个Struts2，Bug多多。说实在话，根本不如原版的WebWork。如果不是靠了原先的fans捧场，根本就没得混。不过Struts原本就不是以技术取胜的，靠的是抢占先机带来的习惯优势。如果原先的fans们在这两年内都能转到Struts2，那么Struts二世仍将雄霸天下。</p>
<p>　　综上所述，未来两年，JSF与Struts将展开Java Web框架的最终战争。</p>
<p>　　以笔者愚见，结局有二：一是不论Struts还是JSF获胜，Java Web层都将结束混战的局面，这对Java Web开发的标准化是非常有利的，并有助于巩固Java在B/S界的地位;二是Struts1.x、Struts2、JSF三分天下，必然从整体上削弱Java在B/S界的竞争力，并将进一步被RoR、ASP.NET</a>、PHP</a>所蚕食。<br></p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/140329.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-08-28 10:04 <a href="http://www.blogjava.net/orangelizq/archive/2007/08/28/140329.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] 谈谈Unicode编码，简要解释UCS、UTF、BMP、BOM等名词</title><link>http://www.blogjava.net/orangelizq/archive/2007/08/02/134021.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Thu, 02 Aug 2007 08:54:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/08/02/134021.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/134021.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/08/02/134021.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/134021.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/134021.html</trackback:ping><description><![CDATA[<h2><span style="FONT-SIZE: 12pt"><font face=#ce_temp_font#>
<h2></font>&nbsp;</span>谈谈Unicode编码，简要解释UCS、UTF、BMP、BOM等名词</h2>
</h2>
<p>这是一篇程序员写给程序员的趣味读物。所谓趣味是指可以比较轻松地了解一些原来不清楚的概念，增进知识，类似于打RPG游戏的升级。整理这篇文章的动机是两个问题：</p>
<dl>
<dt>问题一：
<dd>
<p>使用Windows记事本的&#8220;另存为&#8221;，可以在GBK、Unicode、Unicode big endian和UTF-8这几种编码方式间相互转换。同样是txt文件，Windows是怎样识别编码方式的呢？</p>
<p>我很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节，分别是FF、FE（Unicode）,FE、FF（Unicode big endian）,EF、BB、BF（UTF-8）。但这些标记是基于什么标准呢？</p>
<dt>问题二：
<dd>最近在网上看到一个ConvertUTF.c，实现了UTF-32、UTF-16和UTF-8这三种编码方式的相互转换。对于Unicode(UCS2)、GBK、UTF-8这些编码方式，我原来就了解。但这个程序让我有些糊涂，想不起来UTF-16和UCS2有什么关系。 </dd></dl>
<p>查了查相关资料，总算将这些问题弄清楚了，顺带也了解了一些Unicode的细节。写成一篇文章，送给有过类似疑问的朋友。本文在写作时尽量做到通俗易懂，但要求读者知道什么是字节，什么是十六进制。</p>
<h3>0、big endian和little endian</h3>
<p>big endian和little endian是CPU处理多字节数的不同方式。例如&#8220;汉&#8221;字的Unicode编码是6C49。那么写到文件里时，究竟是将6C写在前面，还是将49写在前面？如果将6C写在前面，就是big endian。如果将49写在前面，就是little endian。</p>
<p>&#8220;endian&#8221;这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开，由此曾发生过六次叛乱，一个皇帝送了命，另一个丢了王位。</p>
<p>我们一般将endian翻译成&#8220;字节序&#8221;，将big endian和little endian称作&#8220;大尾&#8221;和&#8220;小尾&#8221;。</p>
<h3>1、字符编码、内码，顺带介绍汉字编码</h3>
<p>字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码，为了处理汉字，程序员设计了用于简体中文的GB2312和用于繁体中文的big5。</p>
<p>GB2312(1980年)一共收录了7445个字符，包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7，低字节从A1-FE，占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。</p>
<p>GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号，它分为汉字区和图形符号区。汉字区包括21003个字符。</p>
<p>从ASCII、GB2312到GBK，这些编码方法是向下兼容的，即同一个字符在这些方案中总是有相同的编码，后面的标准支持更多的字符。在这些编码中，英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼，GB2312、GBK都属于双字节字符集 (DBCS)。</p>
<p>2000年的GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字，同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。从汉字字汇上说，GB18030在GB13000.1的20902个汉字的基础上增加了CJK扩展A的6582个汉字（Unicode码0x3400-0x4db5），一共收录了27484个汉字。</p>
<p>CJK就是中日韩的意思。Unicode为了节省码位，将中日韩三国语言中的文字统一编码。GB13000.1就是ISO/IEC 10646-1的中文版，相当于Unicode 1.1。</p>
<p>GB18030的编码采用单字节、双字节和4字节方案。其中单字节、双字节和GBK是完全兼容的。4字节编码的码位就是收录了CJK扩展A的6582个汉字。 例如：UCS的0x3400在GB18030中的编码应该是8139EF30，UCS的0x3401在GB18030中的编码应该是8139EF31。</p>
<p>微软提供了GB18030的升级包，但这个升级包只是提供了一套支持CJK扩展A的6582个汉字的新字体：新宋体-18030，并不改变内码。Windows 的内码仍然是GBK。</p>
<p>这里还有一些细节：</p>
<ul>
    <li>
    <p>GB2312的原文还是区位码，从区位码到内码，需要在高字节和低字节上分别加上A0。</p>
    <li>
    <p>对于任何字符编码，编码单元的顺序是由编码方案指定的，与endian无关。例如GBK的编码单元是字节，用两个字节表示一个汉字。 这两个字节的顺序是固定的，不受CPU字节序的影响。UTF-16的编码单元是word（双字节），word之间的顺序是编码方案指定的，word内部的字节排列才会受到endian的影响。后面还会介绍UTF-16。</p>
    <li>
    <p>GB2312的两个字节的最高位都是1。但符合这个条件的码位只有128*128=16384个。所以GBK和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析：在读取DBCS字符流时，只要遇到高位为1的字节，就可以将下两个字节作为一个双字节编码，而不用管低字节的高位是什么。</p>
    </li>
</ul>
<h3>2、Unicode、UCS和UTF</h3>
<p>前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容（更准确地说，是与ISO-8859-1兼容），与GB码不兼容。例如&#8220;汉&#8221;字的Unicode编码是6C49，而GB码是BABA。</p>
<p>Unicode也是一种字符编码方法，不过它是由国际组织设计，可以容纳全世界所有语言文字的编码方案。Unicode的学名是"Universal Multiple-Octet Coded Character Set"，简称为UCS。UCS可以看作是"Unicode Character Set"的缩写。</p>
<p>根据维基百科全书(http://zh.wikipedia.org/wiki/)的记载：历史上存在两个试图独立设计Unicode的组织，即国际标准化组织（ISO）和一个软件制造商的协会（unicode.org）。ISO开发了ISO 10646项目，Unicode协会开发了Unicode项目。</p>
<p>在1991年前后，双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果，并为创立一个单一编码表而协同工作。从Unicode2.0开始，Unicode项目采用了与ISO 10646-1相同的字库和字码。</p>
<p>目前两个项目仍都存在，并独立地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0。ISO的最新标准是ISO 10646-3:2003。</p>
<p>UCS只是规定如何编码，并没有规定如何传输、保存这个编码。例如&#8220;汉&#8221;字的UCS编码是6C49，我可以用4个ascii数字来传输、保存这个编码；也可以用utf-8编码:3个连续的字节E6 B1 89来表示它。关键在于通信双方都要认可。UTF-8、UTF-7、UTF-16都是被广泛接受的方案。UTF-8的一个特别的好处是它与ISO-8859-1完全兼容。UTF是&#8220;UCS Transformation Format&#8221;的缩写。</p>
<p>IETF的RFC2781和RFC3629以RFC的一贯风格，清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记不得IETF是Internet Engineering Task Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。</p>
<h4>2.1、内码和code page</h4>
<p>目前Windows的内核已经支持Unicode字符集，这样在内核上可以支持全世界所有的语言文字。但是由于现有的大量程序和文档都采用了某种特定语言的编码，例如GBK，Windows不可能不支持现有的编码，而全部改用Unicode。</p>
<p>Windows使用代码页(code page)来适应各个国家和地区。code page可以被理解为前面提到的内码。GBK对应的code page是CP936。</p>
<p>微软也为GB18030定义了code page：CP54936。但是由于GB18030有一部分4字节编码，而Windows的代码页只支持单字节和双字节编码，所以这个code page是无法真正使用的。</p>
<h3>3、UCS-2、UCS-4、BMP</h3>
<p>UCS有两种格式：UCS-2和UCS-4。顾名思义，UCS-2就是用两个字节编码，UCS-4就是用4个字节（实际上只用了31位，最高位必须为0）编码。下面让我们做一些简单的数学游戏：</p>
<p>UCS-2有2^16=65536个码位，UCS-4有2^31=2147483648个码位。</p>
<p>UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个plane。每个plane根据第3个字节分为256行 (rows)，每行包含256个cells。当然同一行的cells只是最后一个字节不同，其余都相同。</p>
<p>group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中，高两个字节为0的码位被称作BMP。</p>
<p>将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节，就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。</p>
<h3>4、UTF编码</h3>
<p>&nbsp;</p>
<p>UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下：</p>
<table width="75%" border=1>
    <tbody>
        <tr>
            <td>UCS-2编码(16进制)</td>
            <td>UTF-8 字节流(二进制)</td>
        </tr>
        <tr>
            <td>0000 - 007F</td>
            <td>0xxxxxxx</td>
        </tr>
        <tr>
            <td>0080 - 07FF</td>
            <td>110xxxxx 10xxxxxx</td>
        </tr>
        <tr>
            <td>0800 - FFFF</td>
            <td>1110xxxx 10xxxxxx 10xxxxxx</td>
        </tr>
    </tbody>
</table>
<p>例如&#8220;汉&#8221;字的Unicode编码是6C49。6C49在0800-FFFF之间，所以肯定要用3字节模板了：1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是：0110 110001 001001， 用这个比特流依次代替模板中的x，得到：11100110 10110001 10001001，即E6 B1 89。</p>
<p>读者可以用记事本测试一下我们的编码是否正确。需要注意，UltraEdit在打开utf-8编码的文本文件时会自动转换为UTF-16，可能产生混淆。你可以在设置中关掉这个选项。更好的工具是Hex Workshop。</p>
<p>UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码，UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码，定义了一个算法。不过由于实际使用的UCS2，或者UCS4的BMP必然小于0x10000，所以就目前而言，可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案，UTF-16却要用于实际的传输，所以就不得不考虑字节序的问题。</p>
<h3>5、UTF的字节序和BOM</h3>
<p>UTF-8以字节为编码单元，没有字节序的问题。UTF-16以两个字节为编码单元，在解释一个UTF-16文本前，首先要弄清楚每个编码单元的字节序。例如&#8220;奎&#8221;的Unicode编码是594E，&#8220;乙&#8221;的Unicode编码是4E59。如果我们收到UTF-16字节流&#8220;594E&#8221;，那么这是&#8220;奎&#8221;还是&#8220;乙&#8221;？</p>
<p>Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是&#8220;Bill Of Material&#8221;的BOM表，而是Byte Order Mark。BOM是一个有点小聪明的想法：</p>
<p>在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符，它的编码是FEFF。而FFFE在UCS中是不存在的字符，所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前，先传输字符"ZERO WIDTH NO-BREAK SPACE"。</p>
<p>这样如果接收者收到FEFF，就表明这个字节流是Big-Endian的；如果收到FFFE，就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。</p>
<p>UTF-8不需要BOM来表明字节顺序，但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF（读者可以用我们前面介绍的编码方法验证一下）。所以如果接收者收到以EF BB BF开头的字节流，就知道这是UTF-8编码了。</p>
<p>Windows就是使用BOM来标记文本文件的编码方式的。</p>
<h3>6、进一步的参考资料</h3>
<p>本文主要参考的资料是 "Short overview of ISO-IEC 10646 and Unicode" (http://www.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html)。</p>
<p>我还找了两篇看上去不错的资料，不过因为我开始的疑问都找到了答案，所以就没有看：</p>
<ol>
    <li>"Understanding Unicode A general introduction to the Unicode Standard" (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-Chapter04a)
    <li>"Character set encoding basics Understanding character set encodings and legacy encodings" (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-Chapter03) </li>
</ol>
<p>我写过UTF-8、UCS-2、GBK相互转换的软件包，包括使用Windows API和不使用Windows API的版本。以后有时间的话，我会整理一下放到我的个人主页上(http://fmddlmyy.home4u.china.com)。</p>
<p>我是想清楚所有问题后才开始写这篇文章的，原以为一会儿就能写好。没想到考虑措辞和查证细节花费了很长时间，竟然从下午1:30写到9:00。希望有读者能从中受益。</p>
<h3>附录1 再说说区位码、GB2312、内码和代码页</h3>
<p>有的朋友对文章中这句话还有疑问：<br>&#8220;GB2312的原文还是区位码，从区位码到内码，需要在高字节和低字节上分别加上A0。&#8221;</p>
<p>我再详细解释一下：</p>
<p>&#8220;GB2312的原文&#8221;是指国家1980年的一个标准《中华人民共和国国家标准 信息交换用汉字编码字符集 基本集 GB 2312-80》。这个标准用两个数来编码汉字和中文符号。第一个数称为&#8220;区&#8221;，第二个数称为&#8220;位&#8221;。所以也称为区位码。1-9区是中文符号，16-55区是一级汉字，56-87区是二级汉字。现在Windows也还有区位输入法，例如输入1601得到&#8220;啊&#8221;。（这个区位输入法可以自动识别16进制的GB2312和10进制的区位码，也就是说输入B0A1同样会得到&#8220;啊&#8221;。）</p>
<p>内码是指操作系统内部的字符编码。早期操作系统的内码是与语言相关的。现在的Windows在系统内部支持Unicode，然后用代码页适应各种语言，&#8220;内码&#8221;的概念就比较模糊了。微软一般将缺省代码页指定的编码说成是内码。</p>
<p>内码这个词汇，并没有什么官方的定义，代码页也只是微软这个公司的叫法。作为程序员，我们只要知道它们是什么东西，没有必要过多地考证这些名词。</p>
<p>Windows中有缺省代码页的概念，即缺省用什么编码来解释字符。例如Windows的记事本打开了一个文本文件，里面的内容是字节流：BA、BA、D7、D6。Windows应该去怎么解释它呢？</p>
<p>是按照Unicode编码解释、还是按照GBK解释、还是按照BIG5解释，还是按照ISO8859-1去解释？如果按GBK去解释，就会得到&#8220;汉字&#8221;两个字。按照其它编码解释，可能找不到对应的字符，也可能找到错误的字符。所谓&#8220;错误&#8221;是指与文本作者的本意不符，这时就产生了乱码。</p>
<p>答案是Windows按照当前的缺省代码页去解释文本文件里的字节流。缺省代码页可以通过控制面板的区域选项设置。记事本的另存为中有一项ANSI，其实就是按照缺省代码页的编码方法保存。</p>
<p>Windows的内码是Unicode，它在技术上可以同时支持多个代码页。只要文件能说明自己使用什么编码，用户又安装了对应的代码页，Windows就能正确显示，例如在HTML文件中就可以指定charset。</p>
<p>有的HTML文件作者，特别是英文作者，认为世界上所有人都使用英文，在文件中不指定charset。如果他使用了0x80-0xff之间的字符，中文Windows又按照缺省的GBK去解释，就会出现乱码。这时只要在这个html文件中加上指定charset的语句，例如：<br>&lt;meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1"&gt;<br>如果原作者使用的代码页和ISO8859-1兼容，就不会出现乱码了。</p>
<p>再说区位码，啊的区位码是1601，写成16进制是0x10,0x01。这和计算机广泛使用的ASCII编码冲突。为了兼容00-7f的ASCII编码，我们在区位码的高、低字节上分别加上A0。这样&#8220;啊&#8221;的编码就成为B0A1。我们将加过两个A0的编码也称为GB2312编码，虽然GB2312的原文根本没提到这一点。 <br><br>文章二，本文转载自:http://www.donews.net/holen/archive/2004/11/30/188182.aspx<br></p>
<p>Unicode: <br><br>unicode.org制定的编码机制, 要将全世界常用文字都函括进去.<br>在1.0中是16位编码, 由U+0000到U+FFFF. 每个2byte码对应一个字符; 在2.0开始抛弃了16位限制, 原来的16位作为基本位平面, 另外增加了16个位平面, 相当于20位编码, 编码范围0到0x10FFFF.<br><br>UCS: <br><br>ISO制定的ISO10646标准所定义的 Universal Character Set, 采用4byte编码.<br><br>Unicode与UCS的关系:<br><br>ISO与unicode.org是两个不同的组织, 因此最初制定了不同的标准; 但自从unicode2.0开始, unicode采用了与ISO 10646-1相同的字库和字码, ISO也承诺ISO10646将不会给超出0x10FFFF的UCS-4编码赋值, 使得两者保持一致.<br><br>UCS的编码方式:<br><br></p>
<li>UCS-2, 与unicode的2byte编码基本一样. <br>
<li>UCS-4, 4byte编码, 目前是在UCS-2前加上2个全零的byte.<br><br>UTF: Unicode/UCS Transformation Format<br>---------------------------------------原文------------------------------&nbsp;
<li>UTF-8, 8bit编码, ASCII不作变换, 其他字符做变长编码, 每个字符1-3 byte. 通常作为外码. 有以下优点:<br>* 与CPU字节顺序无关, 可以在不同平台之间交流<br>* 容错能力高, 任何一个字节损坏后, 最多只会导致一个编码码位损失, 不会链锁错误(如GB码错一个字节就会整行乱码) <br>
<li>UTF-16, 16bit编码, 是变长码, 大致相当于20位编码, 值在0到0x10FFFF之间, 基本上就是unicode编码的实现. 它是变长码, 与CPU字序有关, 但因为最省空间, 常作为网络传输的外码.
<p>---------------------------------------原文------------------------------&nbsp; <br>---------------------------------------纠正后---------------------------- </p>
<li>UTF-8, 8bit编码, ASCII不作变换, 其他字符做变长编码, 每个字符1-3 byte. 通常作为外码. 有以下优点:<br>* 与CPU字节顺序无关, 可以在不同平台之间交流<br>* 容错能力高, 任何一个字节损坏后, 最多只会导致一个编码码位损失, 不会链锁错误(如GB码错一个字节就会整行乱码) <br>
<li>UTF-16, 16bit编码, 是定长码,&nbsp; 基本上就是unicode编码的实现. 与CPU字序有关
<p>---------------------------------------纠正后---------------------------- </p>
<li>UTF-16是unicode的preferred encoding. <br>
<li>UTF-32, 仅使用了unicode范围(0到0x10FFFF)的32位编码, 相当于UCS-4的子集.<br><br>UTF与unicode的关系:<br><br>Unicode是一个字符集, 可以看作为内码.<br>而UTF是一种编码方式, 它的出现是因为unicode不适宜在某些场合直接传输和处理. UTF-16直接就是unicode编码, 没有变换, 但它包含了0x00在编码内, 头256字节码的第一个byte都是0x00, 在操作系统(C语言)中有特殊意义, 会引起问题. 采用UTF-8编码对unicode的直接编码作些变换可以避免这问题, 并带来一些优点.<br><br>中国国标编码:<br>
<li>GB 13000: 完全等同于ISO 10646-1/Unicode 2.1, 今后也将随ISO 10646/Unicode的标准更改而同步更改.<br>
<li>GBK: 对GB2312的扩充, 以容纳GB2312字符集范围以外的Unicode 2.1的统一汉字部分, 并且增加了部分unicode中没有的字符. <br>
<li>GB 18030-2000: 基于GB 13000, 作为Unicode 3.0的GBK扩展版本, 覆盖了所有unicode编码, 地位等同于UTF-8, UTF-16, 是一种unicode编码形式. 变长编码, 用单字节/双字节/4字节对字符编码. GB18030向下兼容GB2312/GBK. <br>GB 18030是中国所有非手持/嵌入式计算机系统的强制实施标准.
<p><br>-------------------------------</p>
<h2>什么是 UCS 和 ISO 10646?</h2>
<p>国际标准 ISO 10646 定义了 通用字符集 (Universal Character Set, UCS). UCS 是所有其他字符集标准的一个超集. 它保证与其他字符集是双向兼容的. 就是说, 如果你将任何文本字符串翻译到 UCS格式, 然后再翻译回原编码, 你不会丢失任何信息.</p>
<p>UCS 包含了用于表达所有已知语言的字符. 不仅包括拉丁语,希腊语, 斯拉夫语,希伯来语,阿拉伯语,亚美尼亚语和乔治亚语的描述, 还包括中文, 日文和韩文这样的象形文字, 以及 平假名, 片假名, 孟加拉语, 旁遮普语果鲁穆奇字符(Gurmukhi), 泰米尔语, 印.埃纳德语(Kannada), Malayalam, 泰国语, 老挝语, 汉语拼音(Bopomofo), Hangul, Devangari, Gujarati, Oriya, Telugu 以及其他数也数不清的语. 对于还没有加入的语言, 由于正在研究怎样在计算机中最好地编码它们, 因而最终它们都将被加入. 这些语言包括 Tibetian, 高棉语, Runic(古代北欧文字), 埃塞俄比亚语, 其他象形文字, 以及各种各样的印-欧语系的语言, 还包括挑选出来的艺术语言比如 Tengwar, Cirth 和 克林贡语(Klingon). UCS 还包括大量的图形的, 印刷用的, 数学用的和科学用的符号, 包括所有由 TeX, Postscript, MS-DOS，MS-Windows, Macintosh, OCR 字体, 以及许多其他字处理和出版系统提供的字符.</p>
<p>ISO 10646 定义了一个 31 位的字符集. 然而, 在这巨大的编码空间中, 迄今为止只分配了前 65534 个码位 (0x0000 到 0xFFFD). 这个 UCS 的 16位子集称为 基本多语言面 (Basic Multilingual Plane, BMP). 将被编码在 16 位 BMP 以外的字符都属于非常特殊的字符(比如象形文字), 且只有专家在历史和科学领域里才会用到它们. 按当前的计划, 将来也许再也不会有字符被分配到从 0x000000 到 0x10FFFF 这个覆盖了超过 100 万个潜在的未来字符的 21 位的编码空间以外去了. ISO 10646-1 标准第一次发表于 1993 年, 定义了字符集与 BMP 中内容的架构. 定义 BMP 以外的字符编码的第二部分 ISO 10646-2 正在准备中, 但也许要过好几年才能完成. 新的字符仍源源不断地加入到 BMP 中, 但已经存在的字符是稳定的且不会再改变了.</p>
<p>UCS 不仅给每个字符分配一个代码, 而且赋予了一个正式的名字. 表示一个 UCS 或 Unicode 值的十六进制数, 通常在前面加上 "U+", 就象 U+0041 代表字符"拉丁大写字母A". UCS 字符 U+0000 到 U+007F 与 US-ASCII(ISO 646) 是一致的, U+0000 到 U+00FF 与 ISO 8859-1(Latin-1) 也是一致的. 从 U+E000 到 U+F8FF, 已经 BMP 以外的大范围的编码是为私用保留的.</p>
<h2>什么是组合字符?</h2>
<p>UCS里有些编码点分配给了 组合字符.它们类似于打字机上的无间隔重音键. 单个的组合字符不是一个完整的字符. 它是一个类似于重音符或其他指示标记, 加在前一个字符后面. 因而, 重音符可以加在任何字符后面. 那些最重要的被加重的字符, 就象普通语言的正字法(orthographies of common languages)里用到的那种, 在 UCS 里都有自己的位置, 以确保同老的字符集的向后兼容性. 既有自己的编码位置, 又可以表示为一个普通字符跟随一个组合字符的被加重字符, 被称为 预作字符(precomposed characters). UCS 里的预作字符是为了同没有预作字符的旧编码, 比如 ISO 8859, 保持向后兼容性而设的. 组合字符机制允许在任何字符后加上重音符或其他指示标记, 这在科学符号中特别有用, 比如数学方程式和国际音标字母, 可能会需要在一个基本字符后组合上一个或多个指示标记.</p>
<p>组合字符跟随着被修饰的字符. 比如, 德语中的元音变音字符 ("拉丁大写字母A 加上分音符"), 既可以表示为 UCS 码 U+00C4 的预作字符, 也可以表示成一个普通 "拉丁大写字母A" 跟着一个"组合分音符":U+0041 U+0308 这样的组合. 当需要堆叠多个重音符, 或在一个基本字符的上面和下面都要加上组合标记时, 可以使用多个组合字符. 比如在泰国文中, 一个基本字符最多可加上两个组合字符.</p>
<h2>什么是 UCS 实现级别?</h2>
<p>不是所有的系统都需要支持象组合字符这样的 UCS 里所有的先进机制. 因此 ISO 10646 指定了下列三种实现级别: </p>
<dl>
<dt>级别1
<dd>不支持组合字符和 Hangul Jamo 字符 (一种特别的, 更加复杂的韩国文的编码, 使用两个或三个子字符来编码一个韩文音节)
<dt>级别2
<dd>类似于级别1, 但在某些文字中, 允许一列固定的组合字符 (例如, 希伯来文, 阿拉伯文, Devangari, 孟加拉语, 果鲁穆奇语, Gujarati, Oriya, 泰米尔语, Telugo, 印.埃纳德语, Malayalam, 泰国语和老挝语). 如果没有这最起码的几个组合字符, UCS 就不能完整地表达这些语言.
<dt>级别3
<dd>支持所有的 UCS 字符, 例如数学家可以在任意一个字符上加上一个 tilde(颚化符号,西班牙语字母上面的～)或一个箭头(或两者都加). </dd></dl>
<h2>什么是 Unicode?</h2>
<p>历史上, 有两个独立的, 创立单一字符集的尝试. 一个是<a href="http://www.iso.ch/">国际标准化组织(ISO)</a>的 ISO 10646 项目, 另一个是由(一开始大多是美国的)多语言软件制造商组成的协会组织的 <a href="http://www.unicode.org/">Unicode 项目</a>. 幸运的是, 1991年前后, 两个项目的参与者都认识到, 世界不需要两个不同的单一字符集. 它们合并双方的工作成果, 并为创立一个单一编码表而协同工作. 两个项目仍都存在并独立地公布各自的标准, 但 Unicode 协会和 ISO/IEC JTC1/SC2 都同意保持 Unicode 和 ISO 10646 标准的码表兼容, 并紧密地共同调整任何未来的扩展.</p>
<h2>那么 Unicode 和 ISO 10646 不同在什么地方?</h2>
<p>Unicode 协会公布的 <a href="http://www.unicode.org/unicode/standard/standard.html">Unicode 标准</a> 严密地包含了 ISO 10646-1 实现级别3的基本多语言面. 在两个标准里所有的字符都在相同的位置并且有相同的名字.</p>
<p>Unicode 标准额外定义了许多与字符有关的语义符号学, 一般而言是对于实现高质量的印刷出版系统的更好的参考. Unicode 详细说明了绘制某些语言(比如阿拉伯语)表达形式的算法, 处理双向文字(比如拉丁与希伯来文混合文字)的算法和 排序与字符串比较 所需的算法, 以及其他许多东西.</p>
<p>另一方面, ISO 10646 标准, 就象广为人知的 ISO 8859 标准一样, 只不过是一个简单的字符集表. 它指定了一些与标准有关的术语, 定义了一些编码的别名, 并包括了规范说明, 指定了怎样使用 UCS 连接其他 ISO 标准的实现, 比如 ISO 6429 和 ISO 2022. 还有一些与 ISO 紧密相关的, 比如 ISO 14651 是关于 UCS 字符串排序的.</p>
<p>考虑到 Unicode 标准有一个易记的名字, 且在任何好的书店里的 Addison-Wesley 里有, 只花费 ISO 版本的一小部分, 且包括更多的辅助信息, 因而它成为使用广泛得多的参考也就不足为奇了. 然而, 一般认为, 用于打印 ISO 10646-1 标准的字体在某些方面的质量要高于用于打印 Unicode 2.0的. 专业字体设计者总是被建议说要两个标准都实现, 但一些提供的样例字形有显著的区别. ISO 10646-1 标准同样使用四种不同的风格变体来显示表意文字如中文, 日文和韩文 (CJK), 而 Unicode 2.0 的表里只有中文的变体. 这导致了普遍的认为 Unicode 对日本用户来说是不可接收的传说, 尽管是错误的.</p>
<h2>什么是 UTF-8?</h2>
<p>首先 UCS 和 Unicode 只是分配整数给字符的编码表. 现在存在好几种将一串字符表示为一串字节的方法. 最显而易见的两种方法是将 Unicode 文本存储为 2 个 或 4 个字节序列的串. 这两种方法的正式名称分别为 UCS-2 和 UCS-4. 除非另外指定, 否则大多数的字节都是这样的(Bigendian convention). 将一个 ASCII 或 Latin-1 的文件转换成 UCS-2 只需简单地在每个 ASCII 字节前插入 0x00. 如果要转换成 UCS-4, 则必须在每个 ASCII 字节前插入三个 0x00.</p>
<p>在 Unix 下使用 UCS-2 (或 UCS-4) 会导致非常严重的问题. 用这些编码的字符串会包含一些特殊的字符, 比如 '\0' 或 '/', 它们在 文件名和其他 C 库函数参数里都有特别的含义. 另外, 大多数使用 ASCII 文件的 UNIX 下的工具, 如果不进行重大修改是无法读取 16 位的字符的. 基于这些原因, 在文件名, 文本文件, 环境变量等地方, UCS-2 不适合作为 Unicode 的外部编码.</p>
<p>在 ISO 10646-1 <a href="http://www.cl.cam.ac.uk/~mgk25/ucs/ISO-10646-UTF-8.html">Annex R</a> 和 <a href="ftp://ftp.funet.fi/mirrors/nic.nordu.net/rfc/rfc2279.txt">RFC 2279</a> 里定义的 UTF-8 编码没有这些问题. 它是在 Unix 风格的操作系统下使用 Unicode 的明显的方法.</p>
<p>UTF-8 有一下特性: </p>
<ul>
    <li>UCS 字符 U+0000 到 U+007F (ASCII) 被编码为字节 0x00 到 0x7F (ASCII 兼容). 这意味着只包含 7 位 ASCII 字符的文件在 ASCII 和 UTF-8 两种编码方式下是一样的.
    <li>所有 &gt;U+007F 的 UCS 字符被编码为一个多个字节的串, 每个字节都有标记位集. 因此, ASCII 字节 (0x00-0x7F) 不可能作为任何其他字符的一部分.
    <li>表示非 ASCII 字符的多字节串的第一个字节总是在 0xC0 到 0xFD 的范围里, 并指出这个字符包含多少个字节. 多字节串的其余字节都在 0x80 到 0xBF 范围里. 这使得重新同步非常容易, 并使编码无国界, 且很少受丢失字节的影响.
    <li>可以编入所有可能的 2<sup>31</sup>个 UCS 代码
    <li>UTF-8 编码字符理论上可以最多到 6 个字节长, 然而 16 位 BMP 字符最多只用到 3 字节长.
    <li>Bigendian UCS-4 字节串的排列顺序是预定的.
    <li>字节 0xFE 和 0xFF 在 UTF-8 编码中从未用到. </li>
</ul>
<p>下列字节串用来表示一个字符. 用到哪个串取决于该字符在 Unicode 中的序号.</p>
<div align=center>
<center>
<table border=1>
    <tbody>
        <tr>
            <td>U-00000000 - U-0000007F: </td>
            <td>0xxxxxxx</td>
        </tr>
        <tr>
            <td>U-00000080 - U-000007FF: </td>
            <td>110xxxxx 10xxxxxx</td>
        </tr>
        <tr>
            <td>U-00000800 - U-0000FFFF: </td>
            <td>1110xxxx 10xxxxxx 10xxxxxx</td>
        </tr>
        <tr>
            <td>U-00010000 - U-001FFFFF: </td>
            <td>11110xxx 10xxxxxx 10xxxxxx 10xxxxxx</td>
        </tr>
        <tr>
            <td>U-00200000 - U-03FFFFFF: </td>
            <td>111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx</td>
        </tr>
        <tr>
            <td>U-04000000 - U-7FFFFFFF: </td>
            <td>1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx</td>
        </tr>
    </tbody>
</table>
</center></div>
<p>xxx 的位置由字符编码数的二进制表示的位填入. 越靠右的 x 具有越少的特殊意义. 只用最短的那个足够表达一个字符编码数的多字节串. 注意在多字节串中, 第一个字节的开头"1"的数目就是整个串中字节的数目.</p>
<p>例如: Unicode 字符 U+00A9 = 1010 1001 (版权符号) 在 UTF-8 里的编码为:</p>
<blockquote>
<p>11000010 10101001 = 0xC2 0xA9</p>
</blockquote>
<p>而字符 U+2260 = 0010 0010 0110 0000 (不等于) 编码为:</p>
<blockquote>
<p>11100010 10001001 10100000 = 0xE2 0x89 0xA0</p>
</blockquote>
<p>这种编码的官方名字拼写为 UTF-8, 其中 UTF 代表 UCS Transformation Format. 请勿在任何文档中用其他名字 (比如 utf8 或 UTF_8) 来表示 UTF-8, 当然除非你指的是一个变量名而不是这种编码本身.</p>
<h2>什么编程语言支持 Unicode?</h2>
<p>在大约 1993 年之后开发的大多数现代编程语言都有一个特别的数据类型, 叫做 Unicode/ISO 10646-1 字符. 在 Ada95 中叫 Wide_Character, 在 Java 中叫 char.</p>
<p>ISO C 也详细说明了处理多字节编码和宽字符 (wide characters) 的机制, 1994 年 9 月 <a href="http://www.lysator.liu.se/c/na1.html">Amendment 1 to ISO C</a> 发表时又加入了更多. 这些机制主要是为各类东亚编码而设计的, 它们比处理 UCS 所需的要健壮得多. UTF-8 是 ISO C 标准调用多字节字符串的编码的一个例子, wchar_t 类型可以用来存放 Unicode 字符.</p>
</li>
<img src ="http://www.blogjava.net/orangelizq/aggbug/134021.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-08-02 16:54 <a href="http://www.blogjava.net/orangelizq/archive/2007/08/02/134021.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] J2EE vs .NET (下)</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/27/132900.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Fri, 27 Jul 2007 14:56:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/27/132900.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/132900.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/27/132900.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/132900.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/132900.html</trackback:ping><description><![CDATA[<div class=postText><strong>三. 异中有同同中有异</strong> <br>——J2EE与.NET平台体系架构的异同 <br>南京邮电学院 李建忠 <br>中南大学 毕文杰 <br><br>作为彼此竞争的应用平台，J2EE和.NET开发平台在目标和体系结构上极其相似，但在实现上又完全不同。平台的体系架构是支撑平台的基础，平台各方面的性能也会因平台架构实现的不同而有差异。对两个平台产生至关重要影响的三个方面是：系统平台基础构造、三层/多层体系结构和移植/性能/扩展。J2EE是一个平台规范而非产品，对等而论，在这里述及的.NET也专注于该平台的架构规范，而较少地涉及到具体产品，尽管对.NET而言有时候这方面并不能被区分得很清楚。 <br>类似的平台基础构造 <br>一个平台在语言编译、代码执行、编程支持等基础构造方面往往会对平台的可用性、生产性、移植性等产生重要的影响，也是我们评判一个平台是否适合特定应用的重要依据。J2EE和.NET两个平台在底层的执行引擎都源于托管的虚拟机概念，但.NET的CLR沿着Java虚拟机(JVM)走得更远。CLR在借鉴了JVM的自动垃圾收集、异常处理等机制的同时，又为.NET平台添加了多语言支持、组件自描述等新的特性。 <br>在.NET和 J2EE平台上，程序的编译都经过两个类似的过程。首先特定高级语言编译器将C#（及其他.NET语言）和Java源代码分别翻译成中间语言(IL)和字节代码(ByteCode)。.NET在中间语言设计时通盘考虑了多个主流高级语言，在这一层面实现了.NET平台的跨语言承诺。J2EE的基石是Java语言，它最典型的特征是：一次编写，多次运行。跨平台是J2EE一直引以为豪的关键，这是通过JVM来实现的。 <br>其次，在执行时，中间语言被即时编译器(JIT)编译成特定平台的二进制代码，字节代码则通过JVM解释执行，完成各自语言的指令功能。鉴于微软在&#8220;Wintel平台&#8221;上的代码优化功底，.NET代码的执行速度较之于Java有明显的优势是不争的事实。但在Unix/Linux平台上，由于.NET迟迟未能实现其跨平台的承诺，J2EE几乎成了惟一的选择，执行效率的比较也就无所谓。在代码执行的同时，通用语言运行时和Java虚拟机也都提出了异常捕捉、类型安全、内存分配、垃圾收集等自动化内存管理工作，大大减轻了现代软件的内存泄漏问题和程序员繁重的负担。 <br>面向对象程序设计在J2EE和.NET平台中都获得了直接的支持，单根继承加多接口实现是它们共有的特征。但在面向对象之外，.NET对现代组件编程提供了直接支持。当然，当下的很多企业中间件都是基于J2EE平台的,只是.NET从设计、编码、配置到运行给予了组件编程更多、更直接的支持。 <br>一个能够为编程提供广泛服务的、可从玫腁PI类库对于现代软件平台非常重要。从基础的集合、字符串操作到企业级的API接口，如JMS、JDBC、JAX、JNDI等，可以看到J2EE在这方面有着非常坚实的结构。微软.NET框架类库也不示弱，提供了从图画、网络、线程到ADO.NET、ADSI、Windows表单、ASP.NET等一系列的API。在这些基础的和企业级的服务上两个平台很难一决高下，而且对功能集合的支持很多时候是一个时间问题，往往是一个平台推出了某一子功能集，另一个平台马上推出类似的功能集。 <br>除去API类库的无缝的功能复用外，对本地平台的调用操作也是值得关注的一点。CLR和Java虚拟机都支持本地方法的调用。在异构平台方面，J2EE更钟情于IIOP（Internet InterORB Protocol），而.NET则使用SOAP。 <br>相同的三层/多层体系 <br>基于三层/多层分布式计算结构已毋庸置疑地成为当今企业应用的主流模式，也是两个平台较量的着力点。 <br>在客户端，表示层负责用户与系统的交互。对于不同的处理要求，.NET和J2EE都提出了基于桌面的应用程序和基于浏览器的Web应用的开发组件：Java Application与Windows表单、Java Servlet/JSP与ASP.NET双双形成犄角之势。但Windows表单依赖微软桌面系统的天然优势，不管在交互速度还是在界面的表现性能上都较Java Application稍胜一筹。Servlet/JSP与ASP.NET是目前企业在&#8220;瘦客户端&#8221;应用的重点，两者都基于HTTP请求/响应模型，通过HTML浏览器页面完成用户交互。虽然ASP.NET声称在底层通过编译执行获得了相当高的处理速度，以及服务器方控件的浏览器自适应能力，但目前并没有这方面的硬性数据，很难据此而论高下。在缓存、状态优化等方面两者可谓旗鼓相当。另一个和客户端应用相关的技术是ActiveX与Applet，但从目前的趋势来看，它们在两个平台上的地位逐渐边缘化，也不为大多数企业所接受。 <br>在中间层，分布式业务组件负责企业应用的商业逻辑部署。由于这些业务组件经常负责处理数据库连接、网络资源、线程等高昂的资源，所以一直是三层/多层架构的关键和企业应用的核心。J2EE的EJB是一个成熟的、得到业界广泛支持的大型企业级组件框架，而.NET组件则是建立在新型的COM+服务之上，两者在组件与操作系统的交互、客户端资源共享等方面都有很好的支持。EJB的核心是容器，容器是一个为组件提供服务的运行时环境，负责为组件提供诸如事务处理、持久性、安全性、组建状态自动化管理等服务，它分离了商业逻辑和系统底层逻辑，使开发人员的工作大为简化。.NET则通过元数据支持自描述性的组件开发、XCOPY部署以及多版本共存，而无需注册表和描述文件，对企业客户有一定的吸引力。 <br>在后端数据层，两个平台都为数据库连接量身定做了一套数据存取模型：J2EE的JDBC和.NET的ADO.NET。它们在支持传统SQL数据源的同时，也都支持新型的XML数据源。这方面由于更多地涉及到具体的数据库产品，很难说那种数据模型更有优势。 <br>值得指出的是，在打造三层/多层体系结构的同时，Web服务作为新一代企业计算模型也得到了J2EE和.NET平台相当的关注，在后面的文章会有这方面的详细评述。 <br>不同的移植、性能和扩展 <br>在移植性方面，微软通过.NET 通用语言运行时来消除编程语言的差别，而J2EE则通过Java虚拟机来消除平台差别。&#8220;选择.NET平台就意味着选择Windows&#8221;，这句话至少在可预见的一段时间里仍然是一个基本事实。跨平台是J2EE的一大卖点，也是在选择企业应用开发平台时的一个重要参考因素，几乎所有的主流操作系统都提供了对J2EE的支持。实际上如果要搭建跨Unix、Windows等多个操作系统平台，J2EE平台几乎是惟一的选择。J2EE更关注跨平台而不是跨语言。但微软认为，如果企业的应用都能通过标准协议以Web服务的方式发布，那么平台都是中立的。跨平台甚至是微软所不想的。为了吸引更多的开发者和鼓励广大企业厂商转到.NET平台，微软提出了多语言支持，希望用跨语言的交互性来平衡跨平台的互操作。 <br>性能是J2EE和.NET喋喋不休的话题。二者之间著名的论战是一个关于宠物店的范例应用。宠物店是Sun一度以来作为J2EE典型应用的展示范例，但.NET&#8220;自告奋勇&#8221;地在自己的平台上实现了该宠物店应用，且声称代码行是J2EE的1/3，效率却是J2EE的30倍。但Sun的理由是这个范例根本不适合用来做性能比较，该范例实现也没有做针对性能的优化，而且指责微软通过后端数据库优化和缓存虚抬了.NET平台的效率。这样的争吵当然不能作为我们判断的依据，目前也没有见到更客观的第三方评测报告。在&#8220;Wintel平台&#8221;上我们也许没有理由怀疑.NET的性能，而至于非Windows平台，.NET和J2EE也不再具有可比性。 <br>在平台的成熟度方面，两者也有一拼。J2EE在1999年形成了其成熟的架构，并且到今天已经有相当成熟的经过检验的企业应用系统。而.NET究其渊源是源自微软以前开发企业应用程序的平台DNA（Distributed Network Architecture），其中包括了许多已经被证实的技术，并且这些技术已经在产品中得到实现，包括微软的事务服务器、COM+、消息队列、SQL Server数据库等。而对于扩展性，广为业界接受的事实是.NET平台的扩展思想是基于软件的横向扩展，而J2EE平台的扩展思想则是基于硬件的纵向扩展。这也符合微软和Sun各自的产品利益。 <br>J2EE另一个重要特征就是它的架构开放性，它本身是一系列规范，而不是产品，任何符合这一规范的产品都是J2EE兼容的。这使得J2EE从制订之初就得到了广泛的支持。BEA、IBM、Oracle等都相继开发了符合J2EE的应用服务器，它们的产品相互之间甚至可以兼容。而.NET在设计之初就紧紧地把平台规范与产品胶合在一起，虽然.NET架构的一小部分具有开放性（如C#语言、通用语言基础构造CLI 和Web服务标准），但至少目前很难想像会有一个非微软的.NET实现。 <br><br><br><strong>四。Web服务谁主沉浮？</strong> <br>■ 柴晓路 <br>现在已经是2002年第二季度，Gartner Group对Web服务发展的预测似乎被产品提供商稍稍延误了，最近微软的.NET Framework及其开发工具VS .NET刚刚正式发布。而作为Web服务世界中另一个重量级角色Sun，也为它的J2EE Framework增添了开发Web服务的强有力的工具包Java Web Services Developer Pack(WSDP)。从2002年起到2005年，Gartner Group所预测的B2C、B2B以及e-Government领域Web服务的开发和部署将会大量依赖J2EE和.NET这两个平台及其上的开发工具。 <br>.NET与J2EE 对Web服务的支持 <br>从.NET和J2EE这两个平台的发展历程来看， .NET从一开始就深深打上了Web服务技术的烙印，在它的市场推广活动中，无时无刻不凸显其作为Web服务的开发和部署平台的特征。可以说，.NET天生就是为Web服务准备的开发和部署平台。相对.NET而言，J2EE是一个比较&#8220;老&#8221;的东西，最初它是为了将Java平台拓展到企业级应用领域而制订的一个平台框架规范。随着Web服务的兴起和发展，J2EE平台作为一个企业级应用的开发和部署平台，无法回避业界的重大技术革命——Web服务。随着Web服务技术的发展，J2EE也不断地引入了对Web服务的支持。 <br>从服务描述、服务实现和服务的发布、发现与绑定，以及服务的调用和执行这些不同的角度看，J2EE和.NET的支持基本不相上下，惟一的区别可能是.NET的开发工具更为方便一些，集成度更高一些。.NET是一个在J2EE之后出现的平台，所有的重量级技术产品无一例外地都会吸收先前成功者的优点，.NET就大量地吸收了J2EE平台的优点。其中，最重要的一点就是.NET不再完全沿袭微软先前的技术，从.NET开始，其应用不再以本地机器代码运行，而是编译成中间代码，由称为CLR的虚拟机来运行，这样，.NET也具备了跨平台的可能。不过.NET的跨平台特性主要体现在支持多种开发语言上，VB.NET、C#、C++、JScript等都可以被编译成相同的中间代码，使用相同的运行库执行。 <br>第三方厂商的支持 <br>J2EE作为一种开放的规范，从一开始就得到了众多厂商的支持，IBM、BEA、HP、Oracle等在J2EE的实施上都有较大的投入。目前市场上最好的J2EE应用服务器并不是Sun与Netscape合资的iPlanet，而是BEA的WebLogic和IBM的Webshpere。一年一度的JavaONE就有成千上万的开发商参加。由于J2EE是开放的规范框架，任意厂商只要有实力都可以按照规范来开发实现，不同厂商的组件也可以在一起协同使用，当然最关键的是这些参与J2EE的厂商都具有很强的实力。除了微软以外，基本上所有的软件业巨擎都钟情于J2EE。 <br>然而，J2EE虽然是开放的规范，但是它的使用却不是那么开放，每家使用J2EE技术的公司都不得不为此向Sun支付一笔不小的费用。同时也正因为Sun对J2EE规范的独家控制，使得J2EE规范的开发进度缓慢，迄今为止，J2EE规范中并不包含对Web服务的支持，Sun推出的WSDP只是一种插件形式的扩展支持。有消息表明，在今年年底前，Sun和Java领域的其他支持商，包括IBM、Bea、Silverstream等会就J2EE如何支持Web服务达成一致，然而这一切均存在变数，其中的根结就在于Sun对Java技术的独家控制。 <br>同时，由于J2EE对Web服务支持的步履维艰，各大厂商分别自行开发Java平台的Web服务支持，IBM在这个领域的步伐是飞快的，它的WSAD（Webshpere Studio Application Developer）集成了大量自行开发（部分来自于Apache.org，不过这个项目的前身是IBM发起，而后移交给Apache.org）的Web服务组件，业已成为Java领域开发Web服务的最佳开发工具，同时IBM的Websphere也慢慢向Web服务开发部署应用平台的角色转化。 <br>而对于微软的.NET而言，虽然从一开始，微软就以独占、垄断、不开放的形象出现在平台市场上。然而，它的.NET却表现出了前所未有的开放姿态。 <br>.NET的主力开发语言C# 已经提交给 ECMA，开始标准化，ECMA是一个致力于推动行业范围内采用信息和通信技术的非特定供应商的国际标准组织。C#的标准化使希望在任何平台上都可以实现 C# 编程工具的公司能够实现其愿望。微软 还向 ECMA 提交了微软.NET框架的一个子集，叫做CLR（公共语言架构，Common Language Infrastructure）。这将使其他供应商能够在各种平台上实现 CLI，以便用.NET框架提供的基本体系结构模型编写的软件可以在各种平台上用各种工具来创建。美国Ximian公司已于2001年7月启动了一个名为Mono 的开放源码版.NET开发项目，计划内容包括一个C#编译器、与微软的CLI兼容的类库和Linux版CLR编译器。虽然这只是起步，然而谁也不能肯定，它不会像当初的Java那样，从Sun的小玩具，变成了今天如此重要的开发平台。 <br>Web服务规范的控制 <br>由于Web服务的各种技术都是先以规范的形式制订，然后再交付各大开发商进行实施。所以，某个开发商如果从一开始就参与某种Web服务规范的开发，那么它的平台就能够以最快的速度支持这一Web服务规范。在这一点上，微软给人以非常积极进取的印象。在Web服务领域，微软与IBM共同主推了大量的Web服务规范，在一段时间内，两家公司Web服务技术的市场推广活动都是联合举行的，不难看出这两家公司在这个领域背后的战略合作关系。最初的Web服务核心技术SOAP、WSDL主要由这两家公司制订;后来的UDDI是由这两家为首的多家核心企业共同制订;再后来的一些不是核心的Web服务规范，如WS-Inspection、WSFL、WS-Security、WS-Routing、WS-License、WS-Referral等，则完全由这两家来制订，不难看出IBM和微软对于Web服务的贡献以及它们对Web服务规范的控制。 <br>而Sun自从在XML规范的制订中发挥了重要的作用之后，在其后的Internet规范，尤其是Web服务规范的制订中，声音变得非常微弱，而且似乎并没有改善的趋势。最近在Web服务领域中的一件大事是WS-I.org的成立。WS-I.org是为保证Web服务所承诺的互操作性而成立的一个组织，主要工作就是开发保障Web服务互操作性的相关规范，并进行规范实施的测试。WS-I.org的核心成员包括Accenture、BEA、HP、Intel、IBM、Microsoft、Oracle、SAP等，Sun不在其中，甚至都不在非核心成员的列表中。是Sun的发展战略的问题，还是受盈利问题的困扰，我们不得而知，不过我们可以知道的是，Sun再一次在Web服务领域中落后了，由它控制的J2EE规范的状况也就可想而知。 <br>潜在的市场 <br>从技术的发展来看，大型的企业用户或有着成功实施经验的企业用户，并不会因为新技术的推出而盲目地否定旧技术，它们总是在保护投资的前提下，在不推翻现有架构的前提下，有选择地挑选适合的技术。 <br>J2EE已经是一个成熟的、成功的企业级应用解决方案，拥有大量的客户，已经实施了J2EE的企业不太可能在Web服务的时代全面否定J2EE而去接受.NET。.NET是一个全新的架构，虽然它的开发语言中已经包含了诸如VB、C++等传统开发语言，刚刚接触.NET的开发人员会以为能将以前使用VB开发的代码平滑地转移到.NET平台上来。其实不然，VB.NET的语法与VB 6.0已经有了根本性的差别，与其说VB.NET是VB 6.0的升级，不如说VB.NET是C#的Basic版。由于采用了CLI的结构，VB.NET将很难兼容以前的VB 6.0的代码，大量的VB代码无法顺利地转移到.NET上，我们期待着微软能够提供转换程序以实现代码的升级。虽然在源代码级别上的升级变得不是那么容易，不过开发人员仍然可以在.NET平台下，将原有的COM组件进行重新包装，形成 .NET平台下的Web服务组件，而且.NET的整个平台、开发工具的高集成性和友好的开发环境还是会给开发人员留下深刻印象。在Java领域中，无论是Borland的JBuilder 6，还是Sun的Forte for Java，或是IBM的WebShpere Studio Application Developer、VisualAge for Java都无法达到VS .NET的生产效率。开发工具是.NET的一大优势，同时.NET平台对Web服务规范的支持力度也仅有IBM的J2EE平台能够与之相媲美。 <br>因此，笔者认为在大型企业级应用场合，如果已经采用了J2EE架构，应该会在Web服务的时代继续使用J2EE架构。而原先就是采用微软架构的，出于技术延续性的考虑，大多数仍然会选用微软的.NET。那些采用其他技术的企业级应用则会在开发效率、安全性、可靠性、维护代价等不同指标上对两种架构进行考察，应该说机会是均等的，J2EE强在有大量的应用实例，而.NET强在整合集成的优秀开发部署环境。 <br>在中小级别的应用领域，J2EE的占有率优势不再那么明显，一方面，长期以来微软专长于这个领域;另一方面，Java解决方案已经是如此地深入人心，即使是中小企业也会考虑J2EE架构，在这个领域，两者平分秋色。 <br>而在桌面应用（Web服务客户端）领域，除了一些管理客户端会采用Java开发以外，绝大多数的应用毫无疑问地会在微软平台上开发和部署。 <br>谁主沉浮 <br>下面这张表格概括了对两者的比较： <br>比较项目? J2EE? .NET? <br>对Web服务的支持? <br>服务描述? 好? 好? <br>服务实现? 好? 很好? <br>服务发布、发现与绑定? 好? 很好? <br>服务调用和执行? 好? 好? <br>第三方支持? <br>平台提供商? 很好? 有待考察? <br>软件开发商? 很好? 好? <br>对Web服务规范的控制? 情况复杂(注）? 很好? <br>市场前景? <br>企业级大型应用? 很好? 一般? <br>中小级别应用? 好? 好? <br>桌面应用? 差? 很好? <br>注：J2EE的控制者Sun对Web服务规范几乎没有什么控制能力，然而Sun在J2EE上的合作伙伴IBM等对Web服务规范却具备强大的控制力，所以表格中显示&#8220;情况复杂&#8221;。 <br>从表格中，不难看出两者是旗鼓相当的对手，现在就断言谁主沉浮还为时过早。应该说，J2EE目前需要做的是尽快真正将Web服务规范融入到J2EE规范中去，从规范出发统一对Web服务的支持。而.NET迫切需要进行的则是加大平台的开放力度，争取改善微软在用户心目中独断、单方控制、不开放的形象。 <br>在未来相当长时期内，J2EE和.NET都将是企业构建应用系统的重要选择，两个平台将相互共存，两者本身也在不断地相互借鉴和完善，并且有望通过Web服务实现互操作。真正的市场，正是需要强大的竞争者之间的较量，这样用户才能得到最好的技术和解决方案。 <br>小资料&#183; <br>Gartner Group对未来Web服务发展状况的预测： <br>2001年，Web服务的架构平台、开发工具将基本被各大开发商开发完毕。开发人员能够购买到这些面向服务的开发工具，同时将开始构建实际使用的Web服务。 <br>2002年，商业Web服务将大量出现，大量的面向消费者的B2C Web服务将投入使用。 <br>2003年，UDDI注册中心随着Web服务的发展，将变得越来越重要，其中的商业数据也越来越丰富。私有的UDDI注册中心将被投入使用，以支持内部服务信息的交换。而政府的Web服务应用也将不断出现。 <br>2004年，各类企业将会普遍接受基于Web服务的商务应用模式，而服务集中的计算模式将进入青年期。私有的UDDI注册中心仍然在各类应用中处于优势地位，新的赢利模式和商业渠道将到处可见。40％的金融财务服务事务将使用Web服务模式，而35%的在线政府服务将以Web服务的形式提供。 <br>2005年，公共的UDDI注册中心作为公共商务信息的交换机制将大量应用。动态服务同样将大量投入使用。<br></div>
<img src ="http://www.blogjava.net/orangelizq/aggbug/132900.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-27 22:56 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/27/132900.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] J2EE VS .NET (上)</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/27/132899.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Fri, 27 Jul 2007 14:54:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/27/132899.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/132899.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/27/132899.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/132899.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/132899.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 看到这个标题，也许会有人表示疑惑，J2EE和.NET并不在一个层次上，怎么能将它们放在一起呢？需要指出的是，通常所说的.NET包含了一个相当广泛的产品家族，包括开发平台、操作系统、服务器、终端设备等，此外还包括服务平台。开发平台只是整个.NET战略中的一部分，所以确切地说，放在这里的.NET应该算是.NET开发平台。<br>随着三层/多层企业信息系统结构的深度发展和<span style="COLOR: #0000ff">下一代分布式计算模型Web 服务</span>的出现，企业应用中关于平台、框架、语言的竞争也愈演烈。J2EE平台在过去几年里一直引领着企业应用的潮流，但最近微软强力推出的.NET平台也开始吸引着众多IT企业和开发人员的注意力，向J2EE平台提出了强有力的挑战。企业应用领域的技术对抗也因此拉开了架势。<br>