源码说明 Java中Integer是基本数据类型int的包装类。也就是每一个Integer对象包含一个int类型的属性,是抽象类Number类的子类,位于java.lang包下。
部分源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public final class Integer extends Number implements Comparable <Integer > { @Native public static final int MIN_VALUE = 0x80000000 ; @Native public static final int MAX_VALUE = 0x7fffffff ; private final int value; public Integer (int value) { this .value = value; } public Integer (String s) throws NumberFormatException { this .value = parseInt(s, 10 ); } }
通过源码可以看出,最小值用十六进制表示为0x80000000
。使用二进制表示为1000 0000 0000 0000 0000 0000 0000 0000
.最大值用十六进制表示为0x7fffffff
。 使用二进制表示为 0111 1111 1111 1111 1111 1111 1111 1111
. 由于一个字节占用8位,而Integer的最大值和最小值都是占用32位,由此而知,int类型是占用4个字节的。最小值为 -2的31次方。最大值为2的31次方-1.
为何0x80000000
可以表示最小值,可以参考这篇文章 原码,反码和补码 .
面试题 我们首先来看一道题:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class IntegerTest { public static void main (String[] args) { Integer i1 = 127 ; Integer i2 = new Integer(127 ); Integer i3 = 127 ; System.out.println(i1 == i2); System.out.println(i1.equals(i2)); System.out.println(i1 == i3); Integer ii1 = 128 ; Integer ii2 = 128 ; System.out.println(ii1 == ii2); } }
结果是:
不知道你们有没有答对呢?下面我们来看一下反编译过来的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class IntegerTest { public static void main (String[] args) { Integer i1 = Integer.valueOf(127 ); Integer i2 = new Integer(127 ); Integer i3 = Integer.valueOf(127 ); System.out.println((i1 == i2)); System.out.println(i1.equals(i2)); System.out.println((i1 == i3)); Integer ii1 = Integer.valueOf(128 ); Integer ii2 = Integer.valueOf(128 ); System.out.println((ii1 == ii2)); } }
可以看出 i1 和 i3 都调用了Integer.valueOf方法,这其实就是自动装箱的过程.Java基本数据自动转为包装类的过程称为自动装箱 。
我们看一下valueOf()方法的源码:
1 2 3 4 5 public static Integer valueOf (int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
我们看到其中有一个IntegerCache类,我们再看一下源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 private static class IntegerCache { static final int low = -128 ; static final int high; static final Integer cache[]; static { 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 ); h = Math.min(i, Integer.MAX_VALUE - (-low) -1 ); } catch ( NumberFormatException nfe) {} } high = h; cache = new Integer[(high - low) + 1 ]; int j = low; for (int k = 0 ; k < cache.length; k++) cache[k] = new Integer(j++); assert IntegerCache.high >= 127 ; } private IntegerCache () {} }
可以看出其中有一步是:sun.misc.vm.getSavedProperty("java.lang.Integer.IntegerCache.high")
.
实际上在Java5中引入这个特性的时候,范围固定在-128到127之间。后来在Java6后,最大映射到 java.lang.Integer.IntegerCache.high, 可以使用JVM的启动参数设置最大值。(通过JVM的启动参数 -XX:AutoBoxCacheMax=size 修改)
我们暂时把自定义部分去掉,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private static class IntegerCache { static final int low = -128 ; static final int high; static final Integer cache[]; static { int h = 127 ; high = h; cache = new Integer[(high - low) + 1 ]; int j = low; for (int k = 0 ; k < cache.length; k++) cache[k] = new Integer(j++); assert IntegerCache.high >= 127 ; } private IntegerCache () {} }
以上就是Integer缓存池的源代码。为什么长度要加上1呢?因为需要把整数0算上。我们通过源码可以看到,对于Integer类型,内部有一个缓存池,实现把-128到127之间的对象先创建出来了。所以如果是通过调用valueOf()方法创建并且在-128到127之间的数,自动返回缓存中的对象,而由于自动装箱调用的就是valueOf方法。
下面我们再次来看下面的题:
1 2 3 4 5 6 7 8 9 Integer i1 = 127 ; Integer i2 = new Integer(127 ); Integer i3 = 127 ; System.out.println(i1 == i2); System.out.println(i1.equals(i2)); System.out.println(i1 == i3); Integer ii1 = 128 ; Integer ii2 = 128 ; System.out.println(ii1 == ii2);
由于i1,i3 是自动装箱,调用了Integer.valueOf(),同时在默认返回下-128–127范围内,所以走的是缓存,故i1==i3为true (等于号是直接比较地址的 )。而i2是自己创建对象,不是调用valueOf方法的,那么就不会走缓存,所以 i1 == i2 是false。那么为什么i1.equals(i2) 也会是true。我们看一下Integer类 valueOf()源码:
1 2 3 4 5 6 public boolean equals (Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false ; }
通过源码可以看出,首先判断传入的是否为Integer 类型。
是:判断其intValue()值与当前对象的value是否相等
其他:返回false。
总结,使用Integer i = 9 这种方式来定义; 只要给值在-128到127之间,调用自动装箱方法valueOf,这个值都是作为下标处理的,取的是Integer缓存池的对象。
装箱拆箱过程 自动装箱:
上面的代码会自动装箱。编译之后如下:
1 Integer i = Integer.valueOf(2 );
自动拆箱:
1 2 3 int i = 2 ;Integer ii = 2 ; System.out.println(i == ii);
上面的代码经过自动拆箱。即编译之后变成如下:
1 2 3 int i = 2 ;Integer ii = Integer.valueOf(2 ); System.out.println(i == ii.intValue())
试题+1 在牛客上看到这样一个问题,如下:
【JAVA】条件 a == (Integer) 1 && a == (Integer) 2有可能为true吗?
代码可以表示为:
1 2 3 4 5 6 public static void main (String[] args) { int a = 1 ; if (a == (Integer)1 && a == (Integer)2 ){ System.out.println("true" ); } }
乍一看,肯定不可能啊,但是仔细想想呢?
我们先看一下其反编译过来的代码:
1 2 3 int a = 1 ;if (a == Integer.valueOf(1 ).intValue() && a == Integer.valueOf(2 ).intValue()) System.out.println(true );
可以看出来,a == (Integer)1
,这句其实是先将1进行装箱,再进行拆箱与a进行比较。但是由于自动装箱走缓存同时1和2又在-128到127之间,所以我们可以从缓存进行入手。
如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static void main (String[] args) throws Exception { Class<Integer> integerClass = Integer.class ; Class<?> integerCacheClass = integerClass.getDeclaredClasses()[0 ]; Field cacheField = integerCacheClass.getDeclaredField("cache" ); cacheField.setAccessible(true ); Integer[] o = (Integer[])cacheField.get(integerCacheClass); o[130 ] = o[129 ]; System.out.println(Arrays.toString(o)); int a = 1 ; if (a == (Integer)1 && a == (Integer)2 ){ System.out.println(true ); } }
上面的代码即可在最后输入true
. 主要是利用反射来修改IntegerCache的内容。如果前面自动装箱走缓存理解了,那么这里也可以理解,主要是配合反射来修改原来已经初始化好的缓存中的内容。
数组截图:
另:
byte类型占用1个字节。
short类型占用2个字节。
char类型占用2个字节。
int类型占用4个字节。
long类型占用8个字节。
float类型占用4个字节。7到8位有效数字。
double类型占用8个字节。16到17位有效数字。