应用
47. 不可变的引用类型
BigInteger total = BigInteger.ZERO;
total.add(new BigInteger("1"));
total.add(new BigInteger("10"));
System.out.println(total);//0
上面程序的结果为11吗?答案是0。
BigInteger实例是不可变的。String、BigDecimal以及包装类型:Integer、Long、Short、Byte、Character、Boolean、Float和Double也是如此。对这些类型的操作将返回新的实例。
不可变类型更容易设计、实现与作用;它们出错的可能性更小,并且更加安全。
本程序修改如下:
BigInteger total = BigInteger.ZERO;
total=total.add(new BigInteger("1"));
total=total.add(new BigInteger("10"));
System.out.println(total);//11
48. 请同时重写equals()与hashCode()
class T {
private String str;
T(String str) {
this.str = str;
}
public boolean equals(Object obj) {
if(!(obj instanceof T)){
return false;
}
T t = (T)obj;
return t.equals(this.str);
}
public static void main(String[] args) {
Set set = new HashSet();
set.add(new T("str"));
System.out.println(set.contains(new T("str")));//false
}
}
上面的程序不会打印true,而是false,为什么?
hashCode约定要求相等的对象要具有相同的散列码。
无论何时,只要你重写了equals方法,你就必须同时重写hashCode方法。
如果将自定的类型对象放入HashSet、HashMap、Hashtable、LinkedHashSet、LinkedHashMap这此散列集合时,一定需要重写equals与hashCode方法,这样在放入进去之后还能查找出来。如果放入其他非散列类型的集合时,其实只需要
重写equals就可以了。
本程序解决办法重写hashCode()方法:
public int hashCode() {
return 37 * this.str.hashCode();
}
49. 日期设置
Calendar c = Calendar.getInstance();
c.set(2010, 12, 31);// 月是从0开始的,11其实表示12月
System.out.println(c.get(Calendar.YEAR) + " " + c.get(Calendar.MONTH));
c = Calendar.getInstance();
c.set(2010, 11, 31);
System.out.println(c.get(Calendar.YEAR) + " " + c.get(Calendar.MONTH));
本程序较简单,只需注意月是从0开始的就可以了,如果你设置月为12,则会自动转换为下一年。
50. IdentityHashMap
class T {
private String str;
T(String str) {
this.str = str;
}
public int hashCode() {
return 37 * this.str.hashCode();
}
public boolean equals(Object obj) {
return this.str.equals(((T) obj).str);
}
public static void put(Map m) {
m.put("str", "1");
/*
* 由于上面程序将 "str" 放入了字符串常量池,
* 所以str是同一个对象,不管是什么样类型的
* Map,即使使用IdentityHashMap都只放入一次
*/
m.put("str", "2");
m.put(new T("str"), "3");
m.put(new T("str"), "4");
}
public static void main(String[] args) {
Map m = new HashMap();
put(m);
System.out.println(m.size());// 2 (2,4)
//IdentityHashMap比较时使用==替换equals()方法
m = new IdentityHashMap();
put(m);
System.out.println(m.size());// 3 (2 3 4)
}
}
52. PrintStream对输出结果的缓冲
public static void main(String[] args) {
String str = "Hello World";
for (int i = 0; i < str.length(); i++) {
System.out.write(str.charAt(i));
}
}
54. 实现Serializable的单例问题
class Dog implements Serializable{
public static final Dog INSTANCE = new Dog();
private Dog(){}
}
上面能控制只生成一个单实例吗?
如果对实现了Serializable的对象进行序列化后,再反序列化,内中会不只一个实例了,因为反序列化时会重新生成一个对象。
既然INSTANCE为静态域,那序列化时返回的对象如果也是INSTANCE就可以解决问题了,而打开API我们发现Serializable接口确实有这样两个特殊的方法描述:
将对象写入流时需要指定要使用的替代对象的可序列化类,应使用准确的签名来实现此特殊方法:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
此 writeReplace 方法将由序列化调用,前提是如果此方法存在,而且它可以通过被序列化对象的类中定义的一个方法访问。因此,该方法可以拥有私有 (private)、受保护的 (protected) 和包私有 (package-private) 访问。子类对此方法的访问遵循 java 访问规则。
在从流中读取类的一个实例时需要指定替代的类应使用的准确签名来实现此特殊方法:
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
此 readResolve 方法遵循与 writeReplace 相同的调用规则和访问规则。
上述两个方法的只要出现,就会履盖以下两个方法(这两个方法本质的意义就是用来替换序列与反序列的对象),虽然会执行它们,但最后得到的结果却是writeReplace、readResolve两个方法写入或读出的对象:
private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in)throws IOException, ClassNotFoundException;
另外,writeObject与readObject需成对实现,而writeReplace与readResolve则不需要成对出现,一般单独使用。如果同时出现这四个方法,最后写入与读出的结果以writeReplace和readResolve方法的结果为准。
所以下要解决真真单实例问题,我们如下修正:
class Dog implements Serializable {
public static final Dog INSTANCE = new Dog();
private Dog() {}
private Object readResolve() {
return INSTANCE;
}
}
public class SerialDog {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
new ObjectOutputStream(bos).writeObject(Dog.INSTANCE);
ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray());
Dog dog = (Dog) new ObjectInputStream(bin).readObject();
System.out.println(dog == Dog.INSTANCE);//true
}
}
一个实现了Serializable的单例类,必须有一个readResolve方法,用以返回它的唯一实例。
55. thread. isInterrupted()与Thread.interrupted()
public class SelfInerruption {
public static void main(String[] args) {
Thread.currentThread().interrupt();
if (Thread.interrupted()) {
// Interruped:false
System.out.println("Interruped:" + Thread.interrupted());
} else {
System.out.println("Not interruped:" + Thread.interrupted());
}
}
}
上面结果走的是第一个分支,但结果却不是Interruped:true?
Thread.interrupted()为Thread的静态方法,调用它首先会返回当前线程的中断状态(如果当前线程上调用了interrupt()方法,则返回true,否则为false),然后再清除当前线程的中断状态,即将中断状态设置为false。换句话说,如果连续两次调用该方法,则第二次调用将返回 false。
而isInterrupted()方法为实例方法,测试线程是否已经中断,并不会清除当前线程中断状态。
所以这里应该使用isInterrupted()实例方法,就可以修复该问题。
ref:http://jiangzhengjun.iteye.com/blog/652771
分享到:
相关推荐
java解惑java解惑java解惑java解惑java解惑java解惑
Java解惑Java解惑Java解惑Java解惑Java解惑Java解惑Java解惑Java解惑Java解惑Java解惑Java解惑Java解惑
Java解惑.pdf Java解惑.pdf Java解惑.pdf Java解惑.pdf
Java解惑中文版 Java解惑 java健壮程序
JAVA解惑.pdf JAVA解惑.pdf JAVA解惑.pdf
你认为自己了解Java多少?你是个爱琢磨的代码侦探吗?你是否曾经花费数天时间去追踪一个由Java或其类库的陷阱和缺陷而导致的bug?你喜欢智力测验吗?本书正好适合你!.. Bloch和Gafter继承了Effective Jaya一书的传统,...
JAVA解惑,你面包括一些java经典的问题。
Java PUZZLE Java 解惑 Java PUZZLE Java 解惑 Java PUZZLE Java 解惑Java PUZZLE Java 解惑 Java PUZZLE Java 解惑 Java PUZZLE Java 解惑
与java相关的的学习,适合初学者,可以看看
《Java解惑》《Java解惑》《Java解惑》《Java解惑》《Java解惑》《Java解惑》
《Java解惑》 布洛克 著;陈昊鹏 译 扫描清晰带目录,仅供参阅,请支持正版
"java解惑" PDF版本
讲述如何在程序中避免程序缺陷和程序陷阱的,解惑的过程中,介绍了一些Java编程语言中许多不易被掌握的知识点,其阅读价值非常高,适合具有Java知识的学习者和有编程经验的Java程序员阅读。
Java解惑(中文).pdf 给大家介绍java中容易迷惑用错的实例
Java解惑,是一本以大量java实例,讲述如何在程序中避免程序缺陷和程序陷阱的,解惑的过程中,介绍了一些Java编程语言中许多不易被掌握的知识点,其阅读价值非常高,适合具有Java知识的学习者和有编程经验的Java...
Java四大名著之一:4,JAVA解惑 高清PDF 下载
该书特写了95个有关Java或其类库的陷阱和缺陷的谜题,其中大多数谜题都采用了短程序的方式,这些程序的行为与其看似的大相径庭。在每个谜题之后都给出了详细的解惑方案,这些解惑方案超越了对程序行为的简单解释,向...
。。。。。 Java解惑(中文) 是一本对 Java一些问题的解答 。。。。。。。。。。
Java解惑,罗列你意想不到的100道java疑惑