热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于Integer数值比较的问题以及不可变对象

前言写这篇文章是因为在之前的项目中通过findbugs进行代码优化,爆出的问题。其实我们的代码中暗藏危机,只是没有暴露出来而已我这里使用jdk7测试publicstaticvo

前言

写这篇文章是因为在之前的项目中通过findbugs进行代码优化,爆出的问题。其实我们的代码中暗藏危机,只是没有暴露出来而已

我这里使用jdk7

测试
public static void main(String[] args) {

Integer a = 10;
Integer b = 10;
System.out.println("a == b " + (a == b));
System.out.println("a.equals(b) " + (a.equals(b)));
System.out.println("a.intValue() == b.intValue() " + (a.intValue() == b.intValue()));
System.out.println("a.compareTo(b) " + (a.compareTo(b)));
System.out.println("---------");

a = new Integer(10);
b = new Integer(10);
System.out.println("a == b " + (a == b));
System.out.println("a.equals(b) " + (a.equals(b)));
System.out.println("a.intValue() == b.intValue() " + (a.intValue() == b.intValue()));
System.out.println("a.compareTo(b) " + (a.compareTo(b)));
System.out.println("---------");

a = 189;
b = 189;
System.out.println("a == b " + (a == b));
System.out.println("a.equals(b) " + (a.equals(b)));
System.out.println("a.intValue() == b.intValue() " + (a.intValue() == b.intValue()));
System.out.println("a.compareTo(b) " + (a.compareTo(b)));
System.out.println("---------");

int m = 189;
int n = 189;
System.out.println(m == n);
}

运行结果

a == b  true
a.equals(b) true
a.intValue() == b.intValue() true
a.compareTo(b) 0
---------
a == b false
a.equals(b) true
a.intValue() == b.intValue() true
a.compareTo(b) 0
---------
a == b false
a.equals(b) true
a.intValue() == b.intValue() true
a.compareTo(b) 0
---------
true

奇不奇怪,为什么会酱紫呢?

原因分析

我们都知道,在java中==是比较的两个对象所指向的内存地址是否相等,而equals方法比较的是值。如果按照这个理论,上面的结果中可以看出Integer a = 10;Integer b = 10;这两个对象内存地址是同一个,也就是a和b两个引用指向同一个对象。而Integer a = 189;Integer b = 189;中,a和b两个引用指向了两个不同的对象。真的是这样吗?为什么呢?

我们查看Integer的源码:

/**
* 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= 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[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);
}

/**
* The value of the {@code Integer}.
*
* @serial
*/

private final int value;

/**
* Constructs a newly allocated {@code Integer} object that
* represents the specified {@code int} value.
*
* @param value the value to be represented by the
* {@code Integer} object.
*/

public Integer(int value) {
this.value = value;
}

通过这段源码可知:
- Integer是不可变对象,因为里面的value是final的private final int value;
- -128到127之间的数据放到了IntegerCache中,IntegerCachestatic的,因此将会放到常量池中作为缓存使用

因此可知,Integer a = 10;Integer b = 10;这两个对象其实是从IntegerCache缓存中取的,是同一个对象,地址肯定是相同的。而Integer a = 189;Integer b = 189;是创建的两个新的对象,因此地址肯定不同啦

结论

永远不要用==比较integer对象,以避免一些偶发不可预测的错误。

其他与此相关的知识点

ObjectInteger比较

Object o1 = 10;
Integer o2 = 10;
System.out.println(o1 == o2);

Object o3 = 200;
Integer o4 = 200;
System.out.println(o3 == o4);

结果:

true
false

原因与上面的一样。

* jdk8中上面代码编译可是不通过的吆*

锁相关

我们都知道用于加锁的对象必须是不可变对象, 永远不要再不可变对象上加锁

因此永远不要再integer对象上加锁,因为其实不可变对象private final int value;。当integer重新赋值或者进行了计算以后,得到的值是新创建的对象。

class Stu{
private int a;
public int getA() { return a; }
public void setA(int a) {
this.a = a;
}
}
public class Test {
public static void main(String[] args) {
Stu stu = new Stu();
stu.setA(1);
increase(stu);
increase(stu);

System.out.println(stu.getA());
System.out.println("---");

Integer a = 1;
increase(a);
increase(a);
System.out.println(a);
}
//我们知道java方法传递对象是传引用的
public static void increase(Stu stu){
stu.setA(stu.getA() + 1);
System.out.println(stu.getA());
}
public static void increase(Integer integer){
integer = integer + 1;
System.out.println(integer);//这个integer其实是新创建的一个对象,而不是参数传入进来的对象
}
}

结果

2
3
3
---

2
2
1

推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文讨论了在Spring 3.1中,数据源未能自动连接到@Configuration类的错误原因,并提供了解决方法。作者发现了错误的原因,并在代码中手动定义了PersistenceAnnotationBeanPostProcessor。作者删除了该定义后,问题得到解决。此外,作者还指出了默认的PersistenceAnnotationBeanPostProcessor的注册方式,并提供了自定义该bean定义的方法。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
author-avatar
手机用户2502926053_634
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有