Integer.parseInt(s)和Integer.valueOf(s)之间的区别
前言
今天再看Integer源码的时候,发现了一个问题, Integer.parseInt(s)和Integer.valueOf(s)都可以将字符串解析成int类型。那为啥要提供两个不同的方法了,于是去看了一下他们两个方法的源代码实现
Integer.parseInt(s)
返回值是int类型
public static int parseInt(String s) throws NumberFormatException { return parseInt(s,10); }
这里可以看出还有一个重载的方法,如果传入一个参数,那么第二个参数就是默认是10,这里10代表的是10进制。当然可以自己选择进制。
String a = "11"; System.out.println(Integer.parseInt(a)); //默认十进制 结果是 11 System.out.println(Integer.parseInt(a,2)); //选择2进制 结果是3
所以咱们这里也不深入研究源码,这个不是本篇文章讨论的范围,现在就是知道最终调用的是paseInt()方法。
Integer.valueOf(s)
话不多说,先看源码
public static Integer valueOf(String s) throws NumberFormatException { return Integer.valueOf(parseInt(s, 10)); }
从源码可以看出valueOf方法也是调用的parseInt()方法,也同样可以选择进制,不选就默认十进制。所以两个方法之间的区别就在于valueOf封装的那一层方法了。我们来看下这个方法。
这里的参数int i 就是parseInt返回的结果,首先对i做了判断,如果i在-128到127之间,那么就返回缓存的Integer对象,说明源码里面再加载Integer类的时候就缓存了从-128到127的Integer对象,用到的时候直接返回对象,如果不在这个范围之间就重新生成一个Integer对象。
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
//java虚拟机里面除了字符串常量池还有一个常用的数字常量池,其范围是-128~127, // 所以如果Integer指向这个范围内的数字在编译的时候会直接指向常量池中的数字,而不会创建新的对象 class Main{ public static void main(String[] args) { // Integer a = new Integer(2); // Integer b = new Integer(2); //无论是否在num[-128,127]之间,a都不等于b,因为new了 // Integer a = 128; // Integer b = 128;//在num[-127,128]之间,为true,否则为false Integer a = Integer.valueOf(127); Integer b = Integer.valueOf(127);//作用与直接赋值:Integer a = 127一样,均返回true System.out.println(a==b); } }
结论
从上面的分析可以看出
Integer.valueOf(s)在Integer.parseInt(s)的计算的基础上,将int类型的数值转换成了Integer类型,这样转换的意义大家也应该都知道,就是基本类型的包装类型是引用类型。所以单单为了为了得到一个int值,就用Integer.parseInt(s)就行了。如果为了得到包装类型就用Integer.valueOf(s)。毕竟缓存了一部分数值,可以加强点儿性能。
引申:字符串常量池
字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价。JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。为 了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池,每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中, 就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。Java能够进行这样的优化是因为字符串是不可变的,可以不用担心数据冲突 进行共享,但如果new String的话,则仍会开辟新空间,不会复用.
String.intern()
由于intern()操作每次都需要与常量池中的数据进行比较以查看常量池中是否存在等值数据,同时JVM需要确保常量池中的数据的唯一性,这就涉及到加锁机制,这些操作都是有需要占用CPU时间的,所以如果进行intern操作的是大量不会被重复利用的String的话,则有点得不偿失。由此可见,String.intern()主要 适用于只有有限值,并且这些有限值会被重复利用的场景,如数据库表中的列名、人的姓氏、编码类型等。
总结:
String.intern()方法是一种手动将字符串加入常量池中的方法,原理如下:如果在常量池中存在与调用intern()方法的字符串等值的字符串,就直接返回常量池中相应字符串的引用,否则在常量池中复制一份该字符串,并将其引用返回(Jdk7中会直接在常量池中保存当前字符串的引用);Jdk6 中常量池位于PremGen区,大小受限,不建议使用String.intern()方法,不过Jdk7 将常量池移到了Java堆区,大小可控,可以重新考虑使用String.intern()方法,但是由对比测试可知,使用该方法的耗时不容忽视,所以需要慎重考虑该方法的使用;String.intern()方法主要适用于程序中需要保存有限个会被反复使用的值的场景,这样可以减少内存消耗,同时在进行比较操作时减少时耗,提高程序性能。