﻿<?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/cader/category/12839.html</link><description>要想负多生孩子少种树</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 21:00:40 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 21:00:40 GMT</pubDate><ttl>60</ttl><item><title>clone技术</title><link>http://www.blogjava.net/cader/articles/57007.html</link><dc:creator>负人博客</dc:creator><author>负人博客</author><pubDate>Thu, 06 Jul 2006 10:14:00 GMT</pubDate><guid>http://www.blogjava.net/cader/articles/57007.html</guid><wfw:comment>http://www.blogjava.net/cader/comments/57007.html</wfw:comment><comments>http://www.blogjava.net/cader/articles/57007.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cader/comments/commentRss/57007.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cader/services/trackbacks/57007.html</trackback:ping><description><![CDATA[
		<p>这是clone技术介绍的第一篇。本篇主要介绍对象clone技术的基本知识。</p>
		<p>Clone基本知识储备<br />在Java里提到clone技术，就不能不提java.lang.Cloneable接口和含有clone方法的Object类。所有具有clone功能的类都有一个特性，那就是它直接或间接地实现了Cloneable接口。否则，我们在尝试调用clone（）方法时，将会触发CloneNotSupportedException异常。下面我们通过对Object类的部分源码的分析，来发现和理解这一特性。请看JDK中Object# clone()方法的源码：</p>
		<p>/*</p>
		<p>…………</p>
		<p>     * @return a clone of this instance.<br />     * @exception? CloneNotSupportedException? if the object's class does not<br />     *support the Cloneable interface. Subclasses<br />     *that override the clone method can also<br />     * throw this exception to indicate that an instance cannot<br />     *be cloned.<br />     * @see java.lang.Cloneable<br />     */</p>
		<p>
				<br />protected native Object clone() throws CloneNotSupportedException;</p>
		<p> </p>
		<p>这段源码的@exception部分的描述内容证实了上文关于clone对象特性论断的正确性。它明确指出对象类必须支持Cloneable接口，否则即使派生类覆盖了Object#clone()方法，也同样会抛出CloneNotSupportedException这个异常。关于覆盖clone()方法，后续文章将会用专门篇幅进行比较详细的分析.</p>
		<p>
				<br />在上一篇中，介绍了java里clone的基本知识。本篇将着重描述如何实现clone。</p>
		<p> </p>
		<p> </p>
		<p>l clone的实现</p>
		<p>1.实现Cloneable接口</p>
		<p>通过上一篇的介绍，我们知道，一个类若要具备clone功能，就必须实现Cloneable接口。做到这一步，clone功能已经基本实现了。Clone功能对我们来说，最主要的还是要能够使用它。那么我们如何才能使用clone功能呢？答案是覆盖Object#clone()方法。</p>
		<p>2. 覆盖Object#clone()方法</p>
		<p>为什么需要覆盖Object#clone()方法？这里得再次从jdk源码说起。JDK中Object# clone()方法的原型是：</p>
		<p>protected native Object clone() throws CloneNotSupportedException;</p>
		<p>是否注意到，这里clone()方法修饰符是protected，而不是public。这种访问的不可见性使得我们对Object#clone()方法不可见。相信读者已明白为什么要覆盖Object#clone()方法。而且，覆盖的方法的修饰符必须是public，如果还保留为protected，覆盖将变得没有实际意义。下面举一个具有clone功能的简单的例子：</p>
		<p>/*</p>
		<p>     * 具有clone功能的类的例子</p>
		<p>*/</p>
		<p>public class CloneableObjExample implements Cloneable {</p>
		<p>//……部分代码已省略……</p>
		<p>      private String name = null;</p>
		<p>private int score = 0;</p>
		<p> </p>
		<p> </p>
		<p>   /** </p>
		<p>* NOTE: 将protected 修饰符 更改为 public</p>
		<p>* @see java.lang.Object#clone()</p>
		<p> */</p>
		<p> public/*protected*/ Object clone() throws CloneNotSupportedException {</p>
		<p>    // call父类的clone方法</p>
		<p>    Object result = super.clone();</p>
		<p> </p>
		<p>     //TODO: 定制clone数据</p>
		<p>    return result;</p>
		<p>}</p>
		<p>}</p>
		<p>3.定制clone</p>
		<p>至此，clone已经真相大白。Clone的对象我们可以对其进行定制。还就上面的例子来说。下面的方法对功能做了一定的增强：</p>
		<p>public/*protected*/ Object clone() throws CloneNotSupportedException {</p>
		<p>  // call父类的clone方法</p>
		<p>  CloneableObjExample result = (CloneableObjExample)super.clone();</p>
		<p> </p>
		<p>  //TODO: 定制clone数据</p>
		<p>  //虽然”clone”了，但还可以做点调整</p>
		<p>    result.name = “New Name”;</p>
		<p> </p>
		<p>    result.score = 90;</p>
		<p>    return result;</p>
		<p>}</p>
		<p> </p>
		<p> </p>
		<p>本篇介绍了如何实现clone。接下来的篇幅将就纵深clone等clone的高级特性进行分析。</p>
		<p>
				<br />本章将进入clone的高级特性，着重讲述纵深clone技术。</p>
		<p>Clone通常有两种类型即浅clone和深clone。首先，分析一下两种的不同。浅clone和深clone都是clone，它们本质区别是对象内部的成员属性（非原生类型属性，如int等）在clone时是否处理为引用。如果仍然保留为引用，则称为浅clone，反之称为深clone。其实这两个概念也是相对的概念。在处理上它们有点区别，浅clone方式得到clone对象即可，深clone方式在得到clone对象后，还需要对引用的成员属性进行“clone”处理。从这个层次上说，深clone并没有什么特别地困难，简单讲就是创建好对象，再设置一些成员属性。关于深clone，网上的文章已经有太多，有点目不暇接的感觉，本文不再赘述，这也不是本文的重点。</p>
		<p>本文的重点是要阐述纵深clone，亦即“N深clone”。深到什么程度为止？本文描述的目标是一直深到你想要的程度，包括深到不能再深的程度。</p>
		<p>实现方案为采用java reflection技术和递归相结合。</p>
		<p>大致步骤描述如下：首先采用java reflection技术动态获取成员方法列表。然后视clone的深度，对具备clone条件的并且有必要clone的成员进行逐一clone。这是一个递归的过程，直到clolne深度已到为止或者到对象已经没有需要clone的成员属性为止。</p>
		<p>何为具备clone条件的并且有必要clone的成员进行逐一clone？比如，原生类型（primitive type），定为瞬态（Transient）的类型，不可访问的类型（!Field#isAccessible()），没实现Cloneable接口的类型等等，都不具备clone条件。String等java定义的类型就不需要再深入clone了，这些属于没必要进行clone的情况。但List类型等“容器”类是有必要clone的成员类型。</p>
		<p>据此，递归程序示意如下（deepClone为java 方法）：</p>
		<p>    /**</p>
		<p>     * @return Object 返回clone的对象 </p>
		<p>     * @param obj 原对象</p>
		<p>     * @param length clone的深度</p>
		<p>     */</p>
		<p>public Object deepClone(Object obj, int length) {</p>
		<p>        Object result = obj;</p>
		<p>//此处为伪代码： 如果对象obj不具备clone条件，就返回result，这也是递归的结束条件。</p>
		<p> </p>
		<p> </p>
		<p>//此处为伪代码： 如果对象obj没必要clone，就返回result</p>
		<p> </p>
		<p> </p>
		<p>//此处为伪代码：开始进行“clone”对象。这地方是调一个抽象方法来处理，这样可以增加很多灵活性。该方法主要目的是实现“clone”对象方案。注意：这里面的“clone”方案可能是我们想都想不到的方案，它可能有很多创意，但效果都是一样的，就是要“clone”个新的对象出来。当然最容易想的就是Object#clone()方法了。示意如下：</p>
		<p>        result = om.clone(obj);</p>
		<p>   </p>
		<p>//此处为伪代码： 获取具备clone条件的并且有必要clone的所有成员。这地方也是调一个抽象方法来处理。同样是为了增强灵活性。获取这些成员的方法有很多，可能是通过setter和getter对来得到，也可能是通过get fields 等等方法得到(这种方法可能不少成员是无法直接访问的，往往需要结合别的方法)，甚至是多种方法的综合。总之，目的只有一个，就是获得这些成员。</p>
		<p> </p>
		<p> </p>
		<p>        for (int i = 0; i &lt; fields.length; i++) {</p>
		<p>            //对成员进行处理</p>
		<p> </p>
		<p> </p>
		<p>            //如果已不需要再判断成员了，那除了“容器”成员外，已经clone结束</p>
		<p>            if (length &lt;= 1) {</p>
		<p>                if (!“容器”成员) {</p>
		<p>                    continue;</p>
		<p>                }</p>
		<p>                try {</p>
		<p>                    //只需clone一次了，注意递归方法的深度参数传入1</p>
		<p>                    clonedFieldValue = deepClone(“容器”成员的值, 1);</p>
		<p>                } catch (Exception ex2) {</p>
		<p>                    ex2.printStackTrace();</p>
		<p>                    return result;                    </p>
		<p>                }               </p>
		<p>            } else {</p>
		<p>                try {</p>
		<p>                    clonedFieldValue = deepClone(成员的值, length - 1);</p>
		<p>                } catch (Exception ex) {</p>
		<p>                    ex.printStackTrace();</p>
		<p>                    return result;</p>
		<p>                }</p>
		<p>            }</p>
		<p>            try {</p>
		<p>                //此处为伪代码：将clone好的值（clonedFieldValue）设进去</p>
		<p>            } catch (Exception ex) {</p>
		<p>                ex.printStackTrace();</p>
		<p>                return result;</p>
		<p>            }            </p>
		<p>        }//for..</p>
		<p>        return result;</p>
		<p>    }</p>
		<p> </p>
		<p> </p>
		<p>至此，已完成了“N深clone”。下面讨论一下别的相关问题。比如说这种深度clone原本是A--&gt;B--&gt;C--……--&gt;xz这样一种情况，就是说A类含有B成员，B里面又含有C成员，依此类推。如果想在“N深clone”时，只clone“xz”这个成员怎么办？其实很简单，这个问题主要是要解决在递归过程中有些成员需要clone同时有些成员不需clone仍保留引用这个问题。在上面的递归示例中已经提到，实现“clone”的“方案”已经被定义成抽象方法，那么我们只要对这个方法做一个满足这个需求的实现即可。</p>
		<p> </p>
<img src ="http://www.blogjava.net/cader/aggbug/57007.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cader/" target="_blank">负人博客</a> 2006-07-06 18:14 <a href="http://www.blogjava.net/cader/articles/57007.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>国际化: 理解Java平台上的Locale</title><link>http://www.blogjava.net/cader/articles/57001.html</link><dc:creator>负人博客</dc:creator><author>负人博客</author><pubDate>Thu, 06 Jul 2006 09:48:00 GMT</pubDate><guid>http://www.blogjava.net/cader/articles/57001.html</guid><wfw:comment>http://www.blogjava.net/cader/comments/57001.html</wfw:comment><comments>http://www.blogjava.net/cader/articles/57001.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/cader/comments/commentRss/57001.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/cader/services/trackbacks/57001.html</trackback:ping><description><![CDATA[语言和地理环境对我们的文化产生重要影响.我们同他人之间的交流以及生活中的事件都发生在语言和地理环境所产生的一个系统里.由于语言和环境的不同,以至需要我们来制定一个适合的方式来达到向他人表述我们自己或者我们的想法的目的.为了有效地与他人交流,需要考虑对方的文化,环境以及使用对方的语言.<br />　　<br />　　类似地,一套有实效的软件系统也要考虑用户的语言和地理区域.语言和区域形成了本地环境,描述了软件本地化的配置和上下文.在Java平台上,通过java.util.Locale对象来描述本地化.<br />　　<br />　　本文为在Java平台上的编程人员讲解Locale对象以其含义.<br />　　<br />　　本文分成如下几部分:<br />　　<br />　　. 定义<br />　　. 构造<br />　　. 预先准备的各Locale<br />　　. 识别可支持的Locale<br />　　. 以字符串来表述Locale<br />　　. 使用 Locale<br />　　. 获取Locale的信息<br />　　. 总结<br />　　<br />　　<b>定义</b><br />　　<br />　　Locale确定了一种专门的语言和区域.通过使用java.util.Locale对象来为那些区域敏感型的对象定制格式化数据以及向用户的展示.Locale影响到用户界面的语言,情形映射,整理(排序),日期和时间的格式以及货币格式.Locale在很多文化背景和语言敏感型的数据操作上的要求很严格.<br />　　<br />　　java.util.Locale是个轻量级对象,包含为数不多的几个重要成员:<br />　　<br />　　. 一个语言代号<br />　　. 一个国家或者区域的可选项<br />　　. 一个另一形式代号的可选项<br />　　<br />　　谈到或者书写Locale时,为了方便使用,可用缩写.在这样的缩写表述里,使用下划线来把Locale对象的每部分隔开来:<br />　　&lt;language code&gt;[_&lt;country code&gt;[_&lt;variant code&gt;]]<br />　　<br />　　这三部分内容提供了足够的信息让其他些个区域敏感型对象为了特定的语言文化来修饰它们的行为.举例讲,java.text.NumberFormat对象格式化出来的数字同在德语拼写的奥地利和瑞士是有区别的.<br />　　<br />　　表一,不同Locale的格式化输出<br />　　<br />　　Locale　　　　　　　　　 formatted Numbers<br />　　======================================<br />　　German (Germany)　　　　　 123.456,789<br />　　German (Switzerland)　　　 123'456.789<br />　　English (United States)　　123,456.789<br />　　<br />　　Locale对象是个标识符,像jva.text.NumberFormat,java.text.DateFormat这样的区域敏感型的对象都会提供本地化的数字或者日期的格式.举例说,java.text.DateFormat类在其实例化过程中用Locale对象来正确地定出日期的格式.<br />　　<br />　　以下部分讲述locale的各成分<br />　　<br />　　ISO 639制定了语言代号,国际标准组织为世界上的大多数语言指派2个或者3个字母来代表.Locale用2个字母代号标识出想要的语言.表二列出这样的几个语言代号.<br />　　<br />　　表二, ISO 639标准里的语言代号的例子<br />　　<br />　　Language　　 Code<br />　　=====================<br />　　Arabic　　　　ar<br />　　German　　　　de<br />　　English　　　 en<br />　　Spanish　　　 es<br />　　Japanese　　　ja<br />　　Hebrew　　　　he<br />　　<br />　　语言环境是Locale对象里的重要组成部分,因为它描述了特定用户群的语言.你的应用程序需要用这此信息来为用户提供与其语言一致的用户界面.<br />　　<br />　　当然,语言并没描绘了整个Locale.举一例,即使把 de 作为本地语言代号, 单单的一个 de 并不能让你知道到底是哪一地区的的人讲的德语.一些国家把德语作为官方语言或者作为第二种语言.一个国家与另一个国家里的德语的区别之一就是排列顺序.由于这样的原因以及另外些原由,语言并不总能充分地准确地定义一个区域.<br />　　<br />　　<b>国家(区域)代号</b><br />　　<br />　　另一个国际标准ISO 3166定义了国家代号.这个标准为世界上的大多数主要区域以及每个国家定义了2到3个的缩写字母.跟语言代号对比,国家代号是用大写字符的.表三给出了一些代号定义.Locale用两字母的代号来替代标准里也同样支持的3字母的代号<br />　　<br />　　表三,ISO 3166标准中的一些国家代号的定义<br />　　<br />　　Country　　 Code<br />　　====================<br />　　China　　　　　 CH<br />　　Canada　　　　　　 CA<br />　　France　　　　　　 FR<br />　　Japan　　　　　　 JP<br />　　Germany　　 DE<br />　　<br />　　国家代号是Locale的重要组成部分.对应日期的java.text.Format对象,时间,数字和货币都对国家代号很敏感.有了国家代号就可更好地确认Locale里的语言部分.举例来说,在加拿大和法国都说法语的,然而确切的用法和语言表达习惯却是不一样的.这些不同之处可以用Locale里的国家代号来区分出来.例如, 代号fr_CA(加拿大法语)跟fr_FR(法国法语)就不一样.<br />　　<br />　　<b>方言<br />　　</b><br />　　操作系统,浏览器以及其他软件供应商可用这个代号来提供附加的功能或者到达语言和国家代号所不能实现的定制.比如说,一家软件公司也许会为一特定操作系统指定一个locale,于是开发人员为西班牙的Macintosh操作系统创建了es_ES_MAC或者为Windows操作系统创建es_ES_WIN的本地化.<br />　　<br />　　Java平台历史上的一个例子就是使用EURO变量来为欧洲用户实现本地化.Java 2平台1.3标准版过度时期里就为欧洲的这些国家的本地化使用了这个变量.比如,尽管已经存在了 de_DE(德国德语)的本地化,但还是将 de_DE_EURO(欧洲的德国德语)加进了Java环境里.由于欧元是那些接受欧元流通国家里的标准货币,因此这个变量就从 J2SE1.4版本里剔除了.多数应用程序可能不需要这个代号.<br />　　<br />　　<b>构造</b><br />　　<br />　　Locale类有如下几个构造器:<br />　　<br />　　. Locale(String language)<br />　　. Locale(String language, String country)<br />　　. Locale(String language, String country, String variant)<br />　　<br />　　以下展示每个构造器如何使用:<br />　　//创建一个通用英语的locale.<br />　　Locale locale1 = new Locale("en");<br />　　//创建一个加拿大英语的locale.<br />　　Locale locale2 = new Locale("en", "CA");<br />　　//创建一个美式英语的locale<br />　　//硅谷的英语<br />　　Locale locale3 = new Locale("en", "US", "SiliconValley");<br />　　<br />　　用ISO 639的两字母代号表示的en代表了英语.ISO 3166的代号CA和US分别代表加拿大和美国.有个特别:如上代码片段里最后一行上用一个可选变量en_US_SiliconValley创建locale.<br />　　<br />　　这个locale比第一个locale更明细.不仅说明了是美式英语,还特地讲指出是硅谷那边的.其目的之一是让开发者可以定制用户的本地化,这个变量可以是任何你想要的值.<br />　　<br />　　虽然编译与运行环境不关心你是否指定了你自己的语言或者是别的国家地区,但是你应当用ISO标准里定义的代号来区别.强制自己去用ISO标准里的定义,这样可确保同其他应用程序或者代码标准兼容.<br />　　<br />　　更为重要的一点是,区域敏感型的类库仅用ISO代号.比如,java.text.NumberFormat类对于de_DE的本地化是能明白的,但是却不知如何处置虚构的foo_biz.假如用非ISO标准的定义,你不得不写程序代码去实现支持那些非ISO标准定义的代号.<br />　　<br />　　<b>预定义的Locale</b><br />　　<br />　　Locale类里有几个Locale类型的静态成员实例.比如说,Locale.FRANCE 就是预先就准备好的代表法国法语.你可在想要的地方用Locale.FRANCE也可以用new Locale("fr", "FR")的方式来实现.表四给出了一些预定义的现成Locale对象实例<br />　　<br />　　Locale Name　　　　　　　　　　　Locale<br />　　=======================================<br />　　Locale.CHINA　　　　　　　　　　 zh_CN<br />　　Locale.CHINESE　　　　　　　　　 zh<br />　　Locale.SIMPLIFIED_CHINESE　　　　zh_CN<br />　　Locale.TRADITIONAL_CHINESE　　　 zh_TW<br />　　Locale.PRC　　　　　　　　　　　 zh_CN<br />　　Locale.TAIWAN　　　　　　　　　　zh_TW<br />　　Locale.ENGLISH　　　　　　　　　 en<br />　　Locale.UK　　　　　　　　　　　　en_GB<br />　　Locale.US　　　　　　　　　　　　en_US<br />　　Locale.FRANCE　　　　　　　　　　fr_FR<br />　　Locale.FRENCH　　　　　　　　　　fr<br />　　<br />　　这些个预定义的Locale对象的实例的存在确实很方便.然而,仅列出的这几个静态成员是有限的,不完整的,并不需要对每个重要的locale都要来描述.支持区域敏感型的类的locale并不需要在Locale类里有Locale类型的成员常量.比如说,Locale类里就没有代表南美洲的常量.虽然没有,但是照样支持包括日期格式和数字格式这样的区域敏感型的类.<br />　　<br />　　由于仅存在极少量的预先准备好的locale,以至你可能回避这些静态实例.但是确实存在或者你会在别人的代码里碰到,所以在此对它们有所提级,尽管它们很方便,但是不靠它们,你的代码也是可行的.<br />　　<br />　　<b>识别支持的Locales</b><br />　　<br />　　Java平台支持什么样的locale?你可创建你想要的任何locale,然而你的运行时刻环境不一定全支持你创建的这些locale.<br />　　<br />　　那么你想知道你可以创建什么样的locale呢?答案很简单:你可创建任何你要的locale.构造器不会在乎非ISO标准的参数.但是从这个提问里引出来:对什么样的locale,类库能提供更多信息呢?对什么样的locale,库能提供整理,时间,日期,数字和货币的信息呢?也许你会依靠脚本或干脆你来写能支持运行环境的系统.<br />　　<br />　　以下部分讲述如何识别出运行库能支持的locales.另外还讲解了可支持文本组件的书写.最后,例举了运行库中和开发包里的几个可用的本地化.<br />　　<br />　　<b>把locale用在java.util包和java.text包</b><br />　　<br />　　运行时刻环境并不需要对每个区域敏感型的类都要用locale来支持.每个区域敏感型的类实现为它自身支持的一套locale,这套设置能使类跟类可区别开来.比如,一个格式化数字类支持的locale就跟一个日期类支持的locale有所区别.<br />　　<br />　　另外,不需要所有的运行环境的实现都要支持同一套locale.但是所有的运行环境的实现都必须支持一个locale的最小集合.这个集合很小,仅是英语环境(U.S).幸好,SUN公司提供的运行环境的实现里提供了更多的支持.尽管这不是形式上所必须的,但是SUN公司提供的各运行环境的实现里为区域敏感型的数据格式提供了同样的一套集合.这样的做法为类提供了一致性.Java 2标准版5.0里对locale支持的指南提供了详细的列表说明.表五给出一部分所支持的locales<br />　　<br />　　表五,java.util和java.text包里所支持的部分locales<br />　　<br />　　Language　　　　　　　　　　 Country　　　　　　 Locale ID<br />　　==========================================================<br />　　Arabic　　　　　　　　　　　Saudia Arabia　　　　　 ar_SA<br />　　Chinese(simplified)　　　　 China　　　　　　　　　 zh_CN<br />　　Chinese (traditional)　　　 Taiwan　　　　　　　　　zh_TW<br />　　Dutch　　　　　　　　　　　 Netherlands　　　　　　 nl_NL<br />　　English　　　　　　　　　　 Australia　　　　　　　 en_AU<br />　　English　　　　　　　　　　 Canada　　　　　　　　　en_CA<br />　　English　　　　　　　　　　 United Kingdom　　　　　en_GB<br />　　English　　　　　　　　　　 United States　　　　　 en_US<br />　　French　　　　　　　　　　　Canada　　　　　　　　　fr_CA<br />　　French　　　　　　　　　　　France　　　　　　　　　fr_FR<br />　　German　　　　　　　　　　　Germany　　　　　　　　 de_DE<br />　　Hebrew　　　　　　　　　　　Israel　　　　　　　　　he_IL<br />　　Hindi　　　　　　　　　　　 India　　　　　　　　　 hi_IN<br />　　Italian　　　　　　　　　　 Italy　　　　　　　　　 it_IT<br />　　Japanese　　　　　　　　　　Japan　　　　　　　　　 ja_JP<br />　　Korean　　　　　　　　　　　South Korea　　　　　　 ko_KR<br />　　Portuguese　　　　　　　　　Brazil　　　　　　　　　pt_BR<br />　　Spanish　　　　　　　　　　 Spain　　　　　　　　　 es_ES<br />　　Swedish　　　　　　　　　　 Sweden　　　　　　　　　sv_SE<br />　　Thai (Western digits)　　　 Thailand　　　　　　　　th_TH<br />　　Thai (Thai digits)　　　　　Thailand　　　　　　　　th_TH_TH<br />　　<br />　　你得问一问区域敏感型的类在运行时刻环境里(JRE)到底支持什么locales.每个区域敏感型类支持通过实现方法getAvailableLocales()来支持多种locale.比如:<br />　　Locale[] localeList = NumberFormat.getAvailableLocales();<br />　　<br />　　java.util和java.text包里有许多类实现了getAvailableLocales()方法.例如,NumberFormat,<br />　　DateFormat,Calendar和BreakIterator.<br />　　<br />　　Locale类本身也是本本地化成几个locale,在下面例子中,一个德国locale实例以英语(默认情况下是作者的主机),德语和法语提供了信息:<br />　　<br />　　Locale deLocale = new Locale("de", "DE");<br />　　Locale frLocale = new Locale("fr", "FR");<br />　　System.out.println("Default language name (default): " +<br />　　deLocale.getDisplayLanguage());<br />　　System.out.println("German language name (German): " +<br />　　deLocale.getDisplayLanguage(deLocale));<br />　　System.out.println("German language name (French): " +<br />　　deLocale.getDisplayLanguage(frLocale));<br />　　<br />　　输出结果如下<br />　　German language name (default): German<br />　　German language name (German): Deutsch<br />　　German language name (French): allemand<br />　　<br />　　<b>对字符的支持</b><br />　　<br />　　文本组件通常不支持单个locale.实际上文本组件常在本地上用一套书写样式来显示.<br />　　<br />　　尽管不能从各文本组件上获取一个所支持的书写样式列表,但是这样的一个列表却是J2SE 5.0的本地化指南中公开的.<br />　　<br />　　通常,AWT对等体组件在操作系统的支持下可进行书写样式的展示.如果你的系统是阿拉伯的环境,那么AWT的文本组件会显示阿拉伯文字.在一个阿拉伯环境的系统上,你也能在文本框或者文本域里输入阿拉伯文.然而你可别指望不在同一语言环境时这些AWT组件可显示同样的文本.比如在一个英语环境的系统不大可能在文本框里显示阿拉伯文字.<br />　　<br />　　Java Foundation Classes/Swing (JFC/Swing)组件通常能支持多语言,是因为不依赖主机系统以及使用UNICODE作为多语言字符集的编码.因此SWING组件通常可显示多语言字符而AWT的对等体组件就不能.表六显示了一些能支持的字符<br />　　<br />　　表六.一些可显示的文本字符<br />　　<br />　　Writing System　　　　　　　　　　Language<br />　　=======================================================<br />　　Arabic　　　　　　　　　　　　　　Arabic<br />　　Chinese (simplified)　　　　　　　Chinese<br />　　Chinese (traditional)　　　　　　 Chinese<br />　　Devanagari　　　　　　　　　　　　Hindi<br />　　Hebrew　　　　　　　　　　　　　　Hebrew<br />　　Japanese　　　　　　　　　　　　　Japanese<br />　　Korean　　　　　　　　　　　　　　Korean<br />　　<br />　　Latin: Western European　　　　　 English, French, German, Italian,<br />　　subset　　　　　　　　　　　　　　Spanish, Swedish, and so on<br />　　<br />　　Thai　　　　　　　　　　　　　　　Thai<br />　　Greek　　　　　　　　　　　　　　 Greek<br />　　Cyrillic　　　　　　　　　　　　　Belorussian, Russian, and so on<br />　　Latin: Baltic subset　　　　　　　Latvian, Lithuanian<br />　　Latin: Central European subset　　Czech, Hungarian, Polish, and so on<br />　　Latin: Turkic subset　　　　　　　Turkish and so on<br />　　<br />　　<b>JRE与SDK的本地化</b><br />　　<br />　　运行环境里的几个用户界面元素已经根据本地样式进行了本地化.这些元素包括了AWT与SWING的组件以及JRE和SDK产生的消息.表七给出了J2SE 5.0提供的所有本地化<br />　　<br />　　表七.为JRE作的用户界面的翻译<br />　　<br />　　Language　　　　　　　　 Locale ID<br />　　=================================<br />　　Chinese (simplified)　　　 zh_CN<br />　　Chinese (traditional)　　　zh_TW<br />　　English　　　　　　　　　　en<br />　　French　　　　　　　　　　 fr<br />　　German　　　　　　　　　　 de<br />　　Italian　　　　　　　　　　it<br />　　Japanese　　　　　　　　　 ja<br />　　Korean　　　　　　　　　　 ko<br />　　Spanish　　　　　　　　　　es<br />　　Swedish　　　　　　　　　　sv<br />　　<br />　　某些开发工具,比如JAVA编译器,仅随J2SE的软件开发包供给.这些工具为用户提供了出错,出错,警告和其他消息.这些工具以及SDK里的实用工具包括编译器里的消息,被翻译为英语或者日语.这些翻译的结果在J2SE 5.0里已经实现了.<br />　　<br />　　<b>以字符串形式描述locale</b><br />　　<br />　　尽管你使用locale的多数时候需要引用一个Locale对象,但是有些时候用另一种表现形式会很方便,尤其是进行内部调试时候.Locale对象的toString()方法返回String,它是语言,区域和变种代号的一个串.toString()方法以下划线来把各部分隔开.这样,提供了一种方便的可读性好的形式方便你的调试.<br />　　<br />　　考虑如下那样创建locale的代码<br />　　Locale l = new Locale("ja", "JP");<br />　　<br />　　toString()方法将返回 "ja_JP".<br />　　<br />　　如此的字符串不适合提供给最终用户.大多数用户不熟悉ISO 639和ISO 3166标准中的国家和语言的代号,会觉得这样的字符串不好懂.幸好还有友好用户界面的文本展示,我们将在本文后面中讨论.<br />　　<br />　　<b>使用locale</b><br />　　<br />　　尽管不常见,Locale对象却是在JAVA类库中普遍用到.即使你不明确地要求用locale,JAVA运行环境也会为你提供缺省的设置来实现消息和行为的本地化.当你明确地使用locale,你可为你的应用程序的每个部分使用不同的locale.<br />　　<br />　　譬如,你可用es_MX,西班牙语(墨西哥)的locale来显示本地化的消息;用en_US,(美式英语)的locale来显示货币和数字.这方式很适合在美国工作生活的习惯西班牙语的人.尽管用户见到的是西班牙语的菜单,提示与文本,但是应用程序其他地方可见到按照美国习惯的货币和数字.这个简单例子向你展现了如何在一个应用程序里使用多种locale.如果你的应用程序真是需要这样的locale支持的话,那么你可自主决定应用程序各方面的行为.<br />　　<br />　　除了像格式化数字,货币,日期和时间这样的区域敏感型的类之外还提供了如同词语分解的整理功能.<br />　　<br />　　这些类通常以构造器或者是工厂方法来实例化.无论哪种形式,你可按照你喜欢的一种来创建一个详细的locale.<br />　　<br />　　<b>使用缺省Locale</b><br />　　<br />　　无论应用程序是否明确指定了详细的locale,区域敏感型的对象都会选用缺省的locale.依赖这缺省的locale很不明智.在多用户应用程序里,不是每个用户都适合这个缺省的locale,因此应用程序应当为所有的区域敏感型对象明确指定一个适合的locale.缺省的locale是一种为应用程序里所有的区域敏感型对象提供的系统资源.缺省时,也许对用户来说可能是对的,但是应当在多语言和多文化的环境下明确指定,当程序运行在一台机器上为多用户所使用时,这点尤其重要.<br />　　<br />　　用如下的方法来获得缺省locale:<br />　　public static Locale getDefault()<br />　　<br />　　有三种途径为应用程序确定出一个缺省的locale.<br />　　<br />　　第一,除非明确的更改默认的locale,否则getDefault()方法返回值的locale是由JAVA虚拟机首次装载时确定的.这就是说,JAVA虚拟机从系统环境确定出了缺省的locale.系统环境上的locale是由操作系统和用户喜好来确定的;<br />　　<br />　　第二,某些JAVA运行环境的实现里,用户可通过命令行上设置user.language, user.country以及 user.variant信息来覆盖掉系统的缺省locale.<br />　　<br />　　如的代码会依据这些设置的属性来打印出locale<br />　　import java.util.Locale;<br />　　<br />　　public class Default {<br />　　public static void main(String[] args) {<br />　　System.out.println(Locale.getDefault());<br />　　}<br />　　}<br />　　<br />　　根据如上代码来做个实验.运行在U.S的英语系统时候,如上代码会输出en_US. 假如在命令行上给出选项参数,那么你可让程序使用任何你想要的locale.例如,为程序提供如下的命令行参数:<br />　　java -Duser.language=fr -Duser.country=CA Default<br />　　<br />　　以这样的方式来运行程序的话,将把fr_CA当作默认的locale<br />　　<br />　　第三,可通过调用setDefault(Locale aLocale)方法来实现. setDefault(Locale aLocale)方法让应用程序设置了一个系统级别的资源.在用此方法设置了缺省的locale后,接着调用Locale.getDefault()就得到了最新设置的locale.<br />　　<br />　　注意:别在applet中调用setDefault()方法.applet的安全管理不会让你调用这方法的,因为这个方法调用的结果会影响主机上运行着的JAVA虚拟机上的系统级别的资源.<br />　　<br />　　大多数情况下,对于其他类使用缺省locale意味着完全忽略locale.譬如,你想要以缺省locale格式化一个数字,你可以简单地而不需要任何参数来创建NumberFormat:<br />　　NumberFormat nf = NumberFormat.getInstance();<br />　　<br />　　如上代码的情况下使用locale基本上不需要你做什么.另外一些区域敏感型的类也是照这样的模式.需要缺省locale的行为的时候不需要作特定的工作来创建.但是缺省行为不是适合所有情况的,因此在特殊时候你还需要明确指定locale.<br />　　<br />　　<b>显示使用locale</b><br />　　<br />　　在有些计算机的环境中,仅有单一的locale贯穿于整个应用程序的生命周期里.另外一些环境里,应用程序使用一个可变的全局locale.在这些环境里,你可通过编程来改变locale,除非去改动locale,否则它一直保持原先的效果.JAVA应用程序环境很独特,在应用程序里,可依照你想要的任何方式来使用不同的locale.<br />　　<br />　　跨国公司在世界上有许多客户.这就意味着这些公司的客户和员工也许使用着不同的语言并且希望公司和公司的软件能适合他们.此外,很可能也很常见的事是:公司里有个法国雇员为一位意大利客户处理销售记录.在这种情形下,你必须要控制好业务以及用户界面中的对象所使用的locale以便处理和展现数据.应用程序可能在打印销售收据时候使用意大利格式的日期和货币格式,然而又要为习惯英语的雇员列客户清单.还有很多类似这么复杂的情况没有列在此处.但是JAVA技术提供了灵活的方式来处理如此复杂的情况.<br />　　<br />　　为了最大限度的灵活性,你必须为区域敏感型的类指定明确的locale.这就意味着你必须为程序的各个方面考虑locale的表现样式或者为不同的用户和客户指定locale.<br />　　<br />　　如果你已经为用户方的locale作了考虑,那么在构造器参数列表里明确指定一个locale或者在一些特定方法里指定以此来创建区域敏感型的类的实例.设想有个用来作展现的对象里存储了为客户选用的locale:<br />　　<br />　　Locale userLocale = preferences.getLocale();<br />　　NumberFormat nf = NumberFormat.getInstance(userLocale);<br />　　<br />　　<b>获取locale的信息</b><br />　　<br />　　由于locale对象不包含很多信息,但是提供了一些有趣的方法.正如你想的,这些信息跟语言,国家和方言是有密切联系的.其中一些是依赖locale的,另一些则不依赖locale.这些情况说明了locale对象为它的方法提供了两种不同形式.locale里的一些信息并非是面向客户或者本地化的,另一些则是本地化的并且适合用来为用户展现的.<br />　　<br />　　<b>不依赖locale的信息</b><br />　　<br />　　getLanguage() 方法返回ISO 639 标准中为语言定义的两字母缩写.比如,你创建了ja_JP的locale,那么方法返回的是 ja . 这个方法的完整形式是:<br />　　<br />　　public String getLanguage()<br />　　<br />　　ISO 639标准的一个扩展是用三个字母缩写的代号来定义语言的.尽管在J2SE 5.0里不使用这些代号,但是却是有用的.用如下的方法来获得三字母缩写的语言代号:<br />　　<br />　　public String getISO3Language()<br />　　<br />　　看个对比的例子:<br />　　<br />　　Locale aLocale = Locale.JAPAN;<br />　　System.out.println("Locale: " + aLocale);<br />　　System.out.println("ISO 2 letter: "+ aLocale.getLanguage());<br />　　System.out.println("ISO 3 letter: " + aLocale.getISO3Language());<br />　　aLocale = Locale.US;<br />　　System.out.println("Locale:" + aLocale);<br />　　System.out.println("ISO 2 letter: " + aLocale.getLanguage());<br />　　System.out.println("ISO 3 letter: " + aLocale.getISO3Language());<br />　　<br />　　输出结果如下:<br />　　<br />　　Locale: ja_JP<br />　　ISO 2 letter: ja<br />　　ISO 3 letter: jpn<br />　　Locale: en_US<br />　　ISO 2 letter: en<br />　　ISO 3 letter: eng<br />　　<br />　　getCountry()方法返回 ISO 3169标准中两字母缩写定义的国家或者区域代号.方法的完整形式: public String getCountry()<br />　　<br />　　也有个ISO扩展标准里用三字母缩写来定义国家代号:public String getISO3Country()<br />　　<br />　　看个对比的例子:<br />　　<br />　　Locale aLocale = Locale.CANADA_FRENCH;<br />　　System.out.println("Locale: " + aLocale);<br />　　System.out.println("ISO 2 letter: " + aLocale.getCountry());<br />　　System.out.println("ISO 3 letter: " + aLocale.getISO3Country());<br />　　<br />　　输出结果如下:<br />　　<br />　　Locale: fr_CA<br />　　ISO 2 letter: CA<br />　　ISO 3 letter: CAN<br />　　<br />　　如果locale对象里设置有方言的字段的话, getVariant()方法返回一个字符串来表示.如果locale对象里没设置方言的字段的话,此方法返回空串.此方法的形式如下:public String getVariant()<br />　　<br />　　locale类中的如下几个方法可获取所有可用的语言代号和国家代号:<br />　　public static String[] getISOCountries()<br />　　public static String[] getISOLanguages()<br />　　<br />　　相对于客户来说,开发者更愿意使用getLanguage()方法返回的代号.而客户可能想要如下文中描述的那些不同之处.<br />　　<br />　　<b>依赖locale的信息</b><br />　　<br />　　方法getLanguage(), getCountry(), getVariant()返回的代号对于用户来说不友好.用户并不需要懂这些代号,因此locale提供另外的一些方法来提供了更具有可读性的面向客户的信息.<br />　　<br />　　locale对象提供了一些可返回人能读懂的文本描述的方法.这些文本描述不同于toString()方法返回的字符串.并非把语言,国家和方言这些字段简单的串连起来,而是提供了locale的人可读懂的,本土化了的信息:<br />　　<br />　　public final String getDisplayLanguage()<br />　　public final String getDisplayCountry()<br />　　public final String getDisplayVariant()<br />　　<br />　　<b>显示语言</b><br />　　<br />　　当你需要以用户习惯的语言来显示的时候,应使用locale对象的getDisplayLanguage()放.此方法返回了可显示的,人能读懂的用户方语言的名称.假如你不给出目标locale的话,这个显示的名称就是缺省的locale的本土化.此方法的两种形式:<br />　　public final String getDisplayLanguage()<br />　　public final String getDisplayLanguage(Locale targetLocale)<br />　　<br />　　以下举例如何使用这两个方法:<br />　　<br />　　Locale deLocale = Locale.GERMANY;<br />　　// 系统默认的locale是 en_US<br />　　String defaultLanguage = deLocale.getDisplayLanguage();<br />　　//目标locale显示地指出了使用的语言<br />　　String targetLanguage = deLocale.getDisplayLanguage(deLocale);<br />　　System.out.println(defaultLanguage);<br />　　System.out.println(targetLanguage);<br />　　<br />　　输出结果如下:<br />　　German<br />　　Deutsch<br />　　<br />　　输出的 German 是以美式英语单词来显示locale里所使用的语言.这并没给你留下特别的印象,但是要注意的是:你怎样提供一个目标locale作参数.这种情形中，getDisplayLanguage()试图找到和返回一个本土化了的lcoale中语言组件的版本.<br />　　<br />　　这一点很重要,因为你能为客户显示应用程序可支持的客户方语言的每个locale的语言名称.你可在程序里提供一个列表来让用户选他们喜好的locale.<br />　　<br />　　这就引来一个有趣的问题:如何在locale的语言环境里呈现locale的语言名称?你可按照如下代码来做:<br />　　String displayLang = aLocale.getDisplayLanguage(aLocale);<br />　　<br />　　换言之,就是在调用getDisplayLanguage()方法时候以此locale对象作参数.对于其他可显示的locale元素也可使用这个窍门.比如,也能用这种方法来显示国家和方言.如下代码演示了这个窍门.<br />　　<br />　　Locale[] locales = { new Locale("en", "US"), new Locale("ja","JP"),<br />　　new Locale("es", "ES"), new Locale("it", "IT") };<br />　　for (int x=0; x&lt; locales.length; ++x) {<br />　　String displayLanguage = locales[x].getDisplayLanguage(locales[x]);<br />　　println(locales[x].toString() + ": " + displayLanguage);<br />　　}<br />　　<br />　　<b>显示国家</b><br />　　<br />　　给用户显示locale对象里的国家或者地区信息时可按照如下代码:<br />　　<br />　　public final String getDisplayCountry()<br />　　public final String getDisplayCountry(Locale targetLocale)<br />　　<br />　　第一种形式的方法为缺省locale提供了本土化的国家名称.第二种形式的方法为目标locale提供了同样的本土化信息.<br />　　<br />　　Locale deLocale = Locale.GERMANY;<br />　　// default en_US<br />　　String defaultCountry = deLocale.getDisplayCountry();<br />　　// target de_DE<br />　　String targetCountry = deLocale.getDisplayCountry(deLocale);<br />　　System.out.println(defaultCountry);<br />　　System.out.println(targetCountry);<br />　　<br />　　输出结果如下:<br />　　<br />　　Germany<br />　　Deutschland<br />　　<br />　　<b>显示方言</b><br />　　<br />　　方言在locale里相对于其他元素用地比较少.然而有时还需要要获取它的信息.<br />　　<br />　　getDisplayVariant()方法返回locale里的方言部分的显示名称.<br />　　<br />　　public final String getDisplayVariant()<br />　　public final String getDisplayVariant(Locale targetLocale)<br />　　<br />　　在JAVA平台使用方言的一个方法是为支持泰国语言.根据约定,对于 th 和 th_TH的locale而使用的NumberFormat对象通常以阿拉伯数字或者是泰国格式的阿拉伯数字来使用.对于th_TH_TH的locale的NumberFormat是用泰国数字的,如下面的代码演示:<br />　　<br />　　Locale[] thaiLocale = {new Locale("th"), new Locale("th","TH"),<br />　　new Locale("th","TH", "TH")};<br />　　<br />　　for(Locale locale: thaiLocale) {<br />　　NumberFormat nf = NumberFormat.getNumberInstance(locale);<br />　　StringBuffer msgBuff = new StringBuffer();<br />　　msgBuff.append(locale.toString() + ": ");<br />　　msgBuff.append(nf.format(573.34));<br />　　textArea.append(msgBuff.toString() + "\n");<br />　　}<br />　　<br />　　<b>显示名称</b><br />　　<br />　　显示名称仅是简单地组合了本土化语言,国家和刚才演示的方言.方法的形式如下:<br />　　public final String getDisplayName()<br />　　public final String getDisplayName(Locale targetLocale)<br />　　<br />　　跟locale的toString()方法有所不同.toString()方法是串联起来各个部分并且以下划线分割.<br />　　getDisplayName()方法以括号分割各个部分.<br />　　<br />　　Locale deLocale = Locale.GERMANY;<br />　　// default en_US<br />　　String defaultCountry = deLocale.getDisplayName();<br />　　// target de_DE<br />　　String targetCountry = deLocale.getDisplayName(deLocale);<br />　　System.out.println(defaultCountry);<br />　　System.out.println(targetCountry);<br />　　<br />　　输出结果如下:<br />　　German (Germany)<br />　　Deutsch (Deutschland)<br />　　<br />　　<b>总结</b><br />　　<br />　　locale是一门语言,一个可选国家(或者一个地区)或者是一个可选方言代号的标识符.locale对象提供了几个方法来获取关于locale状态的信息.尽管locale本身不包含许多功能,但是区域敏感型对象依赖于locale来指示行为.区域敏感型对象用locale来定制它们的行为以此满足用户所想要的.<br />　　<br />　　JAVA平台上,每个区域敏感型对象负责它自身区域相关的行为.设计上,区域敏感型对象之间互不相干.这就是说,在一个类里被支持的locale并不一定要跟另一个类里被支持的lcoale一样.<br />　　<br />　　在传统的操作系统上和区域模型里,在同一时间里仅有一个locale的设置起作用.这些系统上,在你通过编程来设置locale后,所有的区域敏感型的功能都会使用指定的locale,这个locale犹如一个全局locale一样贯穿于应用程序.当以setLocale方法或者类似的调用使另一个全局locale起作用后才发生改变.JAVA平台上,由于处理locale时有些不同,所以一个JAVA应用程序里在同一时间可有多个locale起作用.配合格式对象使用多种loalce让开发者有机会为多语言和多文化环境的应用程序创建复杂的必要的组合. <img src ="http://www.blogjava.net/cader/aggbug/57001.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/cader/" target="_blank">负人博客</a> 2006-07-06 17:48 <a href="http://www.blogjava.net/cader/articles/57001.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>