第一次使用FindBugs插件,学习到关于valueOf的东西

前段时间在写一个发送报表邮件的功能时,写了几行很差的代码被带我的同事看到了。(生成报表的代码写的我很烦当时,时间也紧,一不小心就写出烂代码了。。。算了,就是自己比较菜,不为自己辩解了)
大概就是对象与常量比较时,没有把常量放在前面的错误,类似
User.getName().equals("Mike")
(上面这个代码当User的name成员变量为空时会报空指针异常)
对象与常量比较时,必须把常量放在前面,防止对象为空时报空指针异常。要写成类似
if(“name”.equals(obj))…
还有同样的错误如
if(List.size()>0&&List!=null) ...
(上面的代码应该先判断List!=null,否则可能有空指针异常。或者最好直接用工具包里的StringUtils.isNotBlank()判断)
这些问题很多新手都会不注意,是一个代码规范的问题。特别对于java这样讲究协作的工程语言,不规范代码引起的潜在问题很容易累积,然后导致奇怪的bug,让代码很难维护。所以写出符合规范,可维护的代码算Java程序员的基本素质吧。

后来同事给我推荐了一款插件叫FindBugs,说可以检查这样的问题。安装后发现真的好用!检查了一下刚写的代码,提示了下面这个问题:

Method invokes inefficient Number constructor; use static valueOf instead
Using new Integer(int) is guaranteed to always result in a new object whereas Integer.valueOf(int) allows caching of values to be done by the compiler, class library, or JVM. Using of cached values avoids object allocation and the code will be faster.
Values between -128 and 127 are guaranteed to have corresponding cached instances and using valueOf is approximately 3.5 times faster than using constructor. For values outside the constant range the performance of both styles is the same.
Unless the class must be compatible with JVMs predating Java 1.5, use either autoboxing or the valueOf() method when creating instances of Long, Integer, Short, Character, and Byte.

意思就是new Integer(int)这种用法是无效的数字构造,每次使用构造方法都会创建一个新的数字对象。而Integer.valueOf(int)会从缓存里取数字,避免每次都分配对象,速度更快,减少垃圾产生。尤其值在 -128 and 127时会每次用缓存,速度能快3.5倍。除非你的目的就是产生新的对象,否则最好用Integer.valueOf这种方式。

果然一用就有发现问题。那看一下Integer.valueOf()的源码:
(我用的是JDK1.7)

/**
    * Cache to support the object identity semantics of autoboxing for values between
    * -128 and 127 (inclusive) as required by JLS.
    *
    * The cache is initialized on first usage.  The size of the cache
    * may be controlled by the -XX:AutoBoxCacheMax=<size> option.
    * During VM initialization, java.lang.Integer.IntegerCache.high property
    * may be set and saved in the private system properties in the
    * sun.misc.VM class.
    */
   private static class IntegerCache {
       static final int low = -128;
       static final int high;
       static final Integer cache[];

       static {
           // high value may be configured by property
           int h = 127;
           String integerCacheHighPropValue =
               sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
           if (integerCacheHighPropValue != null) {
               int i = parseInt(integerCacheHighPropValue);
               i = Math.max(i, 127);
               // Maximum array size is Integer.MAX_VALUE
               h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
           }
           high = h;

           cache = new Integer[(high - low) + 1];
           int j = low;
           for(int k = 0; k < cache.length; k++)
               cache[k] = new Integer(j++);
       }

       private IntegerCache() {}
   }

   /**
    * Returns an {@code Integer} instance representing the specified
    * {@code int} value.  If a new {@code Integer} instance is not
    * required, this method should generally be used in preference to
    * the constructor {@link #Integer(int)}, as this method is likely
    * to yield significantly better space and time performance by
    * caching frequently requested values.
    *
    * This method will always cache values in the range -128 to 127,
    * inclusive, and may cache other values outside of this range.
    *
    * @param  i an {@code int} value.
    * @return an {@code Integer} instance representing {@code i}.
    * @since  1.5
    */
   public static Integer valueOf(int i) {
       assert IntegerCache.high >= 127;
       if (i >= IntegerCache.low && i <= IntegerCache.high)
           return IntegerCache.cache[i + (-IntegerCache.low)];
       return new Integer(i);
   }

果然如FindBugs中的描述一样,i在-128 到127时(high至少为127,至多是Integer最大值,默认配置127),直接从缓存数组里取。类加载时JVM就会执行IntegerCache 静态类里面的静态代码块,提前把Integer对象都创建好放到cache数组里了,用时候直接取。有意思哈~内部缓存。

所以有关于自动装箱的面试题

publicstaticvoidmain(String[] args) {          
       Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;   
       System.out.println(f1 == f2); 
       System.out.println(f3 == f4);     
} 

这就难怪会f1==f2为true,f3==f4却false了(自动装箱会调用静态方法valueOf)。

感觉这个插件真的好用。对自己养成好的代码习惯一定会很有帮助。(具体怎么安装,根据自己使用的IDE去百度)。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 121,945评论 17 134
  • 总说神马浮云,总觉红尘看破,春来春心荡漾,且去菩提打坐。 本是域外自在物,如今开在双桥下,客来踏青赏春色,可知笑靥...
    碧筠小筑阅读 303评论 1 3