一、废话

先看看这个代码

int a = 1;
if (a == (Integer) 1 && a == (Integer) 2) {
System.out.println("true");
}

在我的idea中,该段代码被标黄了,提示我Condition is always false.

乍一看,确实为false啊,怎么可能a的值即为1,又为2?

可是仔细想一想,这段代码和(a==1&&a==2)还是有很大区别的,至少(a==1&&a==2)肯定为false。

但是这里将基本数据类型转为包装类,转化后的值可不可能是一个值呢?


二、代码

废话不多说,先上输出为true的代码

package com.yang.test;

import java.lang.reflect.Field;

public class Main {
public static void main(String[] args) {
Class cache = Integer.class.getDeclaredClasses()[0];
Field c;
try {
c = cache.getDeclaredField("cache");
c.setAccessible(true);
Integer[] array = (Integer[]) c.get(cache);
array[130] = array[129];
int a = 1;
if (a == (Integer) 1 && a == (Integer) 2) {
System.out.println("true");
}
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}

该段代码确实可以输出为true,真的好神奇啊,到底是怎么实现的呢?


三、实现过程

1、首先,使用

Class cache = Integer.class.getDeclaredClasses()[0];

拿到Integer类中某个内部类,看了Integer类,这个类中只有一个静态内部类,那就是IntegerCache类。

2、接着,使用

c = cache.getDeclaredField("cache");
c.setAccessible(true);
Integer[] array = (Integer[]) c.get(cache);

拿到IntegerCache中的cache变量,并强制转化为Integer数组。

3、这是最重要的一步

array[130] = array[129];

array[129]的值到底是多少?先看一下这个内部类的源码

private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer[] cache;

private IntegerCache() {
}

static {
int h = 127;
//读取配置参数-Djava.lang.Integer.IntegerCache.high的值,一般我们不会去配置,因此直接跳过这段代码,直接看high=h
String integerCacheHighPropValue = VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
int i;
if (integerCacheHighPropValue != null) {
try {
i = Integer.parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
h = Math.min(i, 2147483518);
} catch (NumberFormatException var4) {
}
}

high = h;
//这里的high为127
cache = new Integer[high - -128 + 1];
//cache=new Integer[256];
i = -128;

for(int k = 0; k < cache.length; ++k) {
cache[k] = new Integer(i++);
}

//cache[0]=new Inteher(-128);
//cache[129]=new Inteher(1);
//cache[130]=new Inteher(2);
//cache[255]=new Integer(127);

assert high >= 127;

}
}

现在可以知道,这个内部类中的cache数组,cache[129]=1,cache[130]=2

说到现在,那这个内部类到底干啥的呢?

(Integer) 1,这一句话,将会执行自动装箱操作,相当于调用Integer.valueOf方法

先看integer.valueOf源码

public static Integer valueOf(int i) {
return i >= -128 && i <= Integer.IntegerCache.high ?
Integer.IntegerCache.cache[i + 128] : new Integer(i);
}

此时,如果i属于[-128,127]区间时,将会返回cache数组中对应的值,例如i=0时,返回cache[128],这个值等于0,否则的话,重新创建一个Integer实例。将[-128,127]缓存在Integer内部类IntegerCache里的数组中,有需要直接取,这样的举措能带来效率上的提升。

那么,我们利用反射,修改IntegerCache类里变量cache数组的值,将array[130] = array[129],而array[129]=1。

那么此时将1和2装箱成Integer时,会调用Integer.valueOf方法,(Integer)1返回cache[129],而(Integer)2返回cache(130),

然后a==(Integer)1,基本类型和包装类型比较时,包装类型会自动执行拆箱工作,也就是调用Integer.intValue()方法,该方法返回Integer构造方法中传入的value值。关于装箱与拆箱,可以参考我的另外一篇文章【JAVA】谈谈拆箱与装箱

现在,已经很显而易见了,a现在等于1,(Integer)1等于cache[129],也就是等于new Integer(1),拆箱后等于1

(Integer)2等于cache[130],在我们利用反射修改数组的值后,此时cache[130]也等于new Integer(1),拆箱后还是等于1

因此,上例中确实可以返回true。