个人博客:www.letus179.com
Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer,从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。
Java 为每个原始类型提供了包装类型:
- 原始类型:
boolean,char,byte,short,int,long,float,double - 包装类型:
Boolean,Character,Byte,Short,Integer,Long,Float,Double
两个常见的面试例子
先看两个常见的例子,后面会针对例子加以分析。
例1
public static void main(String[] args) {
Integer a = new Integer(8);
Integer b = 8;
int c = 8;
System.out.println(a == b);
System.out.println(a == c);
}
执行结果: false, true
例2
public static void main(String[] args) {
Integer f1 = 100, f2 = 100, f3 = 250, f4 = 250;
System.out.println(f1 == f2);
System.out.println(f3 == f4);
}
执行结果: true, false
知识点一:自动拆箱与自动包装
概念
1.自动拆箱: 自动将包装器类型转换为基本数据类型;
2.自动包装: 自动将基本数据类型转换为包装器类型。
具体分析
例1中
Integer b = 8;
//自动装箱
//Integer a = new Integer(8);
//int c = 8;
System.out.println(a == c) // 自动拆箱
注意:
Integer与int比较时,会把Integer类型变量拆箱成int类型,然后比较。拆箱调用的是intValue()方法。
对例1反编译看看(这里用jad来反编译), Test是例子中的类名。
下面命令将输出带字节码注释和源码
-a表示用JVM字节格式来注解输出;-o表示无需确认直接覆盖输出;-s表示定义输出文件的扩展名,默认的扩展名是jad;java表示我们想要的反编译后输出java格式文件
jad详细命令参见反编译小工具:jad常用命令介绍
jad -a -o -s java Test.class
反编译结果:
package test;
import java.io.PrintStream;
public class Test {
public Test() {
//
0
0:aload_0
//
1
1:invokespecial
#8
<Method void Object()>
//
2
4:return
}
public static void main(String args[]) {
Integer a = new Integer(8);
//
0
0:new
#16
<Class Integer>
//
1
3:dup
//
2
4:bipush
8
//
3
6:invokespecial
#18
<Method void Integer(int)>
//
4
9:astore_1
Integer b = Integer.valueOf(8);
//
5
10:bipush
8
//
6
12:invokestatic
#21
<Method Integer Integer.valueOf(int)>
//
7
15:astore_2
int c = 8;
//
8
16:bipush
8
//
9
18:istore_3
System.out.println(a == b);
//
10
19:getstatic
#25
<Field PrintStream System.out>
//
11
22:aload_1
//
12
23:aload_2
//
13
24:if_acmpne
31
//
14
27:iconst_1
//
15
28:goto
32
//
16
31:iconst_0
//
17
32:invokevirtual
#31
<Method void PrintStream.println(boolean)>
System.out.println(a.intValue() == c);
//
18
35:getstatic
#25
<Field PrintStream System.out>
//
19
38:aload_1
//
20
39:invokevirtual
#37
<Method int Integer.intValue()>
//
21
42:iload_3
//
22
43:icmpne
50
//
23
46:iconst_1
//
24
47:goto
51
//
25
50:iconst_0
//
26
51:invokevirtual
#31
<Method void PrintStream.println(boolean)>
//
27
54:return
}
}
可以看到第20, 22行,调用了Integer方法.valueOf(int)自动装箱:
Integer b = 8;
Integer b = Integer.valueOf(8);
第36行,调用了Integer方法.intValue()自动拆箱:
System.out.println(a == c);
System.out.println(a.intValue() == c);
所以:a == c的结果为true
例1中
Integer a = new Integer(8);
Integer b = 8;
System.out.println(a == b);
结果为何为false?
刚讲到了
Integer b = 8;
调用了Integer方法.valueOf(int)自动装箱,我们来看下.valueOf(int)源码实现:
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
也就是说最后会new出来一个Integer对象或者返回缓存中的数据。
注意:
+ ==符号在比较对象时,比较的是内存地址;
+ 对于原始数据类型(如上面a == c)直接比对的是数据值。
这里又涉及到了堆栈内存了,需要清楚2点:
1. new出来的对象或创建的数组会在堆中开辟内存空间;
2. 对象的引用(即对象在堆内存中的地址,如a)和基本数据类型存储在栈中;
由此可知a,b引用指向的对象不是同一个,所以结果是false
知识点二:Integer缓存
在上面的.valueOf(int)源码中我们能看到IntegerCache类,看名称就知道是和缓存有关。我们来看下Integer类的静态内部类IntegerCache源码实现:
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) {
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);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
该类中有一个静态数组:
static final Integer cache[];
还有一个静态代码块:
static {...}
既然是在static静态类的静态代码快中,也就是说在类加载的时候就会执行这部分代码逻辑。我们可以看到静态代码快主要是向静态数组中添加了[-128,127],也就要是说,调用方法.valueOf(int)传入的int值在[-128,127]这个范围内时,直接从IntegerCache的缓存数组中获取, 不会去在堆内存中new。
[-128,127]期间的数字比较常用,这一行为有助于节省内存、提高性能。
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
所以在例2中:
1.自动装箱调用方法Integer.valueOf(int)
public static void main(String args[])
{
Integer f1 = Integer.valueOf(100);
//
0
0:bipush
100
//
1
2:invokestatic
#16
<Method Integer Integer.valueOf(int)>
//
2
5:astore_1
Integer f2 = Integer.valueOf(100);
//
3
6:bipush
100
//
4
8:invokestatic
#16
<Method Integer Integer.valueOf(int)>
//
5
11:astore_2
Integer f3 = Integer.valueOf(250);
//
6
12:sipush
250
//
7
15:invokestatic
#16
<Method Integer Integer.valueOf(int)>
//
8
18:astore_3
Integer f4 = Integer.valueOf(250);
//
9
19:sipush
250
//
10
22:invokestatic
#16
<Method Integer Integer.valueOf(int)>
//
11
25:astore
4
}
2.通过Integer.valueOf(int)内部调用IntegerCache类实现。
由于f1,f2对应的基本值在[-128,127]之间,结果返回true;
而f3,f4对应的基本值不在范围内,结果返回false
其他的包装类型也可以类似分析。
最后
以上就是无辜胡萝卜最近收集整理的关于int和Integer解析的全部内容,更多相关int和Integer解析内容请搜索靠谱客的其他文章。
发表评论 取消回复