﻿<?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-My Rhythm-随笔分类-J2se</title><link>http://www.blogjava.net/redcoatjk/category/48589.html</link><description>万物皆对象 万事归节奏</description><language>zh-cn</language><lastBuildDate>Fri, 27 May 2011 21:36:58 GMT</lastBuildDate><pubDate>Fri, 27 May 2011 21:36:58 GMT</pubDate><ttl>60</ttl><item><title>[转]java中hashcode()和equals()的详解</title><link>http://www.blogjava.net/redcoatjk/archive/2011/05/27/351200.html</link><dc:creator>redcoatjk</dc:creator><author>redcoatjk</author><pubDate>Fri, 27 May 2011 10:15:00 GMT</pubDate><guid>http://www.blogjava.net/redcoatjk/archive/2011/05/27/351200.html</guid><wfw:comment>http://www.blogjava.net/redcoatjk/comments/351200.html</wfw:comment><comments>http://www.blogjava.net/redcoatjk/archive/2011/05/27/351200.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/redcoatjk/comments/commentRss/351200.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/redcoatjk/services/trackbacks/351200.html</trackback:ping><description><![CDATA[摘自:<div>http://www.iteye.com/topic/257191</div><br />-----------------<br /><div>今天下午研究了半天hashcode()和equals()方法，终于有了一点点的明白，写下来与大家分享（zhaoxudong 2008.10.23晚21.36）。  <br />1. 首先equals()和hashcode()这两个方法都是从object类中继承过来的。  <br />equals()方法在object类中定义如下：  <br />&nbsp; public boolean equals(Object obj) {  <br />return (this == obj);  <br />}  <br />很明显是对两个对象的地址值进行的比较（即比较引用是否相同）。但是我们必需清楚，当String  、Math、还有Integer、Double。。。。等这些封装类在使用equals()方法时，已经覆盖了object类的equals（）方法。比 如在String类中如下：  <br />&nbsp; public boolean equals(Object anObject) {  <br />if (this == anObject) {  <br />&nbsp;&nbsp;&nbsp; return true;  <br />}  <br />if (anObject instanceof String) {  <br />&nbsp;&nbsp;&nbsp; String anotherString = (String)anObject;  <br />&nbsp;&nbsp;&nbsp; int n = count;  <br />&nbsp;&nbsp;&nbsp; if (n == anotherString.count) {  <br />char v1[] = value;  <br />char v2[] = anotherString.value;  <br />int i = offset;  <br />int j = anotherString.offset;  <br />while (n-- != 0) {  <br />&nbsp;&nbsp;&nbsp; if (v1[i++] != v2[j++])  <br />return false;  <br />}  <br />return true;  <br />&nbsp;&nbsp;&nbsp; }  <br />}  <br />return false;  <br />}  <br />很明显，这是进行的内容比较，而已经不再是地址的比较。依次类推Double、Integer、Math。。。。等等这些类都是重写了equals()方法的，从而进行的是内容的比较。当然了基本类型是进行值的比较，这个没有什么好说的。  <br />我们还应该注意，Java语言对equals()的要求如下，这些要求是必须遵循的：  <br />&#8226; 对称性：如果x.equals(y)返回是&#8220;true&#8221;，那么y.equals(x)也应该返回是&#8220;true&#8221;。  <br />&#8226; 反射性：x.equals(x)必须返回是&#8220;true&#8221;。  <br />&#8226; 类推性：如果x.equals(y)返回是&#8220;true&#8221;，而且y.equals(z)返回是&#8220;true&#8221;，那么z.equals(x)也应该返回是&#8220;true&#8221;。  <br />&#8226; 还有一致性：如果x.equals(y)返回是&#8220;true&#8221;，只要x和y内容一直不变，不管你重复x.equals(y)多少次，返回都是&#8220;true&#8221;。  <br />&#8226; 任何情况下，x.equals(null)，永远返回是&#8220;false&#8221;；x.equals(和x不同类型的对象)永远返回是&#8220;false&#8221;。  <br />以上这五点是重写equals()方法时，必须遵守的准则，如果违反会出现意想不到的结果，请大家一定要遵守。  <br />2. 其次是hashcode() 方法，在object类中定义如下：  <br />&nbsp; public native int hashCode();  <br />说明是一个本地方法，它的实现是根据本地机器相关的。当然我们可以在自己写的类中覆盖hashcode()方法，比如String、 Integer、Double。。。。等等这些类都是覆盖了hashcode()方法的。例如在String类中定义的hashcode()方法如下：  <br />&nbsp;&nbsp;&nbsp; public int hashCode() {  <br />int h = hash;  <br />if (h == 0) {  <br />&nbsp;&nbsp;&nbsp; int off = offset;  <br />&nbsp;&nbsp;&nbsp; char val[] = value;  <br />&nbsp;&nbsp;&nbsp; int len = count;  <br /> <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; len; i++) {  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; h = 31*h + val[off++];  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hash = h;  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return h;  <br />}  <br />解释一下这个程序（String的API中写到）：  <br />s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]  <br />使用 int 算法，这里 s[i] 是字符串的第 i 个字符，n 是字符串的长度，^ 表示求幂。（空字符串的哈希码为 0。）  <br /> <br />3.这里我们首先要明白一个问题：  <br />equals()相等的两个对象，hashcode()一定相等；  <br />equals（）不相等的两个对象，却并不能证明他们的hashcode()不相等。换句话说，equals()方法不相等的两个对象，hashcode()有可能相等。（我的理解是由于哈希码在生成的时候产生冲突造成的）。  <br />反过来：hashcode()不等，一定能推出equals()也不等；hashcode()相等，equals()可能相等，也可能不等。解释 下第3点的使用范围，我的理解是在object、String等类中都能使用。在object类中，hashcode()方法是本地方法，返回的是对象的 地址值，而object类中的equals()方法比较的也是两个对象的地址值，如果equals()相等，说明两个对象地址值也相等，当然 hashcode()也就相等了；在String类中，equals()返回的是两个对象内容的比较，当两个对象内容相等时，  <br />Hashcode()方法根据String类的重写（第2点里面已经分析了）代码的分析，也可知道hashcode()返回结果也会相等。以此类 推，可以知道Integer、Double等封装类中经过重写的equals()和hashcode()方法也同样适合于这个原则。当然没有经过重写的 类，在继承了object类的equals()和hashcode()方法后，也会遵守这个原则。  <br /> <br />4.谈到hashcode()和equals()就不能不说到hashset,hashmap,hashtable中的使用，具体是怎样呢，请看如下分析：  <br />Hashset是继承Set接口，Set接口又实现Collection接口，这是层次关系。那么hashset是根据什么原理来存取对象的呢？  <br />在hashset中不允许出现重复对象，元素的位置也是不确定的。在hashset中又是怎样判定元素是否重复的呢？这就是问题的关键所在，经过一下午的查询求证终于获得了一点启示，和大家分享一下，在java的集合中，判断两个对象是否相等的规则是：  <br />1)，判断两个对象的hashCode是否相等  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果不相等，认为两个对象也不相等，完毕  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果相等，转入2) <br />（这一点只是为了提高存储效率而要求的，其实理论上没有也可以，但如果没有，实际使用时效率会大大降低，所以我们这里将其做为必需的。后面会重点讲到这个问题。）  <br />2)，判断两个对象用equals运算是否相等  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果不相等，认为两个对象也不相等  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果相等，认为两个对象相等（equals()是判断两个对象是否相等的关键）  <br />为什么是两条准则，难道用第一条不行吗？不行，因为前面已经说了，hashcode()相等时，equals()方法也可能不等，所以必须用第2条准则进行限制，才能保证加入的为非重复元素。  <br />比如下面的代码：  <br /> <br />public static void main(String args[]){  <br />String s1=new String("zhaoxudong");  <br />String s2=new String("zhaoxudong");  <br />System.out.println(s1==s2);//false  <br />System.out.println(s1.equals(s2));//true  <br />System.out.println(s1.hashCode());//s1.hashcode()等于s2.hashcode()  <br />System.out.println(s2.hashCode());  <br />Set hashset=new HashSet();  <br />hashset.add(s1);  <br />hashset.add(s2);  <br />/*实质上在添加s1,s2时，运用上面说到的两点准则，可以知道hashset认为s1和s2是相等的，是在添加重复元素，所以让s2覆盖了s1;*/  <br />Iterator it=hashset.iterator();  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(it.hasNext())  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(it.next());  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  <br />最后在while循环的时候只打印出了一个&#8221;zhaoxudong&#8221;。  <br />输出结果为：false  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; true  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -967303459  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -967303459  <br />这是因为String类已经重写了equals()方法和hashcode()方法，所以在根据上面的第1.2条原则判定时，hashset认为它们是相等的对象，进行了重复添加。  <br />但是看下面的程序：  <br />import java.util.*;  <br />public class HashSetTest  <br />{  <br />&nbsp;&nbsp; public static void main(String[] args)  <br />&nbsp;&nbsp;&nbsp; {  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HashSet hs=new HashSet();  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hs.add(new Student(1,"zhangsan"));  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hs.add(new Student(2,"lisi"));  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hs.add(new Student(3,"wangwu"));  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hs.add(new Student(1,"zhangsan"));  <br />&nbsp;  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Iterator it=hs.iterator();  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(it.hasNext())  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(it.next());  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  <br />&nbsp;&nbsp;&nbsp;&nbsp; }  <br />}  <br />class Student  <br />&nbsp;&nbsp; {  <br />&nbsp;&nbsp;&nbsp;&nbsp; int num;  <br />&nbsp;&nbsp;&nbsp;&nbsp; String name;  <br />&nbsp;&nbsp;&nbsp;&nbsp; Student(int num,String name)  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.num=num;  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.name=name;  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public String toString()  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return num+":"+name;  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  <br />输出结果为：  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1:zhangsan  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1:zhangsan  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3:wangwu  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2:lisi  <br />问题出现了，为什么hashset添加了相等的元素呢，这是不是和hashset的原则违背了呢？回答是：没有  <br />因为在根据hashcode()对两次建立的new  Student(1,"zhangsan")对象进行比较时，生成的是不同的哈希码值，所以hashset把他当作不同的对象对待了，当然此时的 equals()方法返回的值也不等（这个不用解释了吧）。那么为什么会生成不同的哈希码值呢？上面我们在比较s1和s2的时候不是生成了同样的哈希码 吗？原因就在于我们自己写的Student类并没有重新自己的hashcode()和equals()方法，所以在比较时，是继承的object类中的 hashcode()方法，呵呵，各位还记得object类中的hashcode()方法比较的是什么吧！！  <br />它是一个本地方法，比较的是对象的地址（引用地址），使用new方法创建对象，两次生成的当然是不同的对象了（这个大家都能理解吧。。。），造成 的结果就是两个对象的hashcode()返回的值不一样。所以根据第一个准则，hashset会把它们当作不同的对象对待，自然也用不着第二个准则进行 判定了。那么怎么解决这个问题呢？？  <br />答案是：在Student类中重新hashcode()和equals()方法。  <br />例如：  <br />&nbsp; class Student  <br />{  <br />int num;  <br />String name;  <br />Student(int num,String name)  <br />{  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.num=num;  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.name=name;  <br />}  <br />public int hashCode()  <br />{  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return num*name.hashCode();  <br />}  <br />public boolean equals(Object o)  <br />{  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Student s=(Student)o;  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return num==s.num &amp;&amp; name.equals(s.name);  <br />}  <br />public String toString()  <br />{  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return num+":"+name;  <br />}  <br />}  <br />根据重写的方法，即便两次调用了new Student(1,"zhangsan")，我们在获得对象的哈希码时，根据重写的方法hashcode()，获得的哈希码肯定是一样的（这一点应该没有疑问吧）。  <br />当然根据equals()方法我们也可判断是相同的。所以在向hashset集合中添加时把它们当作重复元素看待了。所以运行修改后的程序时，我们会发现运行结果是：  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1:zhangsan  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3:wangwu  <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2:lisi  <br />可以看到重复元素的问题已经消除。  <br />关于在hibernate的pojo类中，重新equals()和hashcode()的问题：  <br />1)，重点是equals，重写hashCode只是技术要求（为了提高效率）  <br />2)，为什么要重写equals呢，因为在java的集合框架中，是通过equals来判断两个对象是否相等的  <br />3)，在hibernate中，经常使用set集合来保存相关对象，而set集合是不允许重复的。我们再来谈谈前面提到在向hashset集合中添加元素时,怎样判断对象是否相同的准则，前面说了两条，其实只要重写equals()这一条也可以。  <br />但当hashset中元素比较多时，或者是重写的equals()方法比较复杂时，我们只用equals()方法进行比较判断，效率也会非常低， 所以引入了hashcode()这个方法，只是为了提高效率，但是我觉得这是非常有必要的（所以我们在前面以两条准则来进行hashset的元素是否重复 的判断）。  <br />比如可以这样写：  <br />public int hashCode(){  <br />&nbsp;&nbsp; return&nbsp; 1;}//等价于hashcode无效  <br />这样做的效果就是在比较哈希码的时候不能进行判断，因为每个对象返回的哈希码都是1，每次都必须要经过比较equals()方法后才能进行判断是否重复，这当然会引起效率的大大降低。  <br />我有一个问题，如果像前面提到的在hashset中判断元素是否重复的必要方法是equals()方法（根据网上找到的观点），但是这里并没有涉及到关于哈希表的问题，可是这个集合却叫hashset，这是为什么？？  <br />我想，在hashmap,hashtable中的存储操作，依然遵守上面的准则。所以这里不再多说。这些是今天看书，网上查询资料，自己总结出来 的，部分代码和语言是引述，但是千真万确是自己总结出来的。有错误之处和不详细不清楚的地方还请大家指出，我也是初学者，所以难免会有错误的地方，希望大 家共同讨论。        </div><img src ="http://www.blogjava.net/redcoatjk/aggbug/351200.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/redcoatjk/" target="_blank">redcoatjk</a> 2011-05-27 18:15 <a href="http://www.blogjava.net/redcoatjk/archive/2011/05/27/351200.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java各种数据类型转换</title><link>http://www.blogjava.net/redcoatjk/archive/2011/02/21/344756.html</link><dc:creator>redcoatjk</dc:creator><author>redcoatjk</author><pubDate>Mon, 21 Feb 2011 05:34:00 GMT</pubDate><guid>http://www.blogjava.net/redcoatjk/archive/2011/02/21/344756.html</guid><wfw:comment>http://www.blogjava.net/redcoatjk/comments/344756.html</wfw:comment><comments>http://www.blogjava.net/redcoatjk/archive/2011/02/21/344756.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/redcoatjk/comments/commentRss/344756.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/redcoatjk/services/trackbacks/344756.html</trackback:ping><description><![CDATA[<span style="line-height: 1.3em"><span style="font-size: 14pt"><span style="line-height: 1.3em">各种数字类型转换成字符串型： </span><wbr><br />
<br />
<span style="line-height: 1.3em">String s = String.valueOf( value); // 其中 value 为任意一种数字类型。 </span><wbr><br />
<br />
<span style="line-height: 1.3em">字符串型转换成各种数字类型： </span><wbr><br />
<br />
<span style="line-height: 1.3em">String s = "169"; </span><wbr><br />
<span style="line-height: 1.3em">byte b = Byte.parseByte( s ); </span><wbr><br />
<span style="line-height: 1.3em">short t = Short.parseShort( s ); </span><wbr><br />
<span style="line-height: 1.3em">int i = Integer.parseInt( s ); </span><wbr><br />
<span style="line-height: 1.3em">long l = Long.parseLong( s ); </span><wbr><br />
<span style="line-height: 1.3em">Float f = Float.parseFloat( s ); </span><wbr><br />
<span style="line-height: 1.3em">Double d = Double.parseDouble( s ); </span><wbr><br />
<br />
<span style="line-height: 1.3em">数字类型与数字类对象之间的转换： </span><wbr><br />
<br />
<span style="line-height: 1.3em">byte b = 169; </span><wbr><br />
<span style="line-height: 1.3em">Byte bo = new Byte( b ); </span><wbr><br />
<span style="line-height: 1.3em">b = bo.byteValue(); </span><wbr><br />
<br />
<span style="line-height: 1.3em">short t = 169; </span><wbr><br />
<span style="line-height: 1.3em">Short to = new Short( t ); </span><wbr><br />
<span style="line-height: 1.3em">t = to.shortValue(); </span><wbr><br />
<br />
<span style="line-height: 1.3em">int i = 169; </span><wbr><br />
<span style="line-height: 1.3em">b = bo.byteValue(); </span><wbr><br />
<br />
<span style="line-height: 1.3em">short t = 169; </span><wbr><br />
<span style="line-height: 1.3em">Short to = new Short( t ); </span><wbr><br />
<span style="line-height: 1.3em">t = to.shortValue(); </span><wbr><br />
<br />
<span style="line-height: 1.3em">int i = 169; </span><wbr><br />
<span style="line-height: 1.3em">Integer io = new Integer( i ); </span><wbr><br />
<span style="line-height: 1.3em">i = io.intValue(); </span><wbr><br />
<br />
<span style="line-height: 1.3em">long l = 169; </span><wbr><br />
<span style="line-height: 1.3em">Long lo = new Long( l ); </span><wbr><br />
<span style="line-height: 1.3em">l = lo.longValue(); </span><wbr><br />
<br />
<span style="line-height: 1.3em">float f = 169f; </span><wbr><br />
<span style="line-height: 1.3em">Float fo = new Float( f ); </span><wbr><br />
<span style="line-height: 1.3em">f = fo.floatValue(); </span><wbr><br />
<br />
<span style="line-height: 1.3em">double d = 169f; </span><wbr><br />
<span style="line-height: 1.3em">Double dObj = new Double( d ); </span><wbr><br />
<span style="line-height: 1.3em">d = dObj.doubleValue();</span><wbr> </span></span>
 <img src ="http://www.blogjava.net/redcoatjk/aggbug/344756.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/redcoatjk/" target="_blank">redcoatjk</a> 2011-02-21 13:34 <a href="http://www.blogjava.net/redcoatjk/archive/2011/02/21/344756.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]J2SE 5.0专题 之 语言特性</title><link>http://www.blogjava.net/redcoatjk/archive/2010/05/16/321093.html</link><dc:creator>redcoatjk</dc:creator><author>redcoatjk</author><pubDate>Sun, 16 May 2010 07:35:00 GMT</pubDate><guid>http://www.blogjava.net/redcoatjk/archive/2010/05/16/321093.html</guid><wfw:comment>http://www.blogjava.net/redcoatjk/comments/321093.html</wfw:comment><comments>http://www.blogjava.net/redcoatjk/archive/2010/05/16/321093.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/redcoatjk/comments/commentRss/321093.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/redcoatjk/services/trackbacks/321093.html</trackback:ping><description><![CDATA[概述<br />
J2SE(TM) 5.0引入了很多激进的语言元素变化，这些变化或多或少减轻了我们开发人员的一些编码负担，其中的大部分也必然会被应用到即将发布的<a href="http://www.csai.cn/incsearch/search.asp?key=J2EE" target="_blank">J2EE</a>(TM) 5.0中。主要的新特性包括：
<p>　　&#183; 泛型</p>
<p>　　&#183; 增强的for循环</p>
<p>　　&#183; 自动装箱和自动拆箱</p>
<p>　　&#183; 类型安全的枚举</p>
<p>　　&#183; 可变长度参数</p>
<p>　　&#183; 静态引入</p>
<p>　　&#183; 元数据(注解)</p>
<p>　　&#183; C风格的格式化输出</p>
<p>　　这当中，泛型、枚举和注解可能会占用较大的篇幅，而其余的因为用法直截了当，抑或相对简单，我就稍作介绍，剩下的留给读者去思考、去探索了。</p>
<p>　　1.4. 泛型</p>
<p>　　泛型这个题目相当大，大到完全可以就这个话题写一本书。有关Java是否需要泛型和如何实现泛型的讨论也早就在Java社群广为流传。终于，我们在J2SE(TM) 5.0中看到了它。也许目前Java对泛型的支持还算不上足够理想，但这一特性的添加也经足以让我们欣喜一阵了。</p>
<p>　　在接下来的介绍中，我们会了解到：Java的泛型虽然跟<a href="http://www.csai.cn/incsearch/search.asp?key=C%2B%2B" target="_blank">C++</a>的泛型看上去十分相似，但其实有着相当大的区别，有些细节的东西也相当复杂(至少很多地方会跟我们的直觉背道而驰)。可以这样说，泛型的引入在很大程度上增加了Java语言的复杂度，对初学者尤其是个挑战。下面我们将一点一点往里挖。</p>
<p>　　首先我们来看一个简单的使用泛型类的例子：</p>
<p>&nbsp;<strong>&nbsp;&nbsp; </strong>ArrayList&lt;Integer&gt; aList = new ArrayList&lt;Integer&gt;(); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>aList.add(new Integer(1)); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>// ... <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>Integer myInteger = aList.get(0); </p>
<p>　　我们可以看到，在这个简单的例子中，我们在定义aList的时候指明了它是一个直接受Integer类型的ArrayList，当我们调用aList.get(0)时，我们已经不再需要先显式的将结果转换成Integer，然后再赋值给myInteger了。而这一步在早先的Java版本中是必须的。也许你在想，在使用Collection时节约一些类型转换就是Java泛型的全部吗?远不止。单就这个例子而言，泛型至少还有一个更大的好处，那就是使用了泛型的容器类变得更加健壮：早先，Collection接口的get()和Iterator接口的next()方法都只能返回Object类型的结果，我们可以把这个结果强制转换成任何Object的子类，而不会有任何编译期的错误，但这显然很可能带来严重的运行期错误，因为在代码中确定从某个Collection中取出的是什么类型的对象完全是调用者自己说了算，而调用者也许并不清楚放进Collection的对象具体是什么类的;就算知道放进去的对象&#8220;应该&#8221;是什么类，也不能保证放到Collection的对象就一定是那个类的实例。现在有了泛型，只要我们定义的时候指明该Collection接受哪种类型的对象，编译器可以帮我们避免类似的问题溜到产品中。我们在实际工作中其实已经看到了太多的ClassCastException，不是吗?</p>
<p>　　泛型的使用从这个例子看也是相当易懂。我们在定义ArrayList时，通过类名后面的&lt;&gt;括号中的值指定这个ArrayList接受的对象类型。在编译的时候，这个ArrayList会被处理成只接受该类或其子类的对象，于是任何试图将其他类型的对象添加进来的语句都会被编译器拒绝。</p>
<p>　　那么泛型是怎样定义的呢?看看下面这一段示例代码：(其中用E代替在实际中将会使用的类名，当然你也可以使用别的名称，习惯上在这里使用大写的E，表示Collection的元素。)</p>
<p>&nbsp;<strong>&nbsp;&nbsp; </strong>public class TestGenerics&lt;E&gt; { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>Collection&lt;E&gt; col; <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public void doSth(E elem) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>col.add(elem); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>// ... <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>}&nbsp;<br />
&nbsp;<strong>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</strong>在泛型的使用中，有一个很容易有的误解，那就是既然Integer是从Object派生出来的，那么ArrayList&lt;Integer&gt;当然就是ArrayList&lt;Object&gt;的子类。真的是这样吗？我们仔细想一想就会发现这样做可能会带来的问题：如果我们可以把ArrayList&lt;Integer&gt;向上转型为ArrayList&lt;Object&gt;，那么在往这个转了型以后的ArrayList中添加对象的时候，我们岂不是可以添加任何类型的对象（因为Object是所有对象的公共父类）？这显然让我们的ArrayList&lt;Integer&gt;失去了原本的目的。于是Java编译器禁止我们这样做。那既然是这样，ArrayList&lt;Integer&gt;以及ArrayList&lt;String&gt;、ArrayList&lt;Double&gt;等等有没有公共的父类呢？有，那就是ArrayList&lt;?&gt;。?在这里叫做通配符。我们为了缩小通配符所指代的范围，通常也需要这样写：ArrayList&lt;? extends SomeClass&gt;，这样写的含义是定义这样一个类ArrayList，比方说SomeClass有SomeExtendedClass1和SomeExtendedClass2这两个子类，那么ArrayList&lt;? extends SomeClass&gt;就是如下几个类的父类：ArrayList&lt;SomeClass&gt;、ArrayList&lt;SomeExtendedClass1&gt;和ArrayList&lt;SomeExtendedClass2&gt;。&nbsp;<br />
<br />
&nbsp;<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</strong>接下来我们更进一步：既然ArrayList&lt;? extends SomeClass&gt;是一个通配的公用父类，那么我们可不可以往声明为ArrayList&lt;? extends SomeClass&gt;的ArrayList实例中添加一个SomeExtendedClass1的对象呢？答案是不能。甚至你不能添加任何对象。为什么？因为ArrayList&lt;? extends SomeClass&gt;实际上代表了所有ArrayList&lt;SomeClass&gt;、ArrayList&lt;SomeExtendedClass1&gt;和ArrayList&lt;SomeExtendedClass2&gt;三种ArrayList，甚至包括未知的接受SomeClass其他子类对象的ArrayList。我们拿到一个定义为ArrayList&lt;? extends SomeClass&gt;的ArrayList的时候，我们并不能确定这个ArrayList具体是使用哪个类作为参数定义的，因此编译器也无法让这段代码编译通过。举例来讲，如果我们想往这个ArrayList中放一个SomeExtendedClass2的对象，我们如何保证它实际上不是其他的如ArrayList&lt;SomeExtendedClass1&gt;，而就是这个ArrayList&lt;SomeExtendedClass2&gt;呢？（还记得吗？ArrayList&lt;Integer&gt;并非ArrayList&lt;Object&gt;的子类。）怎么办？我们需要使用泛型方法。泛型方法的定义类似下面的例子： <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public static &lt;T extends SomeClass&gt; void add (Collection&lt;T&gt; c, T elem) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>c.add(elem); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} </p>
<p>　　其中T代表了我们这个方法期待的那个最终的具体的类，相关的声明必须放在方法签名中紧靠返回类型的位置之前。在本例中，它可以是SomeClass或者SomeClass的任何子类，其说明<t entends SomeClass>放在void关键字之前(只能放在这里)。这样我们就可以让编译器确信当我们试图添加一个元素到泛型的ArrayList实例中时，可以保证类型安全。</p>
<p>　　Java泛型的最大特点在于它是在语言级别实现的，区别于<a href="http://www.csai.cn/incsearch/search.asp?key=C%23" target="_blank">C#</a> 2.0中的CLR级别。这样的做法使得JRE可以不必做大的调整，缺点是无法支持一些运行时的类型甄别。一旦编译，它就被写死了，能提供的动态能力相当弱。</p>
<p>　　个人认为泛型是这次J2SE(TM) 5.0中引入的最重要的语言元素，给Java语言带来的影响也是最大。举个例子来讲，我们可以看到，几乎所有的Collections API都被更新成支持泛型的版本。这样做带来的好处是显而易见的，那就是减少代码重复(不需要提供多个版本的某一个类或者接口以支持不同类的对象)以及增强代码的健壮性(编译期的类型安全检查)。不过如何才能真正利用好这个特性，尤其是如何实现自己的泛型接口或类供他人使用，就并非那么显而易见了。让我们一起在使用中慢慢积累。</p>
<p>　　1.5. 增强的for循环</p>
<p>　　你是否已经厌倦了每次写for循环时都要写上那些机械的代码，尤其当你需要遍历数组或者Collection，如：(假设在Collection中储存的对象是String类型的)</p>
<p>&nbsp;<strong>&nbsp;&nbsp; </strong>public void showAll (Collection c) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>for (Iterator iter = c.iterator(); iter.hasNext(); ) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>System.out.println((String) iter.next()); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
<br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public void showAll (String[] sa) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>for (int i = 0; i &lt; sa.length; i++) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>System.out.println(sa[i]); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} </p>
<p>　　这样的代码不仅显得臃肿，而且容易出错，我想我们大家在刚开始接触编程时，尤其是C/C++和Java，可能多少都犯过以下类似错误的一种或几种：把for语句的三个表达式顺序弄错;第二个表达式逻辑判断不正确(漏掉一些、多出一些、甚至死循环);忘记移动游标;在循环体内不小心改变了游标的位置等等。为什么不能让编译器帮我们处理这些细节呢?在5.0中，我们可以这样写：</p>
<p>&nbsp;<strong>&nbsp;&nbsp; </strong>public void showAll (Collection c) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>for (Object obj : c) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>System.out.println((String) obj); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
<br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public void showAll (String[] sa) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>for (String str : sa) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>System.out.println(str); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} </p>
<p>　　这样的代码显得更加清晰和简洁，不是吗?具体的语法很简单：使用":"分隔开，前面的部分写明从数组或Collection中将要取出的类型，以及使用的临时变量的名字，后面的部分写上数组或者Collection的引用。加上泛型，我们甚至可以把第一个方法变得更加漂亮：</p>
<p>&nbsp;<strong>&nbsp;&nbsp; </strong>public void showAll (Collection&lt;String&gt; cs) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>for (String str : cs) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>System.out.println(str); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} </p>
<p>　　有没有发现：当你需要将Collection<string>替换成String[]，你所需要做的仅仅是简单的把参数类型"Collection<string>"替换成"String[]"，反过来也是一样，你不完全需要改其他的东西。这在J2SE(TM) 5.0之前是无法想象的。</p>
<p>　　对于这个看上去相当方便的新语言元素，当你需要在循环体中访问游标的时候，会显得很别扭：比方说，当我们处理一个链表，需要更新其中某一个元素，或者删除某个元素等等。这个时候，你无法在循环体内获得你需要的游标信息，于是需要回退到原先的做法。不过，有了泛型和增强的for循环，我们在大多数情况下已经不用去操心那些烦人的for循环的表达式和嵌套了。毕竟，我们大部分时间都不会需要去了解游标的具体位置，我们只需要遍历数组或Collection，对吧?</p>
<p>　　1.6. 自动装箱/自动拆箱</p>
<p>　　所谓装箱，就是把值类型用它们相对应的引用类型包起来，使它们可以具有对象的特质，如我们可以把int型包装成Integer类的对象，或者把double包装成Double，等等。所谓拆箱，就是跟装箱的方向相反，将Integer及Double这样的引用类型的对象重新简化为值类型的数据。</p>
<p>　　在J2SE(TM) 5.0发布之前，我们只能手工的处理装箱和拆箱。也许你会问，为什么需要装箱和拆箱?比方说当我们试图将一个值类型的数据添加到一个Collection中时，就需要先把它装箱，因为Collection的add()方法只接受对象;而当我们需要在稍后将这条数据取出来，而又希望使用它对应的值类型进行操作时，我们又需要将它拆箱成值类型的版本。现在，编译器可以帮我们自动地完成这些必要的步骤。下面的代码我提供两个版本的装箱和拆箱，一个版本使用手工的方式，另一个版本则把这些显而易见的代码交给编译器去完成：</p>
<p>&nbsp;<strong>&nbsp;&nbsp; </strong>public static void manualBoxingUnboxing(int i) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>ArrayList&lt;Integer&gt; aList = new ArrayList&lt;Integer&gt;(); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>aList.add(0, new Integer(i)); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>int a = aList.get(0).intValue(); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>System.out.println("The value of i is " + a); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
<br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public static void autoBoxingUnboxing(int i) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>ArrayList&lt;Integer&gt; aList = new ArrayList&lt;Integer&gt;(); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>aList.add(0, i); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>int a = aList.get(0); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>System.out.println("The value of i is " + a); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} </p>
<p>　　看到了吧，在J2SE(TM) 5.0中，我们不再需要显式的去将一个值类型的数据转换成相应的对象，从而把它作为对象传给其他方法，也不必手工的将那个代表一个数值的对象拆箱为相应的值类型数据，只要你提供的信息足够让编译器确信这些装箱/拆箱后的类型在使用时是合法的：比方讲，如果在上面的代码中，如果我们使用的不是ArrayList<integer>而是ArrayList或者其他不兼容的版本如ArrayList<java.util.date>，会有编译错误。</p>
<p>　　当然，你需要足够重视的是：一方面，对于值类型和引用类型，在<a href="http://www.csai.cn/incsearch/search.asp?key=%D7%CA%D4%B4" target="_blank">资源</a>的占用上有相当大的区别;另一方面，装箱和拆箱会带来额外的开销。在使用这一方便特性的同时，请不要忘记了背后隐藏的这些也许会影响性能的因素。</p>
<p>　　1.7. 类型安全的枚举</p>
<p>　　在介绍J2SE(TM) 5.0中引入的类型安全枚举的用法之前，我想先简单介绍一下这一话题的背景。</p>
<p>　　我们知道，在C中，我们可以定义枚举类型来使用别名代替一个集合中的不同元素，通常是用于描述那些可以归为一类，而又具备有限数量的类别或者概念，如月份、颜色、扑克牌、太阳系的行星、五大洲、四大洋、季节、学科、四则运算符，等等。它们通常看上去是这个样子：</p>
<p>　　typedef enum {SPRING, SUMMER, AUTUMN, WINTER} season;</p>
<p>　　实质上，这些别名被处理成int常量，比如0代表SPRING，1代表SUMMER，以此类推。因为这些别名最终就是int，于是你可以对它们进行四则运算，这就造成了语意上的不明确。</p>
<p>　　Java一开始并没有考虑引入枚举的概念，也许是出于保持Java语言简洁的考虑，但是使用Java的广大开发者对于枚举的需求并没有因为Java本身没有提供而消失，于是出现了一些常见的适用于Java的枚举设计模式，如int enum和typesafe enum，还有不少开源的枚举API和不开源的内部实现。</p>
<p>　　我大致说一下int enum模式和typesafe enum模式。所谓int enum模式就是模仿C中对enum的实现，如：</p>
<p>&nbsp;<strong>&nbsp;&nbsp; </strong>public class Season { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public static final int SPRING = 0; <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public static final int SUMMER = 1; <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public static final int AUTUMN = 2; <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public static final int WINTER = 3; <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>这种模式跟C中的枚举没有太多本质上的区别，C枚举的局限它基本上也有。而typesafe enum模式则要显得健壮得多： <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public class Season { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>private final String name; <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>private Season(String name) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>this.name = name; <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public String toString() { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>return name; <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public static final Season SPRING = new Season("spring"); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public static final Season SUMMER = new Season("summer"); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public static final Season AUTUMN = new Season("autumn"); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public static final Season WINTER = new Season("winter"); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} </p>
<p>　　后一种实现首先通过私有的构造方法阻止了对该类的继承和显式实例化，因而我们只可能取得定义好的四种Season类别，并且提供了方便的toString()方法获取有意义的说明，而且由于这是一个完全意义上的类，所以我们可以很方便的加入自己的方法和逻辑来自定义我们的枚举类。</p>
<p>　　最终，Java决定拥抱枚举，在J2SE(TM) 5.0中，我们看到了这一变化，它所采用的设计思路基本上就是上面提到的typesafe enum模式。它的语法很简单，用一个实际的例子来说，要定义一个枚举，我们可以这样写：</p>
<p>　　public enum Language {CHINESE, ENGLISH, FRENCH, HUNGARIAN}</p>
<p>　　接下来我们就可以通过Language.ENGLISH来使用了。呃&#8230;这个例子是不是有点太小儿科了，我们来看一个复杂点的例子。使用Java的类型安全枚举，我们可以为所有枚举元素定义公用的接口，然后具体到每个元素本身，可以针对这些接口实现一些特定的行为。这对于那些可以归为一类，又希望能通过统一的接口访问的不同操作，将会相当方便。通常，为了实现类似的功能，我们需要自己来维护一套继承关系或者类似的枚举模式。这里借用Java官方网站上的一个例子：</p>
<p>&nbsp;<strong>&nbsp;&nbsp; </strong>public enum Operation { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>PLUS { double eval(double x, double y) { return x + y; } }, <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>MINUS { double eval(double x, double y) { return x - y; } }, <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>TIMES { double eval(double x, double y) { return x * y; } }, <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>DIVIDE { double eval(double x, double y) { return x / y; } }; <br />
<br />
&nbsp;<strong>&nbsp;&nbsp; </strong>// Do arithmetic op represented by this constant <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>abstract double eval(double x, double y); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>在这个枚举中，我们定义了四个元素，分别对应加减乘除四则运算，对于每一种运算，我们都可以调用eval()方法，而具体的方法实现各异。我们可以通过下面的代码来试验上面这个枚举类： <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public static void main(String args[]) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>double x = Double.parseDouble(args[0]); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>double y = Double.parseDouble(args[1]); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>for (Operation op : Operation.values()) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>System.out.println(x + " " + op + " " + y + " = " + op.eval(x, y)); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} </p>
<p>　　怎么样，使用枚举，我们是不是能够很方便的实现一些有趣的功能?其实说穿了，Java的类型安全枚举就是包含了有限数量的已生成好的自身实例的一种类，这些现成的实例可以通过类的静态字段来获取。</p>
<p>　　1.8. 可变长度参数</p>
<p>　　顾名思义，可变长度参数就是指在方法的参数体中，只要定义恰当，我们可以使用任意数量的参数，类似于使用数组。在J2SE(TM) 5.0中，一个新的语法被引入，就是在参数类型名称后面加上"..."，表示该方法可以接受多个该类型的参数。需要说明的是可变长度参数必须放在参数列表的最后，且一个方法只能包含一个这样的参数。在方法体内部，这样的参数被当作数组处理，看上去代码应该类似这个样子：</p>
<p>&nbsp;<strong>&nbsp;&nbsp; </strong>public String testVararg(String... args) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>StringBuilder sb = new StringBuilder(); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>for (String str : args) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>sb.append(str); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>return sb.toString(); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
</p>
<p>　　这样的方法签名跟你写成testVararg(String[] args)的区别在于：在调用时，你不再需要传入一个包装好的String数组，你只需要简单的写一连串String参数，以逗号隔开即可，就如同这个方法正好有一个重载的版本是接受那么多个String参数一样。</p>
<p>　　1.9. 静态引入</p>
<p>　　所谓静态引入就是指除了引入类之外，我们现在又多了一种选择：引入某个类的静态字段。如：</p>
<p>　　import static java.lang.Math.PI;</p>
<p>　　或者</p>
<p>　　import static java.lang.Math.*;</p>
<p>　　这样我们在接下来的代码中，当我们需要使用某个被引入的静态字段时，就不用再写上前面的类名了。当然，出现名字冲突时，跟原来的类引入一样，还是需要前缀以示区分。我个人认为这个新语言元素意义不大。当引入太多静态字段后，代码会变得难以阅读和维护。由于静态字段的名字通常不如类名那么具有描述性，我认为原先在静态字段前写上类名才是更好的选择。不过，毕竟每个人的喜好和需求不同，如果你觉得它对你有用，既然提供了，那么就用咯。</p>
<p>　　1.10. 元数据(注解)</p>
<p>　　注解是J2SE(TM) 5.0引入的重要语言元素，它所对应的JSR是JSR 175，我们先来看看JSR 175的文档对注解的说明：</p>
<p>　　注解不会直接影响程序的语义，而开发和部署工具则可以读取这些注解信息，并作相应处理，如生成额外的Java源代码、XML文档、或者其他将与包含注解的程序一起使用的物件。</p>
<p>　　在之前的J2SE版本中，我们已经使用到了一部分早期的注解元素，如@deprecated等。这些元素通常被用于产生<a href="http://www.csai.cn/incsearch/search.asp?key=HTML" target="_blank">HTML</a>的Javadoc。在J2SE(TM) 5.0中，注解被正式引入，且推到了Java历史上前所未有的高度。</p>
<p>　　现在，注解不仅仅被用来产生Javadoc，更重要的，注解使得代码的编译期检查更加有效和方便，同时也增强了代码的描述能力。有一些注解是随着J2SE(TM) 5.0一起发布的，我们可以直接使用。除此之外，我们也可以很方便的实现自定义的注解。在此基础上，很多以前我们只能靠反射机制来完成的功能也变得更加容易实现。</p>
<p>　　我们来看现成的有哪些有用的注解：</p>
<p>　　首先是@Override，这个注解被使用在方法上，表明这个方法是从其父类继承下来的，这样的写法可以很方便的避免我们在重写继承下来的方法时，不至于不小心写错了方法签名，且悄悄的溜过了编译器，造成隐蔽性相当高的bug。</p>
<p>　　其次是@Deprecated，表明该项(类、字段、方法)不再被推荐使用。</p>
<p>　　还有一个@SuppressWarnings，表明该项(类、字段、方法)所涵盖的范围不需要显示所有的警告信息。这个注解需要提供参数，如unchecked等等。</p>
<p>　　下面我通过一个例子向大家说明这些现成的注解的用法：</p>
<p>&nbsp;<strong>&nbsp;&nbsp; </strong>public class Main { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>@Deprecated <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public String str; <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public static void main(String[] args) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>new SubMain().doSomething(); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public void doSomething() { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>System.out.println("Done."); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
<br />
&nbsp;<strong>&nbsp;&nbsp; </strong>class SubMain extends Main { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>@Override <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>@SuppressWarnings("unchecked", "warning") <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public void doSomething() { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>java.util.ArrayList aList = new java.util.ArrayList(); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>aList.add(new Integer(0)); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>System.out.println("Done by SubMain."); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} </p>
<p>　　当然，我们也完全可以写自己的注解。注解定义的语法是@interface关键字。J2SE(TM) 5.0支持三种形式的注解：不带参数的标记注解、带一个参数的注解和带多个参数的完整注解。下面分别举例说明：</p>
<p>&nbsp;<strong>&nbsp;&nbsp; </strong>标记注解，类似@Deprecated，如： <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>@interface SomeEmptyAnnotation {} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>单个参数的注解，如： <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>@interface MySingleElementAnnotation { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>String value(); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>以及多个参数的注解，如： <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>@interface MyAnnotationForMethods { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>int index(); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>String info(); <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>String developer() default "Sean GAO"; <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} </p>
<p>　　我们可以看到，注解的定义跟interface的定义相当类似，我们还可以指定默认值。对于这些注解，我们也可以为其添加注解，所谓&#8220;注解的注解&#8221;。比方讲，我们通常会使用@Target指定注解的作用对象，以及用@Retention指定注解信息写入的级别，如源代码、类文件等等。举个例子：</p>
<p>&nbsp;<strong>&nbsp;&nbsp; </strong>@Target(ElementType.METHOD) <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>@Retention(RetentionPolicy.SOURCE) <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public @interface SignedMethod { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>在使用时，我们需要在注解名称前面写上@，然后()中指定参数值，如： <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>@MyAnnotationForMethods ( <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>index = 1, <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>info = "This is a method to test MyAnnotation.", <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>developer = "Somebody else" <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>) <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public void testMethod1() { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>// ... <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} </p>
<p>　　注解的最大作用在于它在源代码的基础上增加了有用的信息，使得源代码的描述性更强。这些信息可以被代码之外的工具识别，从而可以很方便的增加外部功能，以及减少不必要的相关代码/文件的维护。这里我想简单提一个超出J2SE(TM) 5.0范畴的话题：在未来的EJB 3.0规范中会有相当多的对注解的应用，让我们预览一下将来的无状态会话bean用注解来定义会是什么样子：</p>
<p>&nbsp;<strong>&nbsp;&nbsp; </strong>@Stateless public class BookShelfManagerBean { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public void addBook(Book aBook) { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>// business logic goes here... <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>public Collection getAllBooks() { <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>// business logic goes here... <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>// ... <br />
&nbsp;<strong>&nbsp;&nbsp; </strong>} </p>
<p>　　我们甚至不用写任何接口和部署描述符，这些工作将完全由外部工具通过读取注解加上反射来完成，这不是很好吗?</p>
<p>　　1.11. C风格格式化输出</p>
<p>　　Java总算也有类似C的printf()风格的方法了，方法名同样叫作printf()，这一特性依赖于前边提到的可变长度参数。举个例子来说，我们现在可以写：</p>
<p>　　System.out.printf("%s has a value of %d.%n", someString, a);</p>
<p>　　怎么样，看上去还不错吧?需要注意的是Java为了支持多平台，新增了%n标示符，作为对\n的补充。有关Java格式化输出的具体语法，请参考java.util.Formatter的API文档。</p>
<p>　　1.12. 结语</p>
<p>　　在这一篇介绍性的文章中，我们一起领略了J2SE 5.0带来的新的语言元素，不知道大家是否也跟笔者一样，感受到了这些新特性在提高我们的开发效率上所作的巨大努力。其实不只是语言元素，J2SE(TM) 5.0的发布在其他很多方面都作了不小的改进，包括虚拟机、新的API类库等等，性能和功能上都有大幅提升。</p>
<p>　　对于主要靠J2EE吃饭的朋友来讲，也许真正意义上要在工作中充分利用这些新的元素，恐怕要等主流的J2EE<a href="http://www.csai.cn/incsearch/search.asp?key=%B7%FE%CE%F1%C6%F7" target="_blank">服务器</a>都支持J2EE(TM) 5.0的那一天了，对此我充满期待。<br />
</p>
 <img src ="http://www.blogjava.net/redcoatjk/aggbug/321093.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/redcoatjk/" target="_blank">redcoatjk</a> 2010-05-16 15:35 <a href="http://www.blogjava.net/redcoatjk/archive/2010/05/16/321093.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java中的double相加的怪事</title><link>http://www.blogjava.net/redcoatjk/archive/2010/03/07/314761.html</link><dc:creator>redcoatjk</dc:creator><author>redcoatjk</author><pubDate>Sun, 07 Mar 2010 09:23:00 GMT</pubDate><guid>http://www.blogjava.net/redcoatjk/archive/2010/03/07/314761.html</guid><wfw:comment>http://www.blogjava.net/redcoatjk/comments/314761.html</wfw:comment><comments>http://www.blogjava.net/redcoatjk/archive/2010/03/07/314761.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/redcoatjk/comments/commentRss/314761.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/redcoatjk/services/trackbacks/314761.html</trackback:ping><description><![CDATA[摘自:http://www.blogjava.net/ghyghost/archive/2008/06/16/208309.html<br />
<span style="font-size: 12pt;">标题 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 在Java中实现浮点数的精确计算 &nbsp; &nbsp; &nbsp; &nbsp; AYellow（原作） &nbsp; 修改 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; 关键字 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Java &nbsp; 浮点数 &nbsp; 精确计算 &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; 问题的提出：&nbsp;&nbsp;&nbsp;<br />
<clk>&nbsp; 编译运行下面这个程序会看到什么？ &nbsp; </clk><br />
&nbsp; public &nbsp; class &nbsp; Test{ &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public &nbsp; static &nbsp; void &nbsp; main(String &nbsp; args[]){ &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; System.out.println(0.05+0.01); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; System.out.println(1.0-0.42); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; System.out.println(4.015*100); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; System.out.println(123.3/100); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; <br />
&nbsp; }; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; 你没有看错！结果确实是 &nbsp; <br />
&nbsp; 0.060000000000000005 &nbsp; <br />
&nbsp; 0.5800000000000001 &nbsp; <br />
&nbsp; 401.49999999999994 &nbsp; <br />
&nbsp; 1.2329999999999999 &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; Java中的简单浮点数类型float和double不能够进行运算。不光是Java，在其它很多编程语言中也有这样的问题。在大多数情况下，计算的结果是准确的，但是多试几次（可以做一个循环）就可以试出类似上面的错误。现在终于理解为什么要有BCD码了。 &nbsp; <br />
&nbsp; 这个问题相当严重，如果你有9.999999999999元，你的计算机是不会认为你可以购买10元的商品的。 &nbsp; <br />
&nbsp; 在有的编程语言中提供了专门的货币类型来处理这种情况，但是Java没有。现在让我们看看如何解决这个问题。 &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; 四舍五入 &nbsp; <br />
&nbsp; 我们的第一个反应是做四舍五入。Math类中的round方法不能设置保留几位小数，我们只能象这样（保留两位）： &nbsp; <br />
&nbsp; public &nbsp; double &nbsp; round(double &nbsp; value){ &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return &nbsp; Math.round(value*100)/100.0; &nbsp; <br />
&nbsp; } &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; 非常不幸，上面的代码并不能正常工作，给这个方法传入4.015它将返回4.01而不是4.02，如我们在上面看到的 &nbsp; <br />
&nbsp; 4.015*100=401.49999999999994 &nbsp; <br />
&nbsp; 因此如果我们要做到精确的四舍五入，不能利用简单类型做任何运算 &nbsp; <br />
&nbsp; java.text.DecimalFormat也不能解决这个问题： &nbsp; <br />
&nbsp; System.out.println(new &nbsp; java.text.DecimalFormat("0.00").format(4.025)); &nbsp; <br />
&nbsp; 输出是4.02 &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; BigDecimal &nbsp; <br />
&nbsp; 在《Effective &nbsp;
Java》这本书中也提到这个原则，float和double只能用来做科学计算或者是工程计算，在商业计算中我们要用
java.math.BigDecimal。BigDecimal一共有4个够造方法，我们不关心用BigInteger来够造的那两个，那么还有两个，
它们是： &nbsp; <br />
&nbsp; BigDecimal(double &nbsp; val) &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Translates &nbsp; a &nbsp; double &nbsp; into &nbsp; a &nbsp; BigDecimal. &nbsp; &nbsp; <br />
&nbsp; BigDecimal(String &nbsp; val) &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Translates &nbsp; the &nbsp; String &nbsp; repre &nbsp; sentation &nbsp; of &nbsp; a &nbsp; BigDecimal &nbsp; into &nbsp; a &nbsp; BigDecimal. &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; 上面的API简要描述相当的明确，而且通常情况下，上面的那一个使用起来要方便一些。我们可能想都不想就用上了，会有什么问题呢？等到出了问题的时候，才发现上面哪个够造方法的详细说明中有这么一段： &nbsp; <br />
&nbsp; Note: &nbsp; the &nbsp; results &nbsp; of &nbsp; this &nbsp; constructor &nbsp; can &nbsp; be &nbsp; somewhat
&nbsp; unpredictable. &nbsp; One &nbsp; might &nbsp; assume &nbsp; that &nbsp; new &nbsp; BigDecimal(.1) &nbsp;
is &nbsp; exactly &nbsp; equal &nbsp; to &nbsp; .1, &nbsp; but &nbsp; it &nbsp; is &nbsp; actually &nbsp; equal &nbsp; to
&nbsp; .1000000000000000055511151231257827021181583404541015625. &nbsp; This &nbsp; is
&nbsp; so &nbsp; because &nbsp; .1 &nbsp; cannot &nbsp; be &nbsp; represented &nbsp; exactly &nbsp; as &nbsp; a &nbsp;
double &nbsp; (or, &nbsp; for &nbsp; that &nbsp; matter, &nbsp; as &nbsp; a &nbsp; binary &nbsp; fraction &nbsp; of
&nbsp; any &nbsp; finite &nbsp; length). &nbsp; Thus, &nbsp; the &nbsp; long &nbsp; value &nbsp; that &nbsp; is &nbsp;
being &nbsp; passed &nbsp; in &nbsp; to &nbsp; the &nbsp; constructor &nbsp; is &nbsp; not &nbsp; exactly &nbsp;
equal &nbsp; to &nbsp; .1, &nbsp; appearances &nbsp; nonwithstanding. &nbsp; &nbsp; <br />
&nbsp; The &nbsp; (String) &nbsp; constructor, &nbsp; on &nbsp; the &nbsp; other &nbsp; hand, &nbsp; is &nbsp;
perfectly &nbsp; predictable: &nbsp; new &nbsp; BigDecimal(".1") &nbsp; is &nbsp; exactly &nbsp;
equal &nbsp; to &nbsp; .1, &nbsp; as &nbsp; one &nbsp; would &nbsp; expect. &nbsp; Therefore, &nbsp; it &nbsp; is &nbsp;
generally &nbsp; recommended &nbsp; that &nbsp; the &nbsp; (String) &nbsp; constructor &nbsp; be &nbsp;
used &nbsp; in &nbsp; preference &nbsp; to &nbsp; this &nbsp; one. &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; 原来我们如果需要精确计算，非要用String来够造BigDecimal不可！在《Effective &nbsp; Java》一书中的例子是用String来够造BigDecimal的，但是书上却没有强调这一点，这也许是一个小小的失误吧。 &nbsp; <br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; 解决方案 &nbsp; <br />
&nbsp; 现在我们已经可以解决这个问题了，原则是使用BigDecimal并且一定要用String来够造。 &nbsp; <br />
&nbsp;
但是想像一下吧，如果我们要做一个加法运算，需要先将两个浮点数转为String，然后够造成BigDecimal，在其中一个上调用add方法，传入另
一个作为参数，然后把运算的结果（BigDecimal）再转换为浮点数。你能够忍受这么烦琐的过程吗？下面我们提供一个工具类Arith来简化操作。它
提供以下静态方法，包括加减乘除和四舍五入： &nbsp; <br />
&nbsp; public &nbsp; static &nbsp; double &nbsp; add(double &nbsp; v1,double &nbsp; v2) &nbsp; <br />
&nbsp; public &nbsp; static &nbsp; double &nbsp; sub(double &nbsp; v1,double &nbsp; v2) &nbsp; <br />
&nbsp; public &nbsp; static &nbsp; double &nbsp; mul(double &nbsp; v1,double &nbsp; v2) &nbsp; <br />
&nbsp; public &nbsp; static &nbsp; double &nbsp; div(double &nbsp; v1,double &nbsp; v2) &nbsp; <br />
&nbsp; public &nbsp; static &nbsp; double &nbsp; div(double &nbsp; v1,double &nbsp; v2,int &nbsp; scale) &nbsp; <br />
&nbsp; public &nbsp; static &nbsp; double &nbsp; round(double &nbsp; v,int &nbsp; scale) &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; 附录 &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; 源文件Arith.java： &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; import &nbsp; java.math.BigDecimal; &nbsp; <br />
&nbsp; /** &nbsp; <br />
&nbsp; &nbsp; * &nbsp; 由于Java的简单类型不能够精确的对浮点数进行运算，这个工具类提供精 &nbsp; <br />
&nbsp; &nbsp; * &nbsp; 确的浮点数运算，包括加减乘除和四舍五入。 &nbsp; <br />
&nbsp; &nbsp; */ &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; public &nbsp; class &nbsp; Arith{ &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //默认除法运算精度 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; private &nbsp; static &nbsp; final &nbsp; int &nbsp; DEF_DIV_SCALE &nbsp; = &nbsp; 10; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //这个类不能实例化 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; private &nbsp; Arith(){ &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /** &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; 提供精确的加法运算。 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @param &nbsp; v1 &nbsp; 被加数 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @param &nbsp; v2 &nbsp; 加数 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @return &nbsp; 两个参数的和 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; */ &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public &nbsp; static &nbsp; double &nbsp; add(double &nbsp; v1,double &nbsp; v2){ &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BigDecimal &nbsp; b1 &nbsp; = &nbsp; new &nbsp; BigDecimal(Double.toString(v1)); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BigDecimal &nbsp; b2 &nbsp; = &nbsp; new &nbsp; BigDecimal(Double.toString(v2)); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return &nbsp; b1.add(b2).doubleValue(); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /** &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; 提供精确的减法运算。 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @param &nbsp; v1 &nbsp; 被减数 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @param &nbsp; v2 &nbsp; 减数 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @return &nbsp; 两个参数的差 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; */ &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public &nbsp; static &nbsp; double &nbsp; sub(double &nbsp; v1,double &nbsp; v2){ &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BigDecimal &nbsp; b1 &nbsp; = &nbsp; new &nbsp; BigDecimal(Double.toString(v1)); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BigDecimal &nbsp; b2 &nbsp; = &nbsp; new &nbsp; BigDecimal(Double.toString(v2)); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return &nbsp; b1.subtract(b2).doubleValue(); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /** &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; 提供精确的乘法运算。 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @param &nbsp; v1 &nbsp; 被乘数 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @param &nbsp; v2 &nbsp; 乘数 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @return &nbsp; 两个参数的积 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; */ &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public &nbsp; static &nbsp; double &nbsp; mul(double &nbsp; v1,double &nbsp; v2){ &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BigDecimal &nbsp; b1 &nbsp; = &nbsp; new &nbsp; BigDecimal(Double.toString(v1)); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BigDecimal &nbsp; b2 &nbsp; = &nbsp; new &nbsp; BigDecimal(Double.toString(v2)); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return &nbsp; b1.multiply(b2).doubleValue(); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /** &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; 小数点以后10位，以后的数字四舍五入。 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @param &nbsp; v1 &nbsp; 被除数 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @param &nbsp; v2 &nbsp; 除数 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @return &nbsp; 两个参数的商 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; */ &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public &nbsp; static &nbsp; double &nbsp; div(double &nbsp; v1,double &nbsp; v2){ &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return &nbsp; div(v1,v2,DEF_DIV_SCALE); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /** &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; 定精度，以后的数字四舍五入。 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @param &nbsp; v1 &nbsp; 被除数 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @param &nbsp; v2 &nbsp; 除数 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @param &nbsp; scale &nbsp; 表示表示需要精确到小数点以后几位。 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @return &nbsp; 两个参数的商 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; */ &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public &nbsp; static &nbsp; double &nbsp; div(double &nbsp; v1,double &nbsp; v2,int &nbsp; scale){ &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(scale&lt;0){ &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw &nbsp; new &nbsp; IllegalArgumentException( &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "The &nbsp; scale &nbsp; must &nbsp; be &nbsp; a &nbsp; positive &nbsp; integer &nbsp; or &nbsp; zero"); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BigDecimal &nbsp; b1 &nbsp; = &nbsp; new &nbsp; BigDecimal(Double.toString(v1)); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BigDecimal &nbsp; b2 &nbsp; = &nbsp; new &nbsp; BigDecimal(Double.toString(v2)); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return &nbsp; b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /** &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; 提供精确的小数位四舍五入处理。 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @param &nbsp; v &nbsp; 需要四舍五入的数字 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @param &nbsp; scale &nbsp; 小数点后保留几位 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * &nbsp; @return &nbsp; 四舍五入后的结果 &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; */ &nbsp; <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public &nbsp; static &nbsp; double &nbsp; round(double &nbsp; v,int &nbsp; scale){ &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(scale&lt;0){ &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw &nbsp; new &nbsp; IllegalArgumentException( &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "The &nbsp; scale &nbsp; must &nbsp; be &nbsp; a &nbsp; positive &nbsp; integer &nbsp; or &nbsp; zero"); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BigDecimal &nbsp; b &nbsp; = &nbsp; new &nbsp; BigDecimal(Double.toString(v)); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BigDecimal &nbsp; one &nbsp; = &nbsp; new &nbsp; BigDecimal("1"); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return &nbsp; b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; <br />
&nbsp; };&nbsp;&nbsp; </span><br />
<br />
 <img src ="http://www.blogjava.net/redcoatjk/aggbug/314761.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/redcoatjk/" target="_blank">redcoatjk</a> 2010-03-07 17:23 <a href="http://www.blogjava.net/redcoatjk/archive/2010/03/07/314761.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]Java接口和Java抽象类</title><link>http://www.blogjava.net/redcoatjk/archive/2009/05/20/271669.html</link><dc:creator>redcoatjk</dc:creator><author>redcoatjk</author><pubDate>Wed, 20 May 2009 02:41:00 GMT</pubDate><guid>http://www.blogjava.net/redcoatjk/archive/2009/05/20/271669.html</guid><wfw:comment>http://www.blogjava.net/redcoatjk/comments/271669.html</wfw:comment><comments>http://www.blogjava.net/redcoatjk/archive/2009/05/20/271669.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/redcoatjk/comments/commentRss/271669.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/redcoatjk/services/trackbacks/271669.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;Java接口和Java抽象类&nbsp;&nbsp;&nbsp; 在没有好好地研习面向对象设计的设计模式之前，我对Java接口和Java抽象类的认识还是很模糊，很不可理解。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 刚学Java语言时，就很难理解为什么要有接口这个概念，虽说是可以实现所谓的多继承，可一个只有方法名，没有方法体的东西，...&nbsp;&nbsp;<a href='http://www.blogjava.net/redcoatjk/archive/2009/05/20/271669.html'>阅读全文</a><img src ="http://www.blogjava.net/redcoatjk/aggbug/271669.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/redcoatjk/" target="_blank">redcoatjk</a> 2009-05-20 10:41 <a href="http://www.blogjava.net/redcoatjk/archive/2009/05/20/271669.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>