一个疏忽损失惨重!就因为把int改成Integer,第2天被辞了
1 故事背景
一个程序员就因为改了生产环境上的一个方法参数,把int型改成了Integer类型,因为涉及到钱,结果上线之后公司损失惨重,程序员被辞退了。信不信继续往下看。先来看一段代码:
public static void main(String[] args) {
Integer a = Integer.valueOf(100);
Integer b = 100;
Integer c = Integer.valueOf(129);
Integer d = 129;
System.out.println("a==b:" + (a==b));
System.out.println("c==d:" + (c==d));
}
大家猜它的运行结果是什么?在运行完程序后,我们才发现有些不对,得到了一个意想不到的运行结果,如下图所示。

看到这个运行结果,有人就一定会问,为什么是这样?之所以得到这样的结果,是因为Integer用到的享元模式。来看Integer的源码。
public final class Integer extends Number implements Comparable<Integer> {
...
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
...
}
再继续进入到IntegerCache的源码来看low和high的值:
private static class IntegerCache {
// 最小值
static final int low = -128;
// 最大值,支持自定义
static final int high;
// 缓存数组
static final Integer cache[];
static {
// 最大值可以通过属性配置来改变
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);
// 最大数组大小为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;
// 将low-high范围内的值全部实例化并存入数组中当缓存使用
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源码中的valueOf()方法做了一个条件判断,如果目标值在-128 - 127,则直接从缓存中取值,否则新建对象。其实,Integer第一次使用的时候就会初始化缓存,其中范围最小值为-128,最大值默认是127。接着会把low至high中所有的数据初始化存入数据中,默认就是将-128 - 127总共256个数循环实例化存入cache数组中。准确的说应该是将这256个对象在内存中的地址存进数组中。这里又有人会问了,那为什么默认是-128 - 127,怎么不是-200 - 200或者是其他值呢?那JDK为何要这样做呢?
在Java API 中是这样解释的:
Returns an Integer instance representing the specified int value. If a new Integer instance is not required, this method should generally be used in preference to the constructor 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
大致意思是:
128~127的数据在int范围内是使用最频繁的,为了减少频繁创建对象带来的内存消耗,这里其实是用到了享元模式,以提高空间和时间性能。
JDK增加了这一默认的范围并不是不可变,我们在使用前可以通过设置-Djava.lang.Integer.IntegerCache.high=xxx或者设置-XX:AutoBoxCacheMax=xxx来修改缓存范围,如下图:

后来,我又找到一个比较靠谱的解释:
实际上,在Java 5中首次引入此功能时,范围固定为-127到+127。后来在Java 6中,范围的最大值映射到java.lang.Integer.IntegerCache.high,VM参数允许我们设置高位数。根据我们的应用用例,它可以灵活地调整性能。应该从-127到127选择这个数字范围的原因应该是什么。这被认为是广泛使用的整数范围。在程序中首次使用Integer必须花费额外的时间来缓存实例。
Java Language Specification 的原文解释如下:
Ideally, boxing a given primitive value p, would always yield an identical reference. In practice, this may not be feasible using existing implementation techniques. The rules above are a pragmatic compromise. The final clause above requires that certain common values always be boxed into indistinguishable objects. The implementation may cache these, lazily or eagerly. For other values, this formulation disallows any assumptions about the identity of the boxed values on the programmer's part. This would allow (but not require) sharing of some or all of these references. This ensures that in most common cases, the behavior will be the desired one, without imposing an undue performance penalty, especially on small devices. Less memory-limited implementations might, for example, cache all char and short values, as well as int and long values in the range of -32K to +32K.
2 关于Integer和int的比较
\1) 由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。
Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.print(i == j); //false
\2) Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
Integer i = new Integer(100);
int j = 100;
System.out.print(i == j); //true
\3) 非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。(因为 ①当变量值在-128 - 127之间时,非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同;②当变量值在-128 - 127之间时,非new生成Integer变量时,java API中最终会按照new Integer(i)进行处理(参考下面第4条),最终两个Interger的地址同样是不相同的)
Integer i = new Integer(100);
Integer j = 100;
System.out.print(i == j); //false
\4) 对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false
3 扩展知识
在JDK中,这样的应用不止int,以下包装类型也都应用了享元模式,对数值做了缓存,只是缓存的范围不一样,具体如下表所示:

大家觉得这个锅背得值不值?
4 使用享元模式实现数据库连接池
再举个例子,我们经常使用的数据库连接池,因为使用Connection对象时主要性能消耗在建立连接和关闭连接的时候,为了提高Connection对象在调用时的性能,将Connection对象在调用前创建好并缓存起来,在用的时候直接从缓存中取值,用完后再放回去,达到资源重复利用的目的,代码如下。
public class ConnectionPool {
private Vector<Connection> pool;
private String url = "jdbc:mysql://localhost:3306/test";
private String username = "root";
private String password = "root";
private String driverClassName = "com.mysql.jdbc.Driver";
private int poolSize = 100;
public ConnectionPool() {
pool = new Vector<Connection>(poolSize);
try{
Class.forName(driverClassName);
for (int i = 0; i < poolSize; i++) {
Connection conn = DriverManager.getConnection(url,username,password);
pool.add(conn);
}
}catch (Exception e){
e.printStackTrace();
}
}
public synchronized Connection getConnection(){
if(pool.size() > 0){
Connection conn = pool.get(0);
pool.remove(conn);
return conn;
}
return null;
}
public synchronized void release(Connection conn){
pool.add(conn);
}
}
这样的连接池,普遍应用于开源框架,可以有效提升底层的运行性能。
一个疏忽损失惨重!就因为把int改成Integer,第2天被辞了的更多相关文章
- 就因为把int改成Integer,第2天被辞了
本文节选自<设计模式就该这样学>之享元模式(Flyweight Pattern) 1 故事背景 一个程序员就因为改了生产环境上的一个方法参数,把int型改成了Integer类型,因为涉及到 ...
- 今天做一个winform,想直接把窗体改成输出类库,其他地方直接调结果总提示不能注册组件,回来调度,可以,总结,windows还是直接用新建的类型项目,改容易出错
如题, 对于winform程序,还是新建一个类库,这样,在类库里面可以添加窗体.这样可以提供其他程序集来调用里面的窗体
- String.valueOf(int i)和Integer.toString(int i)有什么区别?
以下是2个人的回答,我是从百度上复制下来的,做个笔记,以后方便看 String.valueOf()它可以将JAVA基本类型(int,double,boolean等)和对象(Object)转换成Stri ...
- arm指令bne.w改成b,即无条件跳转
近期逆向一个程序,需要把bne.w改成b,无条件跳转.由于ios逆向不像pc上,可以在od里直接改汇编指令,这篇文章给了我很大的帮助.通过memory write 修改后,验证可行后,再用ultrae ...
- 转:JAVA里面的int类型 和Integer类型,有什么不一样
JAVA里面的int类型 和Integer类型,有什么不一样 原文链接:http://blog.csdn.net/wuxinliulei/article/details/11099565 java.l ...
- 关于将汉语拼音字母“ü”改成“v”的设想和建议
http://bbs.tianya.cn/post-free-1667253-1.shtml?_t=t -- 徐州工业职业技术学院 孙生强 <汉语拼音方案>为中国人的语言文字学习带来极大方 ...
- JAVA里面的int类型 和Integer类型,有什么不一样
JAVA里面的int类型 和Integer类型,有什么不一样 原创 2013年09月04日 23:15:11 标签: java / 2120 编辑 删除 JAVA里面的int类型 和Integer类型 ...
- LigerUI一个前台框架增、删、改asp.net代码
LigerUI一个前台框架增.删.改asp.net代码的实现 先上代码:前台代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tran ...
- Java笔试题:给定一个ReadOnlyClass的对象roc,能否把这个对象的age值改成30?
在Java笔试面试中,经常会遇到代码题,今天我们就来看一则Java代码笔试题. 有如下代码: Class ReadOnlyClass { private Integer age=20; public ...
随机推荐
- 深入浅出WPF-03.XAML语法
2 XAML语法 树形结构,我们将整个XAML的结构想象成一棵树,我们从树的顶部向下看,形成俯视图.最上面的叶子节点会覆盖父节点,同级的子节点,后面的(也就是树的最上面)会覆盖前面的.覆盖包含了形状( ...
- netty系列之:分离websocket处理器
目录 简介 netty的消息处理 处理WebSocketFrame 处理HTTP 编码和解码器 总结 简介 在上一篇文章中,我们使用了netty构建了可以处理websocket协议的服务器,在这个服务 ...
- 【.NET 与树莓派】气压传感器——BMP180
BMP180 是一款数字气压计传感器,实际可读出温度和气压值.此模块使用 IIC(i2c)协议.模块体积很小,比老周的大拇指指甲还小:也很便宜,一般是长这样的.螺丝孔只开一个,也有开两个孔的. 这货基 ...
- TCP协议基本概念
TCP协议最主要的特点 TCP是面向连接的运输层协议.这就是说,应用程序在使用TCP协议之前,必须要建立TCP连接,且在传输完毕后,还要断开连接. 每一条TCP连接只能有两个端点,每一条TCP连接只能 ...
- C语言日记② 初识 C的内存
第一节: 上一个笔记 有趣的恶搞小程序 定时关机 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <str ...
- tomcat启动程序报错
1.问题 23-Apr-2021 10:53:38.897 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.de ...
- Go语言核心36讲(Go语言基础知识三)--学习笔记
03 | 库源码文件 在我的定义中,库源码文件是不能被直接运行的源码文件,它仅用于存放程序实体,这些程序实体可以被其他代码使用(只要遵从 Go 语言规范的话). 这里的"其他代码" ...
- 开源协同OA办公平台教程:O2OA服务管理中,接口的调用权限
本文介绍O2OA服务管理中,接口的权限设定和调用方式. 适用版本:5.4及以上版本 创建接口 具有服务管理设计权限的用户(具有ServiceManager角色或Manager角色)打开" ...
- Solon 框架如何方便获取每个请求的响应时间?
经常会有同学问 Solon 怎样才能获取每个请求的响应时间?要求是不需要给每个函数加注解.故此,整理了一下. 不给每个函数加注解,主要有两种方式可以获取请求响应时间: 方式1:基于全局过滤器 Solo ...
- vue.$set实现原理
上源码: export function set (target: Array<any> | Object, key: any, val: any): any { if (process. ...