java回忆录——运行看泛型?1==1?200==200?1+1=2?
Posted minigeek
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java回忆录——运行看泛型?1==1?200==200?1+1=2?相关的知识,希望对你有一定的参考价值。
大家是不是都是看到标题来的呢?好奇心?好吧,不管出于什么原因,都请大家看完这篇文章,让你对 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);
结果:
以上是关于java回忆录——运行看泛型?1==1?200==200?1+1=2?的主要内容,如果未能解决你的问题,请参考以下文章