3

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

 1 year ago
source link: https://www.cnblogs.com/liumashaonian/p/17107167.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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));
   }
}

输出结果为:

image.png

奇怪!底层是怎么处理的呢?我用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 -

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

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


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK