Integer.valueOf(String) 方法之惑

本文解释了Java中Integer缓存的工作原理,并通过几个示例展示了如何比较Integer对象及其潜在陷阱。

有个仁兄在 StackOverflow 上发起了一个问题,是这么问的:

“ 我被下面的代码搞晕了,为什么它们会返回不同的值?”

1
2
3
System.out.println(Integer.valueOf( "127" )==Integer.valueOf( "127" ));
System.out.println(Integer.valueOf( "128" )==Integer.valueOf( "128" ));
System.out.println(Integer.parseInt( "128" )==Integer.valueOf( "128" ));

输出是:

1
2
3
true
false
true

为什么第一个判断返回了true而第二个判断返回了false127128有什么我不知道的区别吗?(当然除了127小于128…)
还有,为什么第三个判断返回了true
我看了另一个相关提问的回答,但是我还是不知道它们什么时候返回true还有为什么第二个判断返回false

回答#1:

Integer.valueOf(String)确有一个不同寻常的行为。

valueOf会返回一个Integer(整型)对象,当被处理的字符串在-128127(包含边界)之间时,返回的对象是预先缓存的。这就是为什么第一行的调用会返回true-127这个整型对象是被缓存的(所以两次valueOf返回的是同一个对象)——第二行的调用返回false是因为128没有被缓存,所以每次调用,都会生成一个新的整型对象,因此两个128整型对象是不同的对象。

重要的是你要知道在上面的比较中,你实际进行比较的是integer.valueOf返回的对象引用,所以当你比较缓存外的整型对象时,相等的判断不会返回true,就算你传个valueOf的值是相等的也没用。(就像第二行中Integer.valueOf(128)==Integer.valueOf(128))。想让这个判断返回true,你需要使用equals()方法。

parseInt()返回的不是整型对象,而是一个int型基础元素。这就是为什么最后一个判断会返回true,第三行的判断中,在判断相等时,实际比较的是128 == 128,所以它必然是相等的。

再来说说第三种比较中的一点区别,使得它的结果与第二种比较不一样了:

一个unboxing conversion(一种比较时的转换,把对对象的引用转换为其对应的原子类型)在第三行的比较中发生了。因为比较操作符使用了==同时等号的两边存在一个int型和一个Integer对象的引用。这样的话,等号右边返回的Integer对象被进一步转换成了int数值,才与左边进行相等判断。

所以在转换完成后,你实际比较的是两个原子整型数值。这种转换正是你在比较两个原子类型时所期待看到的那样,所以你最终比较了128等于128

回答#2:

Integer类有一个静态缓存,存储了256个特殊的Integer对象——每个对象分别对应`-128 和127之间的一个值。
有了这个概念,就可以知道上面三行代码之间的区别。

1
new Integer(123);

显示创建了一个新的Integer对象。

1
Integer.parseInt( "123" );

解析完字符串后返回一个int值。

1
Integer.valueOf( "123" );

这种情况比其他的要更复杂一些。首先进行了字符串解析,然后如果解析的值位于-128127之间,就会从静态缓存中返回对象。如果超出了这个范围,就会调用Integer()方法并将解析的值作为参数传入,得到一个新的对象。

现在,让我们看一下问题中的3个表达式。

1
Integer.valueOf( "127" )==Integer.valueOf( "127" );

上面的表达式返回true,因为Integer的值从静态缓存中取了2次,表达式返回了对象与自己比较的结果。因为只有一个Integer对象,所以返回结果为true

1
Integer.valueOf( "128" )==Integer.valueOf( "128" );

上面的表达式返回false,因为128没有存在静态缓冲区。所以每次在判断相等时等式两边都会创建新的Integer对象。由于两个Integer对象不同,所以==只有等式两边代表同一个对象时才会返回true。因此,上面的等式返回false

1
Integer.parseInt( "128" )==Integer.valueOf( "128" );

上面的表达式比较的是左边的原始int128与右边新创建的Integer对象。但是因为intInteger之间比较是没有意义的,所以Java在进行比较前会将Integer自动拆箱,所以最后进行的是intint值之间的比较。由于128和自己相等,所以返回true

补充说明:
文章没有列出具体的执行环境,jdk版本信息,因此造成不一样的结果,让大家误解了.抱歉.现在说明一下:
此文只适应于jdk7或以上版本,因为jdk6jdk7的Integer具体实现有差别,详情可查看下源代码.

原文链接:  stackoverflow  翻译:  ImportNew.com  靳禹
译文链接:  http://www.importnew.com/9162.html
<think>我们正在讨论Integer.parseInt(String)Integer.valueOf(String)的区别。根据引用内容,我们可以总结如下: 1. **返回值类型不同**: - `Integer.parseInt(String s)` 返回的是基本数据类型 `int`。 - `Integer.valueOf(String s)` 返回的是包装类 `Integer` 的对象。 2. **内部实现**: - `parseInt` 方法内部调用了 `parseInt(String s, int radix)`,默认使用十进制。 - `valueOf` 方法内部调用了 `parseInt` 方法得到 `int` 值,然后使用 `Integer.valueOf(int)` 将其转换为 `Integer` 对象。 3. **缓存机制**: - `Integer.valueOf(int)` 方法会使用缓存。对于范围在 -128 到 127 之间的整数,会返回缓存中的对象,而不是新建对象。但注意,这里说的是 `valueOf(int)` 的缓存,而 `valueOf(String)` 因为内部调用了 `parseInt` 得到 `int`,再调用 `valueOf(int)`,所以同样适用缓存。 4. **参数类型**: - `parseInt` 方法只能接受 `String` 类型参数。 - `valueOf` 方法可以接受多种类型参数(如 `byte`, `char`, `short`, `int`, `long` 等),但这里我们讨论的是字符串参数。 5. **自动拆箱**: - 由于 `valueOf` 返回的是 `Integer` 对象,但在需要 `int` 的上下文中(如赋值给 `int` 变量,算术运算等),会自动拆箱为 `int`。因此,在大多数情况下,两者可以互换使用,但要注意可能发生的自动拆箱带来的性能影响(微乎其微)以及空指针风险(如果返回的 `Integer` 对象为 `null`,但这种情况在转换字符串时不会出现,因为转换失败会抛出异常)。 6. **异常处理**: - 两者在转换失败时都会抛出 `NumberFormatException`。 7. **使用场景**: - 如果需要基本类型 `int`,使用 `parseInt` 更直接,避免创建对象(但实际上,在自动拆箱下,`valueOf` 也会被优化,且缓存机制可能减少对象创建)。 - 如果需要 `Integer` 对象(例如放入集合中),使用 `valueOf` 可以避免显式创建对象。 根据引用[4]和[5]: > Integer.parseInt()返回int,Integer.valueOf()返回int的封装类Integer。 > 通常情况下,如果只需要使用整数的值而不需要Integer对象,建议使用Integer.parseInt(str)方法。如果需要将整数作为对象进行操作,则可以使用Integer.valueOf(str)。 因此,我们可以这样总结: - 如果你需要一个基本类型 `int`,那么使用 `parseInt` 更合适。 - 如果你需要一个 `Integer` 对象(例如要放入集合中,或者需要使用对象的方法),那么使用 `valueOf` 更合适。 另外,引用[3]提到: > 当参数类型为String时,Integer.ParseInt(String)返回int,Integer.valueOf(String)返回Integer,无区别。 这里说的“无区别”是指功能上都可以将字符串转为整数,但返回类型不同。 同时,引用[3]还提到: > 由于Integer.valueOf()的参数可以为多种类型;①当参数为byte,char,short,string等自动转换为int类型时,会自动转换,当为float或double时会提示错误。②特殊的:当参数类型为char时,Integer.valueOf()会自动计算char类型的ASII码,如对于char ch=‘a’,使用Integer.valueOf(ch)输出97. 这里需要注意,我们讨论的是参数为字符串的情况。对于其他类型,`valueOf` 有重载方法。 现在,我们按照要求组织答案,包括数学公式(如果有的话,但本题不涉及复杂公式,所以可能不需要)和代码示例。 注意:在回答中,我们需要在引用内容的末尾添加引用标识,如[^1]。</think>### Java 中 `Integer.parseInt()` 与 `Integer.valueOf()` 的区别详解 #### 🔍 核心区别总结 | 特性 | `Integer.parseInt(String)` | `Integer.valueOf(String)` | |---------------------|----------------------------|---------------------------| | **返回值类型** | `int` (基本数据类型) | `Integer` (包装类对象) | | **内部实现** | 直接调用 `parseInt(s, 10)` | 调用 `parseInt()` 后包装为对象 | | **缓存机制** | ❌ 无缓存 | ✅ 使用整数缓存池 (-128~127) | | **参数兼容性** | 仅支持 `String` | 支持多种类型(包括 `char`) | | **内存开销** | 更低(无对象创建) | 更高(可能创建新对象) | | **推荐使用场景** | 需要基本类型计算的场景 | 需要对象操作的场景(如集合) | --- #### 💡 详细解析(含源码分析) 1. **返回值本质区别** ```java // parseInt 返回基本类型 int int num1 = Integer.parseInt("42"); // → 42 (基本类型) // valueOf 返回包装类 Integer Integer num2 = Integer.valueOf("42"); // → Integer@1234 (对象引用) ``` - **关键点**:`parseInt()` 直接返回 `int`,而 `valueOf()` 返回的是包装类对象([^4]) 2. **源码实现差异** ```java // parseInt 源码 (简化版) public static int parseInt(String s) { return parseInt(s, 10); // 直接返回 int } // valueOf 源码 (简化版) public static Integer valueOf(String s) { return Integer.valueOf(parseInt(s, 10)); // 调用 parseInt 后包装 } ``` - `valueOf()` 内部依赖 `parseInt()` 进行字符串转换([^4]) - 对于 `int` 值,`Integer.valueOf(int)` 会使用缓存池优化 3. **缓存机制对比** ```java Integer a = Integer.valueOf(127); Integer b = Integer.valueOf(127); System.out.println(a == b); // true (使用缓存池) Integer c = Integer.valueOf(128); Integer d = Integer.valueOf(128); System.out.println(c == d); // false (超出缓存范围) ``` - 缓存范围:**-128 到 127**(通过 `IntegerCache` 实现) - `parseInt()` 无此优化([^1]) 4. **参数兼容性差异** ```java // valueOf 支持多种类型 Integer ascii = Integer.valueOf('A'); // 65 (ASCII 值) // Integer.parse('A'); // 编译错误! // 处理浮点数需注意 Double.parseDouble("3.14"); // ✅ 正确方式 // Integer.valueOf(3.14); // ❌ 编译错误 ``` - `valueOf()` 可接受 `char` 类型并自动转换 ASCII([^3]) - 两者都无法直接处理浮点数字符串 --- #### ⚠️ 异常处理与最佳实践 1. **统一异常类型** ```java try { int num = Integer.parseInt("12a"); // 抛出 NumberFormatException } catch (NumberFormatException e) { System.out.println("无效数字格式"); } ``` - 两者对非法输入均抛出 `NumberFormatException` 2. **性能与内存建议** - 优先使用 `parseInt()` 的场景: ```java // 计算场景(避免自动拆箱开销) int sum = Integer.parseInt("100") + 50; ``` - 优先使用 `valueOf()` 的场景: ```java // 集合操作 List<Integer> list = new ArrayList<>(); list.add(Integer.valueOf("42")); ``` 3. **空值安全处理** ```java // 使用 Optional 避免 NPE String input = getInput(); int safeValue = Optional.ofNullable(input) .filter(s -> s.matches("\\d+")) .map(Integer::parseInt) .orElse(0); ``` --- #### 💎 终极选择指南 | 场景 | 推荐方法 | 原因 | |-------------------------------|------------------------|-------------------------------| | 数学运算/基本类型操作 | `Integer.parseInt()` | 避免自动拆箱开销 | | 集合存储/对象操作 | `Integer.valueOf()` | 直接获得对象类型 | | 高频使用小数字 (-128~127) | `Integer.valueOf()` | 利用缓存减少对象创建 | | 不确定输入是否有效 | 结合 `try-catch` | 统一处理 `NumberFormatException` | > **经验法则**:需要 `int` 用 `parseInt`,需要 `Integer` 用 `valueOf`([^5]) --- #### 示例场景对比 ```java // 场景1:简单计算(parseInt 更优) int total = Integer.parseInt("500") * 2; // 场景2:放入HashMap(valueOf 更优) Map<String, Integer> map = new HashMap<>(); map.put("count", Integer.valueOf("100")); // 场景3:缓存优化(valueOf 节省内存) List<Integer> smallNumbers = Arrays.asList( Integer.valueOf("100"), Integer.valueOf("100") // 复用缓存对象 ); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值