首页 > 编程语言 >Java-Integer好大一坑,一不小心就掉进去了

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

时间:2023-02-09 21:58:36浏览次数:38  
标签:lang Ljava Java java valueOf 一坑 Integer Method

遛马少年,一个代码写的很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 -

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

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

标签:lang,Ljava,Java,java,valueOf,一坑,Integer,Method
From: https://www.cnblogs.com/liumashaonian/p/17107167.html

相关文章

  • Prometheus监控java
    1、使用jmxexporter暴露监控指标:java启动时通过指定参数 -javaagent的形式运行jmxexporter的jar包,进程内读取jvm运行时状态数据,转换为Prometheusmetrics格式,并......
  • javascript 提取字符串方法 slice substr substring
    本文将对javascript提取字符串的三个方法slice/substr/substring,进行分析。这三个方法都具有提取字符串的功能,且都有两个参数。下面将详细介绍三个方法在一些特殊参数值......
  • JavaScript实现数组对象去重
    有多种实现方式:一、使用Set对象:Array.from(newSet(array))该方法会先创建一个Set对象,然后再使用Array.from方法将Set对象转换为数组,因为Set对象不允许有......
  • java中的一些概念
    1. 包的概念   包就是一个文件夹,里面还可以含所有子文件夹。 相当于C++中的命名空间   (1)类中包的声明格式:package包名.包名.包名…;   (2)包中类的访......
  • java到报名的编码运行
    Hello.javapackagea.b;importcom.beyondiary.kit.KitConstant;publicclassHello{publicstaticvoidmain(String[]args){System.out.println(K......
  • 运算符与JavaDoc
    运算符Java语言支持如下运算符:算术运算符:+,-,,/,%,++,--short和byte运算会转化为int%:取余、模运算a%b++--:自增,自减(一元运算)inta=3;......
  • Java常用类之String源码分析
    一、概述String类的一个最大特性是不可修改性,而导致其不可修改的原因是在String内部定义了一个常量数组,因此每次对字符串的操作实际上都会另外分配分配一个新的常量数组空......
  • Java Mysql Time类型 接收显示问题
    最近写项目需要用的mysql中的Time时间类型问题产生原因想要实现在mysql中只存入Time类型,如10:30。这样可以方便存取,在后台接收和显示都比较方便。产生的问题但是这......
  • java8 lambda 求list最大值、最小值、平均值、求和、中位数、属性排序(空指针异常,空值
    java8lambda求list最大值、最小值、平均值、求和、中位数、属性排序(空指针异常,空值排前、排后)、去重importorg.junit.Test;importjava.text.SimpleDateFormat;im......
  • 解读 Java 云原生实践中的内存问题
    作者:风敬(谢文欣)Java凭借着自身活跃的开源社区和完善的生态优势,在过去的二十几年一直是最受欢迎的编程语言之一。步入云原生时代,蓬勃发展的云原生技术释放云计算红利,推动业......