浅谈Integer缓存机制原理
面试题引入
这里引申出一个经典问题,看下面代码
Integer a = 100;
Integer b = 100;
System.out.println(a == b);//true
Integer c = 200;
Integer d = 200;
System.out.println(c == d);//false
为什么第一个输出的是true,第二个输出的是false?
源码分析
Integer a = 100的这种直接赋值操作,是调⽤Integer.valueOf(100)方法,从Integer.valueOf()源码可以看到,返回的是Integer对象,但这里的实现并不是简单的new Integer,而是先判断 i 这个值是否在IntegerCache范围内,如果在,直接返回IntegerCache中的值,如果不在则new Integer
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
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) {
try {
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);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
从源码可以看到,默认Integer cache 的下限是-128,上限默认127。当赋值100给Integer时,刚好在这个范围内,所以从cache中取对应的Integer并返回,所以a和b返回的是同一个对象,所以 比较是相等的,当赋值200给Integer时,不在cache 的范围内,所以会new Integer并返回,当然 比较的结果是不相等的。
扩展:Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False
System.out.println(Integer.valueOf(-128) == Integer.valueOf(-128));//1.true
System.out.println(Integer.valueOf(127) == Integer.valueOf(127));//2.true
System.out.println(Integer.valueOf(128) == Integer.valueOf(128));//3.false
System.out.println(Integer.parseInt("128") == Integer.valueOf(128));//4.true
1、2、3都好理解,缓存范围是 [-128,127],1、2都在范围内,返回的是缓存中的对象,因此输出true,3不在范围内,返回的是新 new 的Integer,因此输出false。
那为什么4输出的是true呢? 128 在缓存范围外,按道理会 new 出一个Integer对象,为什么输出true呢?
首先Integer.parseInt方法返回的是int 基本数据类型,不是对象,也就是说 Integer.parseInt("128") = 128
public static int parseInt(String s) throws NumberFormatException {
return parseInt(s,10);
}
当进行比较("==")运算时,会进行自动拆箱,也就是说 Integer.valueOf(128) 生成的 Integer 会自动拆箱成128,那么比较两个相等的额数值自然是true的
当基础类型与它们的包装类有如下几种情况时,编译器会自动进行装箱或拆箱:
- 赋值操作(装箱或拆箱)
- 进行加减乘除混合运算 (拆箱)
- 进行>,<,==比较运算(拆箱)
- 调用equals进行比较(装箱)
- ArrayList、HashMap等集合类添加基础类型数据时(装箱)
注意:三目运算符 condition ? 表达式 1:表达式 2 中,高度注意表达式 1 和 2 在类型对齐时,可能抛出因自动拆箱导致的 NPE 异常
- 表达式 1 或 表达式 2 的值只要有一个是原始类型。
- 表达式 1 或 表达式 2 的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。
Integer a = 1;
Integer b = 2;
Integer c = null;
Boolean flag = false;
// a*b 的结果是 int 类型,那么 c 会强制拆箱成 int 类型,抛出 NPE 异常
Integer result = (flag ? a * b : c);
思考
缓存机制存在的原因:将频繁被使用的对象缓存起来,可以提升读取的效率,这是一个典型的用空间换时间的例子(其实缓存机制都是这个原理),而Java开发者认为[-128,127]是比较常使用的范围。
关于作者
来自一线程序员Seven的探索与实践,持续学习迭代中~
本文已收录于我的个人博客:Seven的菜鸟成长之路
公众号:seven97,欢迎关注~
浅谈Integer缓存机制原理的更多相关文章
- 浅谈HTTP缓存以及后端,前端如何具体实现HTTP缓存
<浅谈HTPP缓存>原版: https://juejin.im/post/5bdeabbbe51d4505466cd741?utm_source=gold_browser_extensio ...
- TODO:浅谈pm2基本工作原理
TODO:浅谈pm2基本工作原理 要谈Node.js pm2的工作原理,需要先来了解撒旦(Satan)和上帝(God)的关系. 撒旦(Satan),主要指<圣经>中的堕天使(也称堕天使撒旦 ...
- $.ajax()方法详解 ajax之async属性 【原创】详细案例解剖——浅谈Redis缓存的常用5种方式(String,Hash,List,set,SetSorted )
$.ajax()方法详解 jquery中的ajax方法参数总是记不住,这里记录一下. 1.url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. 2.type: 要求为Str ...
- Python 基于python+mysql浅谈redis缓存设计与数据库关联数据处理
基于python+mysql浅谈redis缓存设计与数据库关联数据处理 by:授客 QQ:1033553122 测试环境 redis-3.0.7 CentOS 6.5-x86_64 python 3 ...
- DNS 缓存机制原理
DNS 缓存机制原理 简单来说,一条域名的DNS记录会在本地有两种缓存:浏览器缓存和操作系统(OS)缓存.在浏览器中访问的时候,会优先访问浏览器缓存, 如果未命中则访问OS缓存,最后再访问DNS服务器 ...
- Integer缓存机制-基本数据类型和包装类型-自动拆装箱
Integer缓存机制 总结: 1.jdk1.5对Integer新增了缓存机制,范围在-128-127(这个范围的整数值使用频率最高)内的自动装箱返回的是缓存对象,不会new新的对象,所以只要在缓存范 ...
- 浅谈web缓存(转)
这是一篇知识性的文档,主要目的是为了让Web缓存相关概念更容易被开发者理解并应用于实际的应用环境中.为了简要起见,某些实现方面的细节被简化或省略了.如果你更关心细节实现则完全不必耐心看完本文,后面参考 ...
- java中字面量,常量和变量之间的区别(附:Integer缓存机制)
一.引子 在各种教科书和博客中这三者经常被引用,今天复习到内存区域,想起常量池中就是存着字面量和符号引用,其实这三者并不是只在java中才有,各个语言中都有类似的定义,所以做一下总结,以示区分. 二. ...
- 浅谈SpringBoot核心注解原理
SpringBoot核心注解原理 今天跟大家来探讨下SpringBoot的核心注解@SpringBootApplication以及run方法,理解下springBoot为什么不需要XML,达到零配置 ...
- 浅谈java缓存
java中要用到缓存的地方很多,首当其冲的就是持久层缓存,针对持久层谈一下: 要实现java缓存有很多种方式,最简单的无非就是static HashMap,这个显然是基于内存缓存,一个map就可以搞定 ...
随机推荐
- arm-gcc 命名规则
背景 在 Ubuntu中交叉编译裸机程序的时候,注意到了这个问题:因此对此作为一个总结. 命名规则 交叉编译工具链的命名规则为:arch [-vendor] [-os] [-(gnu)eabi] ar ...
- 单qubit量子门
量子编程的基本单元就是量子门.量子编程有点像传统的电路设计,一个量子程序可以被写成量子门序列. 图中有一些符合,比如H门.X门.Z门.测量等,我们都会接触到. 传统计算机程序的输入和输出可以不一样,但 ...
- OpenWrt中的LuCi和Lua一些总结
Lua.LuCi Lua是一种小巧的脚本语言,和Python一样,Lua脚本的运行需要Lua解释器: UCI(Unified Configuration Interface)是OpenWrt实现所有系 ...
- Java -jar 启动程序参数说明
Springboot环境下的启动参数说明 # Java启动命令语法: java [options] -jar file.jar [arguments] java -jar [options] file ...
- WOE编码与IV值
参考: WOE与IV值浅谈 机器学习-变量筛选之IV值和WOE 0. Introduction WOE (weight of evidence): 证据权重 IV (information value ...
- 使用Sequelize
访问MySQL 当我们安装好MySQL后,Node.js程序如何访问MySQL数据库呢? 访问MySQL数据库只有一种方法,就是通过网络发送SQL命令,然后,MySQL服务器执行后返回结果. 我们可以 ...
- yb课堂 开发前端项目路由 《三十五》
Vue-Router开发前端项目路由 vue-router 是Vue.js官方的路由管理器,它和Vue.js的核心深度集成,让构建单页面应用变得易如反掌 官方文档:https://router.vue ...
- C#使用SendMessage进行进程间通讯
最近公司有个需求是,拖动文件到桌面图标上,自动打开文件.那么只需在OnStartup事件中通过StartupEventArgs获取文件名然后进行操作即可.操作之后发现当软件已经启动了(单例运行),那么 ...
- 在windows双系统中,nginx配置虚拟域名
比如在ubuntu系统中,nginx配置了域名www.abc.com, 那么需要在终端 sudo vim /etc/hosts文件中配置域名,如下: 127.0.0.1 www.abc.com 即可访 ...
- mysql order by 造成语句 执行计划中Using filesort,Using temporary相关语句的优化解决
mysql> explain select permission.* from t_rbac_permission permission inner JOIN t_rbac_acl ...