Integer.valueof()和Integer.parseInt()的区别

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. 正数的边界值为1至0x7fffffff;负数的边界值为-1至0x80000000;
  2. 代码中将所有数据当做负数(正数)来处理,最后处理符号问题;
  3. 方法中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 //不会用到缓存

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值