Java Integer 缓存特性(Integer最大/小值探究、整型的自动装箱)

简介: Java Integer 缓存特性(Integer最大/小值探究、整型的自动装箱)

问题引出


Integer最大/小值


java中Integer是有最大值和最小值的


最大值为


Integer.MAX_VALUE = 2147483647


最小值为


Integer.MIN_VALUE = -2147483648


注意:两个值并没有互为相反数



Integer.MAX_VALUE + 1 = Integer.MIN_VALUE


同理


Integer.MIN_VALUE - 1 = Integer.MAX_VALUE


Integer最大/小值案例


但是用到了这样的一个案例:


public static void test() {
    //true
    Integer a1 = 127, b1 = 127;
    System.out.println(a1 == b1);
    //false
    Integer a2 = new Integer(127);
    Integer b2 = new Integer(127);
    System.out.println(a2 == b2);
    //false
    Integer a3 = 128, b3 = 128;
    System.out.println(a3 == b3);
    //true
    Integer a4 = 128, b4 = 128;
    System.out.println(a4.equals(b4));
}
public static void main(String[] args) {
    test();
}


比较答案:


true
false
false
true


结果原因


  • 第一个使用"=="判断 Integer a1 = 127 和 Integer b1 = 127


相等原因:Integer的取值范围是-128~127


  • 第二个使用"=="判断Integer a2 = new Integer(127) 和 Integer b2 = new Integer(127)


不相等原因:装箱时,java为了提高效率,IntegerCache类中有一个数组将-128<=i<=127范围之内的数据打包成缓存里的Integer对象了,因此不用new,这个区间里的值用直接=赋值方法得到的变量地址就是同一个,而超出这个范围的数值就会new一个Integer对象出来


  • 第三个使用"=="判断Integer a3=128 和 Integer b3 = 128


不相等原因:因为Integer类型赋值范围在-128—127之间,超出这个范围的数值就会重新new一个Integer对象出来,直接用==去比较是比较a3和b3的地址,a3和b3的地址是不相等的


  • 第四个使用使用"=="equals()方法判断Integer a4=128 和 Integerb4 = 128


相等原因:使用equals()方法比较的是a4和b4的数值


注意点:"=="可以用于原始值进行比较,也可以用于对象进行比较,当用于对象与对象之间比较时,比较的不是对象代表的值,而是检查两个对象是否是同一对象,这个比较过程中没有自动装箱发生。进行对象值比较不应该使用”==“,而应该使用对象对应的equals方法。


小总结


JVM会自动维护八种基本类型的常量池,int常量池中初始化-128~127的范围,所以当为Integer i=127时,在自动装箱过程中是取自常量池中的数值,而当Integer i=128时,128不在常量池范围内,所以在自动装箱过程中需new 128,所以地址不一样。


上面就用到了Java自动装箱的概念,下面我们来深入了解一下:


Integer 自动装箱


IntegerCache缓存


Integer 缓存是 Java 5 中引入的一个有助于节省内存、提高性能的特性。


Integer中有个静态内部类IntegerCache,里面有个cache[],也就是Integer常量池,常量池的大小为一个字节(-128~127)。JDK源码如下(摘自JDK1.8源码):


    /**
    * 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=<size> 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));
            }
            high = h;
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }
        private IntegerCache() {}
    }


当创建Integer对象时,不使用new Integer(int i)语句,大小在-128~127之间,对象存放在Integer常量池中。


例如:Integer a = 10;


调用的是Integer.valueOf()方法,代码为:


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


这也是自动装箱的代码实现。


Java自动装箱


JAVA将基本类型自动转换为包装类的过程称为自动装箱(autoboxing)。


实际上在 Java 5 中引入这个特性的时候,范围是固定的 -128 至 +127。后来在Java 6 后,最大值映射到 java.lang.Integer.IntegerCache.high,可以使用 JVM 的启动参数设置最大值。(通过 JVM 的启动参数 -XX:AutoBoxCacheMax=size 修改)


缓存通过一个 for 循环实现。从小到大的创建尽可能多的整数并存储在一个名为 cache 的整数数组中。这个缓存会在 Integer 类第一次被使用的时候被初始化出来。以后,就可以使用缓存中包含的实例对象,而不是创建一个新的实例(在自动装箱的情况下)。


测试情况:


int i = 10;
int i1 = 10;
Integer in1 = 10;
Integer in2 = 10;
Integer in3 = new Integer(10);
Integer in4 = new Integer(10);
Integer in5 = 199;
Integer in6 = 199;
System.out.println(i == i1);    // true
System.out.println(i == in1);   // true
System.out.println(i == in2);   // true
System.out.println(i == in3);   // true
System.out.println(in1 == in2);   // true
System.out.println(in5 == in6);   // false
System.out.println(in1 == in3);   // false
System.out.println(in3 == in4);   // false


在 Boxing Conversion 部分的Java语言规范(JLS)规定如下:


如果一个变量 p 的值属于:-128至127之间的整数(§3.10.1),true 和 false的布尔值 (§3.10.3),’u0000′ 至 ‘u007f’ 之间的字符(§3.10.4)中时,将 p 包装成 a 和 b 两个对象时,可以直接使用 a == b 判断 a 和 b 的值是否相等。


所有整数类型的类都有类似的缓存机制:


  • 有 ByteCache 用于缓存 Byte 对象


  • 有 ShortCache 用于缓存 Short 对象


  • 有 LongCache 用于缓存 Long 对象


Byte,Short,Long 的缓存池范围默认都是: -128 到 127。可以看出,Byte的所有值都在缓存区中,用它生成的相同值对象都是相等的。


所有整型(Byte,Short,Long)的比较规律与Integer是一样的。


同时Character 对象也有CharacterCache 缓存 池,范围是 0 到 127。


除了 Integer 可以通过参数改变范围外,其它的都不行。


相关文章
|
2月前
|
存储 安全 Java
Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
【10月更文挑战第17天】Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
77 2
|
2月前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
45 3
|
2月前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
34 2
|
2月前
|
存储 算法 Java
Java Set因其“无重复”特性在集合框架中独树一帜
【10月更文挑战第14天】Java Set因其“无重复”特性在集合框架中独树一帜。本文深入解析Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定的数据结构(哈希表、红黑树)确保元素唯一性,并提供最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的`hashCode()`与`equals()`方法。
35 3
|
2月前
|
缓存 JavaScript 前端开发
Java 如何确保 JS 不被缓存
【10月更文挑战第19天】在 Java 中,可以通过设置 HTTP 响应头来确保 JavaScript 文件不被浏览器缓存。方法包括:1. 使用 Servlet 设置响应头,通过 `doGet` 方法设置 `Expires`、`Cache-Control` 和 `Pragma` 头;2. 在 Spring Boot 中配置拦截器,通过 `NoCacheInterceptor` 类和 `WebConfig` 配置类实现相同功能。这两种方法都能确保每次请求都能获取到最新的 JavaScript 内容。
|
2月前
|
安全 Java API
Java 17新特性让你的代码起飞!
【10月更文挑战第4天】自Java 8发布以来,Java语言经历了多次重大更新,每一次都引入了令人兴奋的新特性,极大地提升了开发效率和代码质量。本文将带你从Java 8一路走到Java 17,探索那些能让你的代码起飞的关键特性。
92 1
|
8天前
|
存储 Java 开发者
什么是java的Compact Strings特性,什么情况下使用
Java 9引入了紧凑字符串特性,优化了字符串的内存使用。它通过将字符串从UTF-16字符数组改为字节数组存储,根据内容选择更节省内存的编码方式,通常能节省10%至15%的内存。
|
17天前
|
存储 Java 数据挖掘
Java 8 新特性之 Stream API:函数式编程风格的数据处理范式
Java 8 引入的 Stream API 提供了一种新的数据处理方式,支持函数式编程风格,能够高效、简洁地处理集合数据,实现过滤、映射、聚合等操作。
34 6
|
1月前
|
分布式计算 Java API
Java 8引入了流处理和函数式编程两大新特性
Java 8引入了流处理和函数式编程两大新特性。流处理提供了一种声明式的数据处理方式,使代码更简洁易读;函数式编程通过Lambda表达式和函数式接口,简化了代码书写,提高了灵活性。此外,Java 8还引入了Optional类、新的日期时间API等,进一步增强了编程能力。这些新特性使开发者能够编写更高效、更清晰的代码。
33 4
|
2月前
|
存储 Java API
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
94 3