Java-Integer好大一坑,一不小心就掉进去了

遛马少年,一个代码写的很6的程序员,专注于技术干货分享

最近,在处理线上bug的时候,发现了一个奇怪的现象

业务代码大概是这样的

public static boolean doSth(Integer x, Integer y) {    if (x == y) {       return true;    }    //do other...    return false; } 

当x、y都是较小的值时,比如100、100,正常返回true

当是较大值时,比如500、500,反而返回false

难道100==100,500!=500吗?

带着这样的疑问,我写了个demo程序一探究竟

public class IntDemo {     public static boolean doSth(Integer a, Integer b) {       if (a == b) {          return true;       }       return false;    }     public static void main(String[] args) {       int a = 100;       int b = 500;       System.out.println(doSth(a, a));       System.out.println(doSth(b, b));    } } 

输出结果为:

Java-Integer好大一坑,一不小心就掉进去了

奇怪!底层是怎么处理的呢?我用javap看了一下上面代码的字节码指令

public class com.integer.IntDemo {   public com.integer.IntDemo();     Code:        0: aload_0        1: invokespecial #1                  // Method java/lang/Object."<init>":()V        4: return    public static boolean doSth(java.lang.Integer, java.lang.Integer);     Code:        0: aload_0        1: aload_1        2: if_acmpne     7        5: iconst_1        6: ireturn        7: iconst_0        8: ireturn    public static void main(java.lang.String[]);     Code:        0: bipush        100        2: istore_1        3: sipush        500        6: istore_2        7: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;       10: iload_1       11: invokestatic  #3                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;       14: iload_1       15: invokestatic  #3                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;       18: invokestatic  #4                  // Method doSth:(Ljava/lang/Integer;Ljava/lang/Integer;)Z       21: invokevirtual #5                  // Method java/io/PrintStream.println:(Z)V       24: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;       27: iload_2       28: invokestatic  #3                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;       31: iload_2       32: invokestatic  #3                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;       35: invokestatic  #4                  // Method doSth:(Ljava/lang/Integer;Ljava/lang/Integer;)Z       38: invokevirtual #5                  // Method java/io/PrintStream.println:(Z)V       41: return } 

可以看到,doSth函数传入的实参是int类型,函数定义的形参却是Integer类型

看到第11行字节码指令我就懂了,原来是通过Integer.valueOf 来做的一个int的自动装箱

11: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

所以,问题肯定出在Integer.valueOf里面,接着,我点开valueOf的源码

public static Integer valueOf(int i) {     if (i >= IntegerCache.low && i <= IntegerCache.high)         return IntegerCache.cache[i + (-IntegerCache.low)];     return new Integer(i); } 

好家伙,这里用到了一个缓存类:IntegerCache

判断如果在缓存范围内,直接返回这个缓存类持有的引用,否则就new一个Integer对象

再点开这个缓存类,low=-128,high=127

这就解释了为什么100是true,500是false了

JDK为什么要设计这样一个很容易掉进去的坑呢?

其实,在valueOf方法上,官方已经给出了说明:

/**  * 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  */ 

大概意思就是,-128~127 的数据在 int 范围内是使用最频繁的,为了减少频繁创建对象带来的内存消耗,这里其实是用到了享元模式,以提高空间和时间性能。

既然Integer这样设计了,其他类会不会也有呢?

接着,我又看了其他数据类型,用缓存的还不少,这里我给各位列一下,防止你们以后踩坑

基本类型 包装类型 缓存范围
boolean Boolean -
byte Byte -128-127
short Short -128-127
int Integer -128-127
long Long -128-127
float Float -
double Double -

小伙伴们在开发过程中,也要注意,避免掉进这个坑里。

好了,今天的分享就到这里了,如果你觉得有用,麻烦给兄弟点个小赞,这样我才更有动力去分享更多技术干货~

发表评论

相关文章