• 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)复制代码前后都要检查是否有问题。

    六、数值溢出问题

        // 为了更好的展示问题,代码举出的是较极端的情况
        public void overFlow(int a) {
            int b = 999999 * a; // 6个9 int最大值=2147483647
            int limit = 999999999; // 9个9
            if (b < limit) {
                System.out.println("a*b小于limit");
            }
        }

    如果a传入一个较大的int值,a*999999之后会超过int的最大值

    而默认结果是int类型,会将a*999999的结果强转成int,导致不是想要的结果的结果

    即使a*999999是大于limit的,强转成int后,b可能会比limit小,甚至可能是负数

    解决:

        // 用long类型计算(还会用一定风险)
        public void overFlow(int a) {
            long b = 999999L * a;
            int limit = 999999999;
            if (b < limit) {
                System.out.println("a*999999小于limit");
            }
        }
        
        // 将加法和乘法转变成减法和除法运算
        public void overFlow(int a) {
            int limit = 999999999;
            if (a < limit/999999) {
                System.out.println("a*999999小于limit");
            }
        }

    七、对象引用问题

    public static void main(String[] args) {
            Map<Integer, Inner> map = new HashMap<Integer, Inner>();
            
            // inner1.list [1]
            Inner inner1 = new Inner(new LinkedList<Integer>());
            inner1.getList().add(1);
            map.put(1, inner1);
            
            // inner2.list [1, 2]
            Inner inner2 = new Inner(map.get(1).getList());
            inner2.getList().add(2);
            map.put(2, inner2);
            
            for (Entry<Integer, Inner> entry : map.entrySet()) {
                System.err.println("" + entry.getKey() + " " + entry.getValue().getList());
            }
            
            /**
             * 目的:inner1.list [1]   inner2.list [1, 2]
             * 结果:inner1.list [1, 2]   inner2.list [1, 2]
             */
        }
    
        static class Inner {
            List<Integer> list;
    
            public List<Integer> getList() {
                return list;
            }
            
            public Inner(List<Integer> list) {
                this.list = list;
            }
        }

    分析:

    1.将inner1.list的引用给了inner2.list,nner1.list inner2.list指向的是同一个List。

    2.很简单的问题,开发时习惯了构造方法里这样写:this.list = list;

    解决:

    this.list = list;  改成   this.list = new LinkedList<Integer>(list);

  • 相关阅读:
    【转】[行业透视] 外企九年-我最终选择放弃
    【转】Win7下有线与无线网络使用优先级
    【转】POJ 1177 Picture(1)
    【转】POJ 1151 Atlantis
    POJ1151Atlantis(扫描线求面积并+线段树+离散化)
    【转】poj_1151(Atlantis)
    【转】poj pku 线段树题目20道汇总+简要算法+分类+难度
    【转】POJ 1151 Atlantis(AC)
    【转】线段树(segment tree)
    【转】poj 1177 pictures(2)
  • 原文地址:https://www.cnblogs.com/hexinwei1/p/9720310.html
Copyright © 2020-2023  润新知