java回忆录——运行看泛型?1==1?200==200?1+1=2?

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_22063697/article/details/52165689

大家是不是都是看到标题来的呢?好奇心?好吧,不管出于什么原因,都请大家看完这篇文章,让你对 java 的源代码和反射有更深的理解,哈哈,好像已经说出来了,这篇文章是继于上篇反射入门的补充。

第一个问题:泛型真的能够限制吗?

需求:我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢??

大家一看,这不明摆着不行吗,往集合中添加一个integer那行,当我往集合中添加一个字符串的时候,编译器就直接报错了。表面上是这样的,让我们来看看 ArrayList 中 add()方法的源码:

这里写图片描述

可以看到 add()方法传的参数为泛型,有就是说我们在创建 ArrayList 的对象时指定的泛型只在编译的时候起作用,这时我们就想到了反射,因为反射是在运行时起作用的。目标就是利用反射调用 add()方法,将字符串作为参数传进去,不就实现了吗?

public class Test {
    public static void main(String[] args) {  
        ArrayList<Integer> list = new ArrayList<>();
        //添加一个Integer
        list.add(10);
        //获取ArrayList的Class对象
        Class c = list.getClass();
        //获取ArrayList的add方法,参数为泛型,所以传入个Object的类类型
        Method add = c.getMethod("add", Object.class);
        //调用add方法将字符串添加进去
        add.invoke(list, "qiuqing");
        //打印该ArrayList
        System.out.println(list);   
    }

结果:

这里写图片描述

第二个问题:1 真的和 1 相等吗?

大家先来看看几个简单的例子:

    int a = 1;
    int b = 1;
    System.out.println(1==1);

这个结果肯定是 true ,因为这是基本类型的比较,==比较的是内容,所以为 true。

    Integer a = new Integer(1);
    Integer b = new Integer(1);
    System.out.println(a==b);

这个结果是 false,应该也不难,Integer 为包装器类型,属于引用类型,==比较的是地址,因为使用了 new 关键字,肯定要在堆中开辟一块内存,所以内存地址肯定不一样。

        Integer a = 1;
        Integer b = 1;
        System.out.println(a==b);

这个结果有的人不一定知道,结果为 true,为什么呢?我们都知道左边类型为 Integer ,右边为1是 int,并且在JDK1.5之后基本数据类型和包装器类型之间存在自动装箱和拆箱。

那么右边的自动装箱为 new Integer(1),那就和第二个例子一样,结果不应该是 false 吗?怎么为 true呢?

我们知道自动装箱的时候应该会调用 valueOf(int i)方法,那我们来看一下 Integer 中的 valueOf(int i)方法:

这里写图片描述

可以知道 low = -128,high = 127,当前 i = 1,满足判断条件,可以看到返回了一个从 cache 数组的值。这里返回 cache[129];

那么这个 cache 数组到底多大,到底存了哪些值呢?

这里写图片描述

可以看到 cache 数组是属于 Integer 的内部类 IntegerCache 的,而且可以算出 cache 数组大小为 256 ,并且存的数为 -128 到 127 。

这下我们知道当为 1 的时候返回的是 cache[129] = 1;当 == 比较的时候,两个 1 为 Integer 类型会自动拆箱,这时就和第一个例子一样了,基本数据类型比较,所以为 true。

        Integer a = 200;
        Integer b = 200;
        System.out.println(a==b);

第三个例子听懂了,这个应该很简单了,结果为 false,为什么?因为 200 不在 -128 到 127 之间,所以判断条件不满足,应该是走下面这条语句:

这里写图片描述

记住包装器的缓存:

Boolean:(全部缓存)

Byte:(全部缓存)

Character(<= 127缓存)

Short(-128 — 127缓存)

Long(-128 — 127缓存)

Integer(-128 — 127缓存)

Float(没有缓存)

Doulbe(没有缓存)

第三个问题:1 + 1 永远等于 2 吗?

相信大家看完上面这个例子,这个题应该不难吧,可以利用这个缓存数组来做点手脚,是的,就是结合上篇文章讲的反射将缓存数组反射出来。

需要利用反射更改Integer中的缓存,具体步骤:

1、 获取Integer中的内部静态类IntegerCache;

2、获取cache域;

3、myCache.setAccessible(true)是取消jvm的变量修饰检查,虽然cache数组是static final类型,我们仍然可以修改其数组元素;

4、获取数组对象;

5、数组范围默认是-128到127,阅读源码可知,-128到127之间的、并且通过Integer.valueOf得到的Integer值是直接使用缓存的;cache[130]对应的是Integer(2),可将其修改为Integer(3);

6、System.out.printf的函数接口是PrintStream.printf(String format, Object… args),由于入参为Object,1 + 1会被自动装箱,从而输出Integer.valueOf(2) = cache[130] = cache[131] = 3。

        Class cache = Integer.class.getDeclaredClasses()[0]; 
        Field myCache = cache.getDeclaredField("cache");
        myCache.setAccessible(true);
        Integer[] newCache = (Integer[]) myCache.get(cache);
        newCache[130] = newCache[131];
        int e = 1;
        int f = e + e;
        System.out.printf("%d + %d = %d",e,e,f);

结果:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_22063697/article/details/52165689