Integer. valueOf()可以将基本类型int转换为包装类型Integer,或者将String转换成Integer,String如果为Null或“”都会报错。
Integer. valueOf()是高效的
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() {}
}
从他的实现方法可以看出他int在[-128,127]之间的时候他会直接拿缓存,而不会new Integer(),Integer.valueOf()方法基于减少对象创建次数和节省内存的考虑,缓存了[-128,127]之间的数字。此数字范围内传参则直接返回缓存中的对象。在此之外,直接new出来。
下面我们来看一道选择题,就是关于Integer.valueOf()的知识,题目如下:
A.System.out.println(i01== i02);
B.System.out.println(i01== i03);
C.System.out.println(i03== i04);
D.System.out.println(i02== i04);
答案呢,我也做对了,但是也是靠排除法做对的,至于这道题考察的具体细节问题,我当时没考虑,不过等我查看了Integer的相关源码时,茅舍顿开。答案这里我这里先不公布,我们慢慢开始分析。
分析
选项A
选项A中比较的是i01和i02,Integer i01=59这里涉及到自动装箱过程,59是整型常量,经包装使其产生一个引用并存在栈中指向这个整型常量所占的内存,这时i01就是Integer 的引用。
而int i02=59由于int是基本类型,所以不存在引用问题,直接由编译器将其存放在栈中,换一句话说,i02本身就是59。那么System.out.println(i01== i02)结果任何呢?这里涉及到了拆箱的过程,因为等号一边存在基本类型所以编译器后会把另一边的Integer对象拆箱成int型,这时等号两边比较的就是数值大小,所以是true。
好了,到了这里,你有没有想到这样一个问题:如果是Integer i01=59;Integer i02=59;然后System.out.println(i01== i02)的结果是?可能你会说比较数值大小所以相等啊,也有可能说等号两边对象引用,所以比较的是引用,又因为开辟了不同的内存空间,所以引用不同所以返回false。可是正确答案是:true.
再来看这个问题::如果是Integer i01=300;Integer i02=300;然后System.out.println(i01== i02)的结果是? 你可能说上面你不是说了true嘛,怎么还问这样的问题,可是这次的答案是false。你是否会吃惊?大牛除外,我是小白,求不打脸!
解析:当靠想象无法解决问题的时候,这是就要看源代码了!!很重要!我们可以在Integer类中找到这样的嵌套内部类IntegerCache,这个类就是在Integer类装入内存中时,会执行其内部类中静态代码块进行其初始化工作,做的主要工作就是把一字节的整型数据(-128-127)装包成Integer类并把其对应的引用存入到cache数组中,这样在方法区中开辟空间存放这些静态Integer变量,同时静态cache数组也存放在这里,供线程享用,这也称静态缓存。
所以当用Integer 声明初始化变量时,会先判断所赋值的大小是否在-128到127之间,若在,则利用静态缓存中的空间并且返回对应cache数组中对应引用,存放到运行栈中,而不再重新开辟内存。
所以对于Integer i01=59;Integer i02=59;**i01 和 i02是引用并且相等都指向缓存中的数据,所以返回true。而对于**Integer i01=300;Integer i02=300;因为其数据大于127,所以虚拟机会在堆中重新new (开辟新空间)一个 Integer 对象存放300,创建2个对象就会产生2个这样的空间,空间的地址肯定不同导致返回到栈中的引用的只不同。所以System.out.println打印出false。
补充:为什么1个字节的数据范围是-128到127呢,因为Java中数据的表示都是带符号数,所以最高位是用来表示数据的正负,0表示正数,1表示负数,所以正数最大的情况对应的二进制数为:01111111,负数最小对应的二进制数为:10000000.
B选项
从上面的分析,我们已经知道Integer i01=59返回的是指向缓存数据的引用。那么Integer.valueOf(59)返回的是什么或者操作是什么呢?
这个函数的功能就是把int 型转换成Integer,简单说就是装包,那他是新创建一个对象吗?还是像之前利用缓存的呢?有了之前的经验,肯定想到的是利用缓存,这样做既提高程序速度,又节约内存,何乐而不为?
来看一下源代码:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
很明显跟之前的思想一致,若在-128到127范围,直接返回该对象的引用,否则在堆中重新new 一个。
到这,System.out.println(i01== i03)的结果毋庸置疑就是true.
选项C
Integer.valueOf(59)返回的是已缓存的对象的引用,而Integer i04 = new Integer(59)是在堆中新开辟的空间,所以二者的引用的值必然不同,返回false,这道题呢就选C
选项D
System.out.println(i02== i04) i02是整型变量,i04是引用,这里又用到了解包,虚拟机会把i04指向的数据拆箱为整型变量再与之比较,所以比较的是数值,59==59,返回true.
出一道题:
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"));
输出是:
true
false
true
回答#1:
Integer.valueOf(String)确有一个不同寻常的行为。
valueOf会返回一个Integer(整型)对象,当被处理的字符串在-128和127(包含边界)之间时,返回的对象是预先缓存的。这就是为什么第一行的调用会返回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之间的一个值。
有了这个概念,就可以知道上面三行代码之间的区别。
new Integer(123);
显示创建了一个新的Integer对象。
Integer.parseInt("123");
解析完字符串后返回一个int值。
Integer.valueOf("123");
这种情况比其他的要更复杂一些。首先进行了字符串解析,然后如果解析的值位于-128和127之间,就会从静态缓存中返回对象。如果超出了这个范围,就会调用Integer()方法并将解析的值作为参数传入,得到一个新的对象。
现在,让我们看一下问题中的3个表达式。
Integer.valueOf("127")==Integer.valueOf("127");
上面的表达式返回true,因为Integer的值从静态缓存中取了2次,表达式返回了对象与自己比较的结果。因为只有一个Integer对象,所以返回结果为true。
Integer.valueOf("128")==Integer.valueOf("128");
上面的表达式返回false,因为128没有存在静态缓冲区。所以每次在判断相等时等式两边都会创建新的Integer对象。由于两个Integer对象不同,所以==只有等式两边代表同一个对象时才会返回true。因此,上面的等式返回false。
Integer.parseInt("128")==Integer.valueOf("128");
上面的表达式比较的是左边的原始int值128与右边新创建的Integer对象。但是因为int和Integer之间比较是没有意义的,所以Java在进行比较前会将Integer自动拆箱,所以最后进行的是int和int值之间的比较。由于128和自己相等,所以返回true。
注意:此文只适应于jdk7或以上版本,因为jdk6与jdk7的Integer具体实现有差别,详情可查看下源代码.
Integer.parseInt()
Integer.valueof() 和 Integer.parseInt() 的底层都是用的Integer.parseInt(String s ,int radix)这个方法。在这里给这个方法做一下解释。
Integer.parseInt(String s ,int radix),radix用来表示传进来的值是什么进制的,并返回10进制的 int 类型的结果。
比如Integer.parseInt(“A”,16),则输出结果为10进制的10,其中16表示"A"是一个16进制的值。
根据:Character.MIN_RADIX=2和Character.MAX_RADIX=36 则,parseInt(String s, int radix)参数中radix的范围是在2~36之间,超出范围会抛异常。其中s的长度也不能超出7,否则也会抛异常。其中限制在36位之内是因为数字加字母刚好可以表示到36位,比如Integer.parseInt(“Z”,36),输出结果为35。
以下为parseInt(String s ,Int radix)的源代码实现。
/**
* 字符串转换成整数
* @param s 待转换字符串
* @param radix 进制
* @return
*/
public static int parseInt(String s,int radix){
//边界值处理
if(s==null)
throw new NumberFormatException("null");
if(radix<Character.MIN_RADIX){
throw new NumberFormatException("radix "+radix+" less than Character.MIN_RADIX");
}
if(radix>Character.MAX_RADIX){
throw new NumberFormatException("radix "+radix+" greater than Character.MAX_RADIX");
}
//最终返回的结果的负数形式
int result=0;
//判断是否为负数
boolean negative=false;
//字符串偏移指针
int i=0;
int digit;
int max=s.length();
//最大边界值
int limit;
//最大边界值右移一位
int multmin;
if(max>0){
//处理符号
if(s.charAt(0)=='-'){
negative=true;
//边界值为0x80000000
limit=Integer.MIN_VALUE;
i++;
}
else{
//边界值为-0x7fffffff
limit=-Integer.MAX_VALUE;
}
//计算multmin 值 ,multmin = -214748364 负数跟整数的limit是不同的
multmin=limit/radix;
if(i<max){
digit=Character.digit(s.charAt(i++), radix);
if(digit<0){
throw NumberFormatException.forInputString(s);
}
else{
result=-digit;
}
}
//开始循环追加数字,比如输入“123” 10进制数
while(i<max){
//获取字符转换成对应进制的整数,如上,这里第一次循环获取1
//第二次循环获取2
//第三次循环获取3
digit=Character.digit(s.charAt(i++), radix);
if(digit < 0){
throw NumberFormatException.forInputString(s);
}
//判断,在追加后一个数字前,判断其是否能能够在继续追加数字,比如multmin = 123
//那么再继续追加就会变为123*10+下一个数字,就会溢出
if(result < multmin){
throw NumberFormatException.forInputString(s);
}
//第一次循环 result = 0;
//第二次循环 result = -10;
//第三次循环 result = -120;
result*=radix;
if(result<limit+digit){
//第一次循环 limit + digit = -2147483647+1;
//第二次循环 limit + digit = -2147483647+2;
//第三次循环 limit + digit = -2147483647+3;
throw NumberFormatException.forInputString(s);
}
result-=digit;
//第一次循环 result = -1;
//第二次循环 result = -12;
//第三次循环 result = -123;
}
}
else{
throw NumberFormatException.forInputString(s);
}
if(negative){
if(i>1){
return result;
}
else{
throw NumberFormatException.forInputString(s);
}
}
else{
//negative 值为false,所以 -result = -(-123) = 123 返回结果
return -result;
}
}
关键点:
- 正数的边界值为1至0x7fffffff;负数的边界值为-1至0x80000000;
- 代码中将所有数据当做负数(正数)来处理,最后处理符号问题;
- 方法中multmin这个变量是为了在循环中result*=radix不会发生越界;
Integer.parseInt("")
、 Integer.valueOf("")
和new Integer("")
它们之间有什么区别呢?我们可以分别看一下它们的源码
//Integer.parseInt("")
public static int parseInt(String s) throws NumberFormatException {
return parseInt(s,10);
}
//Integer.valueOf("")
public static Integer valueOf(String s) throws NumberFormatException {
return Integer.valueOf(parseInt(s, 10));
}
//new Integer("")
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
从源码中可以看出,Integer.valueOf("")
和Integer.parseInt("")
内部实现是一样的,它们之间唯一的区别就是Integer.valueOf(“”)返回的是一个Integer对象,而Integer.parseInt(“”)返回的是一个基本类型的int。
我们再看Integer.valueOf("")
和new Integer("")
,它们同样返回的是一个Integer对象,但它们又有什么区别呢?我们再进入Integer.valueOf(parseInt(s, 10) )
方法内部:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
我们可以看到,Integer.valueOf("")
会用到IntegerCache
对象,当IntegerCache
中存在时就从cache中取,不存在时才会调用new Integer(i)
构造函数返回一个Integer对象。所以Integer.valueOf("")
会用到cache,其效率可能会比用构造函数new Integer(i)
高。
关于IntegerCache
,在-127~128之间的值都会被cache,所以当我们要的值位于这个区间时返回的都是同一个实例,例如:
System.out.println(Integer.valueOf(5) == Integer.valueOf(5));
System.out.println(Integer.valueOf(500) == Integer.valueOf(500));
输出结果:
true //会用到缓存
false //不会用到缓存