我是靠谱客的博主 无辜胡萝卜,最近开发中收集的这篇文章主要介绍int和Integer解析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

个人博客:www.letus179.com

Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer,从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。

Java 为每个原始类型提供了包装类型:

  • 原始类型: booleancharbyteshortintlongfloatdouble
  • 包装类型:BooleanCharacterByteShortIntegerLongFloatDouble

两个常见的面试例子

先看两个常见的例子,后面会针对例子加以分析。
例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解析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(34)

评论列表共有 0 条评论

立即
投稿
返回
顶部