int是我们常说的整型数字,是Java的8个原始数据类型之一。Java语言虽然号称一切都是对象,但原始数据类型是例外。
Integer是int对应的包装类,它有一个int类型的字段存储数据,并且提供了基本操作,比如数学运算、int和字符串之间转换等。在Java 5中,引入了自动装箱和自动拆箱功能(boxing/unboxing),Java可以根据上下文,自动进行转换,极大地简化了相关编程。
知识扩展
1、理解自动装箱、拆箱
自动装箱与拆箱实际上算是一种“语法糖”。所谓语法糖,可简单理解为Java平台为我们自动进行了一些转换,保证不同的写法在运行时等价。因此它们是发生在编译阶段的,也就是说生成的字节码是一致的。
对于整数,javac替我们自动把装箱转换为Integer.valueOf(),把拆箱替换为Integer.intValue()。可以通过将代码编译后,再反编译加以证实。
原则上,建议避免无意中的装箱、拆箱行为,尤其是在性能敏感的场合,创建10万个Java对象和10万个整数的开销可不是一个数量级的。当然请注意,只有确定你现在所处的场合是性能敏感的,才需要考虑上述问题。毕竟大多数的代码还是以开发效率为优先的。
顺带说一下,在32位环境下,Integer对象占用内存16字节;在64位环境下则更大。
2、值缓存
就像上一讲谈到的String,Java也为Integer提供了值缓存。
Integer i1 = 1;Integer i2 = Integer.valueOf(2);Integer i3 = new Integer(3);
上述代码中第一行与第二行的写法取值使用了值缓存,而第三行的写法则没有利用值缓存。结合刚刚讲到的自动装箱、拆箱的知识,第一行代码用到的自动装箱,等价于调用了Integer.valueOf()。
不仅仅是Integer,Java也为其它包装类提供了值缓存机制,包括Boolean、Byte、Short和Character等。但与String不同的是,默认都只会将绝对值较小的值放入缓存。以Integer为例,默认情况下只会缓存-128到127之间的值。当然如果你愿意也可以通过以下JVM参数进行设置:
-XX:AutoBoxCacheMax=N
3、原始类型操作线程安全吗?
这个问题的正确答案是“线程不安全”,是否有些出乎你的意料?
原始数据类型的变量,需要使用并发相关手段才能保证线程安全。特别的是,部分比较宽的数据类型,比如long、float、double,甚至不能保证更新操作的原子性,可能出现程序读取到只更新了一半数据位的数值!关于这个话题会在这个专栏后面的并发主题详细介绍。如果有线程安全的计算需要,建议考虑使用类似AtomicInteger、AtomicLong这样线程安全的类。
这个在转换的过程中会调用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);
}
这里有个IntegerCache其实就是在VM启动时,为了加快int类型到Integer的转化速度,在VM启动时就new出一些int类型与Integer的对应关系,方便直接返回Integer,而不用new。new其实是很耗时的。
注意:
这里的valueOf是static的。因为,这时你想要的是Integer对象,所以只能是static的。
这个需要调用Integer的方法intValue()。这个方法不是静态的。
源码如下:
/**
* Returns the value of this {@code Integer} as an
* {@code int}.
*/
public int intValue() {
return value;
}
这样,当Integer转成int时,如果Integer是null的,则可能会抛空指针异常NullPointerException。
Integer integer = 1;
int i = integer;
首先会调用Integer的静态方法valueOf,然后调用integer对象的intValue方法。这时,如果integer对象是null的,在integer.intValue()时,会出现NullPointerException。
javac Test.java
javap -verbose Test
public class Test {
public static void main(String[] args) {
Integer integer = 1;
int i = integer;
System.out.println(i);
}
}
看先反编译代码:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: iconst_1
1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1
5: aload_1
6: invokevirtual #3 // Method java/lang/Integer.intValue:()I
9: istore_2
10: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
13: iload_2
14: invokevirtual #5 // Method java/io/PrintStream.println:(I)V
17: return
一个是invokestatic、一个是invokevirtual。
如果Integer integer = null;的话:
public class Test {
public static void main(String[] args) {
Integer integer = null;
int i = integer;
System.out.println(i);
}
}
反编译后:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: aconst_null
1: astore_1
2: aload_1
3: invokevirtual #2 // Method java/lang/Integer.intValue:()I
6: istore_2
7: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_2
11: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
14: return
intValue()是把Integer对象类型变成int的基础数据类型;
parseInt()是把String 变成int的基础数据类型;
Valueof()是把给定的String参数转化成Integer对象类型;(现在JDK版本支持自动装箱拆箱了。)
intValue()用法与另外两个不同,比如int i = new Integer("123"), j = i.intValue(); 相当于强制类型转换(强制类型转换事实上就是调用的这个方法)。
另外两个用法: Integer.Valueof() , Integer.parseInt() 用的是Interger类名。i.intValue()用的是对象i
另外,
Integer a=new Integer(1)
Integer a=Integer.valueOf(1);
两个都是得到一个Integer对象,但是Integer.valueOf的效率高。