Java日常错误及需要注意细节,持续更新......
记录日常工作中一些容易被忽视的错误及细节,持续更新......
一、问题:HashMap<Long, String>中,用get(Integer key)取不到值
Map<Long, String> map = new HashMap<Long, String>(); map.put(1L, "1"); System.err.println(map.get(1));// null System.err.println(map.get(1L));// 1
1.首先想到Long与Integer的hashCode方法不同,Integer-value Long-(int)(value ^ (value >>> 32))
但是!!计算出的hashCode值是相同的,不是问题所在
2.查看HashMap源码:注意加亮部分
先比较key.hash,然后first.key == key || key.equals(first.key)
/** * 先比较key.hash,然后first.key == key || key.equals(first.key) */ final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null) { if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
先看first.key == key:"=="比较地址值,l是Long cache[]中的1,o是Integer cache[]中的1,false
Long l = 1L; Object o = 1; System.err.println(l == o);// false // 反编译后: Long l = Long.valueOf(1L); Object o = Integer.valueOf(1); System.err.println(l == o);
然后看key.equals(first.key):先检查类型,false
//Long的equals方法 public boolean equals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } return false; }
引发新的问题:为什么这个是true?——反编译解决
Long l = 1L; System.err.println(l == 1);// true // 反编译后: Long l = Long.valueOf(1L); System.err.println(l.longValue() == 1L);//编译器直接将1转成1L
二、两个值相等的Integer不“==”
Integer c = 99999; Integer d = 99999; System.out.println(c == d);// false
Integer c = 99999;// 反编译:Integer c = Integer.valueOf(99999);
查看Integer源码:
-128 <= i <= 127时,直接在Integer cache[]中取;否则,new Integer(i)
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
结论:
int a = 99999; int b = 99999; System.err.println(a == b);// true Integer c = 99999; Integer d = 99999; System.out.println(c == d);// false Integer e = 127; Integer f = 127; System.out.println(e == f);// true
三、List.remove()方法调用错误
注意list两个remove方法,remove(int index) remove(Object o)
public static void main(String[] args) { List<Integer> list = new LinkedList<Integer>(); for (int i = 0; i < 9999999; i++) { list.add(i); } // remove(int index) long before = System.currentTimeMillis(); int i = 8888888; list.remove(i); long after = System.currentTimeMillis(); System.err.println("index=" + (after - before));// 6ms // remove(Object o) long before = System.currentTimeMillis(); Integer i = 8888888; list.remove(i); long after = System.currentTimeMillis(); System.err.println("Object=" + (after - before));// 96ms }
四、三目运算符与自动拆装箱
Map<String,Boolean> map = new HashMap<String, Boolean>(); Boolean b = (map!=null ? map.get("test") : false);// Exception in thread "main" java.lang.NullPointerException
查问题:
NullPointerException找不出原因
反编译看: ((Boolean)map.get("test")) == null
HashMap map = new HashMap(); Boolean boolean1 = Boolean.valueOf(map == null ? false : ((Boolean)map.get("test")).booleanValue());
结论:
三目运算符的语法规范,参见 jls-15.25。
三目运算符 当第二,第三位操作数分别为基本类型和对象时,其中的对象就会拆箱为基本类型进行操作。
以后注意:
1、保证三目运算符的第二第三位操作数都为对象类型
Map<String,Boolean> map = new HashMap<String, Boolean>(); Boolean b = (map!=null ? map.get("test") : Boolean.FALSE);
2、自动拆装箱问题
Integer integer = 1; // 装箱 Integer integer=Integer.valueOf(1); new Integer() int i = integer; // 拆箱 int i=integer.intValue();
1)包装对象的数值比较,不能简单的使用==(只有-128到127之间IntegerCache内的数字可以,但是这个范围之外还是需要使用
equals比较)。
2)自动拆箱,如果包装类对象为null,那么自动拆箱时就有可能抛出NPE。
3)如果一个for循环中有大量装箱操作,会浪费很多资源。
五、switch语句忘记break
本来我跟你现在想的一样,一定不会忘,直到遇到了这个问题。
int i = 3; switch (i) { case 1: System.out.println(1); break; case 2: System.out.println(2); break; case 3: System.out.println(3); // 没有break, 不会有问题 }
当你需要在之后接着case的时候,直接复制case 3,就bug了。
(1)case完一定break,除非特别需要穿透到下一个case;
(2)复制代码前后都要检查是否有问题。
- opensocial的细节需要注意哪些?更新中……
- JAVA后台集成支付宝的注意事项,持续更新
- java代码注意点总结(持续更新)
- 数据库查询需要注意的事项(持续更新)
- JAVA变量赋值需要注意的细节问题
- JAVA编程需要注意的细节
- 一些java编程时需要注意的细节
- Java 性能调优需要格外注意的细节
- Java transient关键字使用小结及一些需要注意的细节
- Ubuntu中关于java尤其是Netbeans的注意事项(持续更新)
- easyui使用总结(各种注意的小细节) 持续更新
- (.Net,DevExpress)关于devexpress部分aspx控件需要注意的细节_持续补充ing
- Java transient关键字使用小结及一些需要注意的细节
- Worldwindjava 更新版本需要注意的问题
- Java 相关细节问题(持续更新)
- 那些学完 Java 在学 C 才会犯的错误(持续更新)
- Java transient关键字使用小结及一些需要注意的细节
- WebView使用时需要注意的细节问题(持续总结)
- java的编译错误(持续更新)
- RTSP服务器配置细节注意(持续更新)