从源码看java中Integer的缓存问题

在开始详细的说明问题之前,我们先看一段代码

1 public static void compare1(){
2         Integer i1 = 127, i2 = 127, i3 = 128, i4 = 128;
3         System.out.println(i1  == i2);
4         System.out.println(i1.equals(i2));
5         System.out.println(i3  == i4);
6         System.out.println(i3.equals(i4));
7     }

这段代码输出的结果是什么呢?

答案是:

是不是感到奇怪呢?为什么127的时候==是true,128的时候就变成了false?其实要回答这个问题不难。

Integer在赋值的时候会发生自动装箱操作,调用Integer的valueOf方法,那么我们看一下java的源码(1.8):

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

这里根据源码可以看出,在传入的i值在IntegerCache.low和IntegerCache.high之间的时候,会返回IntegerCache.cache数组里面的数,不在这个范围才会new一个Integer对象。接下来我们看一下IntegerCache数组的初始化情况:

 1 /**
 2      * Cache to support the object identity semantics of autoboxing for values between
 3      * -128 and 127 (inclusive) as required by JLS.
 4      *
 5      * The cache is initialized on first usage.  The size of the cache
 6      * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
 7      * During VM initialization, java.lang.Integer.IntegerCache.high property
 8      * may be set and saved in the private system properties in the
 9      * sun.misc.VM class.
10      */
11
12     private static class IntegerCache {
13         static final int low = -128;
14         static final int high;
15         static final Integer cache[];
16
17         static {
18             // high value may be configured by property
19             int h = 127;
20             String integerCacheHighPropValue =
21                 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
22             if (integerCacheHighPropValue != null) {
23                 try {
24                     int i = parseInt(integerCacheHighPropValue);
25                     i = Math.max(i, 127);
26                     // Maximum array size is Integer.MAX_VALUE
27                     h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
28                 } catch( NumberFormatException nfe) {
29                     // If the property cannot be parsed into an int, ignore it.
30                 }
31             }
32             high = h;
33
34             cache = new Integer[(high - low) + 1];
35             int j = low;
36             for(int k = 0; k < cache.length; k++)
37                 cache[k] = new Integer(j++);
38
39             // range [-128, 127] must be interned (JLS7 5.1.7)
40             assert IntegerCache.high >= 127;
41         }
42
43         private IntegerCache() {}
44     }

我们看到IntegerCache的low定义为-128,high默认定义为127.但是high是可以配置的,如果没有配置才是127.我们不去看配置的情况,因为java默认是没有配置的。看一下cache数组,长度为high-low+1,从-128开始到127,存在cache数组内。

从上面的代码中可以看出,java在申请一个大于-128小于127的数时,其实是从cache中直接取出来用的,如果不在这个范围则是new了一个Integer对象。

对于==,他比较的是地址。对于int来说比较的是值。

对于equals,比较的是内容(要看equals的具体实现)。看一下Integer里面的实现:

 1 /**
 2      * Compares this object to the specified object.  The result is
 3      * {@code true} if and only if the argument is not
 4      * {@code null} and is an {@code Integer} object that
 5      * contains the same {@code int} value as this object.
 6      *
 7      * @param   obj   the object to compare with.
 8      * @return  {@code true} if the objects are the same;
 9      *          {@code false} otherwise.
10      */
11     public boolean equals(Object obj) {
12         if (obj instanceof Integer) {
13             return value == ((Integer)obj).intValue();
14         }
15         return false;
16     }

它比较的确实是值的大小。

因此i1==i2和i1.equals(i2)都是true

i3==i4为false

i3.equals(i4)为true。

时间: 2024-10-03 13:21:09

从源码看java中Integer的缓存问题的相关文章

从源码看Android中sqlite是怎么读DB的

执行query 执行SQLiteDatabase类中query系列函数时,只会构造查询信息,不会执行查询. (query的源码追踪路径) 执行move(里面的fillwindow是真正打开文件句柄并分配内存的地方) 当执行Cursor的move系列函数时,第一次执行,会为查询结果集创建一块共享内存,即cursorwindow moveToPosition源码路径 fillWindow----真正耗时的地方 然后会执行sql语句,向共享内存中填入数据, fillWindow源码路径 在SQLite

从源码看Android中sqlite是怎么读DB的(转)

执行query 执行SQLiteDatabase类中query系列函数时,只会构造查询信息,不会执行查询. (query的源码追踪路径) 执行move(里面的fillwindow是真正打开文件句柄并分配内存的地方) 当执行Cursor的move系列函数时,第一次执行,会为查询结果集创建一块共享内存,即cursorwindow moveToPosition源码路径 fillWindow----真正耗时的地方 然后会执行sql语句,向共享内存中填入数据, fillWindow源码路径 在SQLite

从源码看Java集合之ArrayList

Java集合之ArrayList - 吃透增删查改 从源码看初始化以及增删查改,学习ArrayList. 先来看下ArrayList定义的几个属性: private static final int DEFAULT_CAPACITY = 10; private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; tra

深入源码看java类加载器ClassLoader

ClassLoader类加载器是负责加载类的对象.ClassLoader 类是一个抽象类.如果给定类的二进制名称(即为包名加类名的全称),那么类加载器会试图查找或生成构成类定义的数据.一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的"类文件".java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java 类,即 java.lang.Class类的一个实例.除此之外,ClassLoad

从字节码看Java中for-each循环(增强for循环)实现原理

转发:http://blog.csdn.net/u011392897/article/details/54562596 for-each循环是jdk1.5引入的新的语法功能.并不是所有东西都可以使用这个循环的.可以看下Iterable接口的注释,它说明了除了数组外,其他类想要使用for-each循环必须实现这个接口.这一点表明除了数组外的for-each可能底层是由迭代器实现的. Iterable接口在1.8之前只有一个方法,Iterator<T> iterator(),此方法返回一个迭代器.

JDK源码看Java域名解析

前言 在互联网中通信需要借助 IP 地址来定位到主机,而 IP 地址由很多数字组成,对于人类来说记住某些组合数字很困难,于是,为了方便大家记住某地址而引入主机名和域名. 早期的网络中的机器数量很少,能很方便地通过 hosts 文件来完成主机名称和 IP 地址的映射,这种方式需要用户自己维护网络上所有主机的映射关系.后来互联网迅猛发展起来,hosts 文件方式已经无法胜任,于是引入域名系统(DNS)来解决主机名称和 IP 地址的映射. 局域网中常用来表示 IP 地址的名称更多称为主机名,而互联网上

从 php 源码看 php 中的对象

从一个简单的例子说起: class Person { public $name; public $age; public function __construct($name, $age) { $this->name = $name; $this->age = $age; } } $person = new Person("php", 20); echo serialize($person) . PHP_EOL; $array = [ 'name' => 'php',

解密随机数生成器(二)——从java源码看线性同余算法

Random Java中的Random类生成的是伪随机数,使用的是48-bit的种子,然后调用一个linear congruential formula线性同余方程(Donald Knuth的编程艺术的3.2.1节) 如果两个Random实例使用相同的种子,并且调用同样的函数,那么生成的sequence是相同的 也可以调用Math.random()生成随机数 Random实例是线程安全的,但是并发使用Random实例会影响效率,可以考虑使用ThreadLocalRandom变量. Random实

【从源码看Android】03Android MessageQueue消息循环处理机制(epoll实现)

1 enqueueMessage handler发送一条消息 mHandler.sendEmptyMessage(1); 经过层层调用,进入到sendMessageAtTime函数块,最后调用到enqueueMessage Handler.java public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeE