A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© mk7 中级黑马   /  2013-6-26 10:18  /  2873 人查看  /  16 人回复  /   1 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 孙百鑫 于 2013-6-30 08:32 编辑

有几行代码很疑惑  请大家分析一下
  1. ArrayList<String> al = new ArrayList<String>();

  2. al.getClass().getMethod("add", Object.class).invoke(al, 3);
  3. System.out.println(al.get(0));
复制代码
上述代码会报错: java.lang.Integer cannot be cast to java.lang.String


而下面的代码却正常 这是为什么??
  1. ArrayList<Integer> al = new ArrayList<Integer>();
  2. al.getClass().getMethod("add", Object.class).invoke(al, "abc");
  3. System.out.println(al.get(0));
复制代码

评分

参与人数 1技术分 +1 收起 理由
刘凯 + 1 赞一个!

查看全部评分

16 个回复

倒序浏览
  1. ArrayList<String> al = new ArrayList<String>();

  2. al.getClass().getMethod("add", Object.class).invoke(al, 3);//集合是存放对象的,3不是对象,是个基本数据类型。
  3. System.out.println(al.get(0));
复制代码
回复 使用道具 举报

不是有自动装箱与拆箱功能吗

怎么换成al.getClass().getMethod("add", Object.class).invoke(al, new Integer(3)); 也不行

请详细剖析一下 谢谢
回复 使用道具 举报
这是类型转换错误。
你的泛型定义是String类型,你存入的是Integer类型。
就好像String []s = new String[];
s[0]=1;
回复 使用道具 举报
mk7 发表于 2013-6-26 11:01
不是有自动装箱与拆箱功能吗

怎么换成al.getClass().getMethod("add", Object.class).invoke(al, new In ...

试了一下,我的解释好像不正确。误导你了。
回复 使用道具 举报
mk7 发表于 2013-6-26 11:01
不是有自动装箱与拆箱功能吗

怎么换成al.getClass().getMethod("add", Object.class).invoke(al, new In ...

找到实际的原因了,用反射的时候,数字的确装箱存到集合里面了。跟al.get(0)方法有关系,如果直接输出System.out.println(al);是不会报错的。可以看看底层代码,就知道原因了。
回复 使用道具 举报
mk7 中级黑马 2013-6-26 12:24:57
7#
曹德君 发表于 2013-6-26 11:28
找到实际的原因了,用反射的时候,数字的确装箱存到集合里面了。跟al.get(0)方法有关系,如果直接输出Syst ...

底层代码也没看明白 你能给解释一下吗 谢谢
回复 使用道具 举报
  1. public E get(int index) {
  2.         RangeCheck(index);

  3.         return (E) elementData[index];
  4.     }
复制代码
应该是个泛型方法,在去集合的元素的时候,编译器检测到不属于该类型的就报错了。
回复 使用道具 举报
mk7 中级黑马 2013-6-26 15:32:52
9#
十分感谢   不过我还是没弄太明白  
回复 使用道具 举报
楼主 您问的问题很好
开始我也是被你的问题困惑了

我现在解释一下
1.最后一个不打印 仅仅是取出  运行时不会报错  我调试了一下 真真正正地看到了  int类型的3被存入collection1这个引用指向的集合对象中
  1. ArrayList<String> collection1 =new ArrayList<String>();
  2.                 ArrayList<Integer> collection2 =new ArrayList<Integer>();

复制代码
调试结果见 图1同时也证明了:get方法也是没问题的 那就应该是println出了问题
此时修改代码最后一行:System.out.println(collection1.get(0));
2. 这个时候 我想看下println的源代码
先是ctrl+f 打开了System.out.println(collection2.get(0)); 处的println()源码
结果是:
  1. public void println(Object x) {
  2.         String s = String.valueOf(x);
  3.         synchronized (this) {
  4.             print(s);
  5.             newLine();
  6.         }
  7.     }
复制代码
由于 编译阶段  已经确定了你这个参数打印是由哪个println来执行的(println的方法有很多重载:八种基本数据类型+ String + Object)
这里面  由于编译的时候 泛型起作用(运行时不起作用)  Integer不属于八种基本数据类型和String  所以 ctrl+f跳转到 public void println(Object x) 这个方法
所以 String s = String.valueOf(x); 会把你的Integer的对象转换成String类型   
毕老师讲过:valueOf可以把任意数据类型转换成String  (实际上这个方法也是重载的) 所以 后面不会报错
下面我说下 为什么会打印出来"abc"
这个时候 我又一次打开了此处valueOf对应的源码:
  1. public static String valueOf(Object obj) {
  2.         return (obj == null) ? "null" : obj.toString();
  3.     }
复制代码
但是 运行时传进来的实际指向String的对象
所以 打印出来的是abc

****************************
但是:
ctrl+f 打开了System.out.println(collection1.get(0));的println的源代码:
  1. public void println(String x) {
  2.         synchronized (this) {
  3.             print(x);
  4.             newLine();
  5.         }
  6.     }
复制代码
这里直接跳转到了参数为String的prrintln的另一个重载方法
可以看到  由于 collection1在编译的时候 实例化参数是String 此时Javac就将这个打印这个集合中元素的方法定位到println(String x)
这个时候  你传进来的是Integer的对象(3) 赋值给参数String x
Intege y =new Integer(3);
也就相当于  String x = y; 所以 运行时会报错!

***********************************************************
这时候编译不报错的原因就是张孝祥老师讲的 泛型在运行的时候不起作用
以下面的为例:javac仅仅按照代码行来检查  如果这一行有多次成员调用  只要语法规则不错就行  不会考虑行与行之间的关系
collection2.getClass().getMethod("add", Object.class).invoke(collection2, "abc");
这里面 javac首先检查 collection2是一个对象 所以 一定有相应的class对象存在  所以collection2.getClass()这段语法正确
getClass()返回Class对象  所以可以调用getMethod()方法  折页编译通过  此时 又检查参数是不是和getMethod声明的样子一不一样(还是语法检查)
public Method getMethod(String, Class<T>...)  这个是getMethod()的声明
实际调用 是getMethod("add", Object.class)  javac一看 "add"是String类型的  Object.class是Class对象  所以 这也通过
最后 invoke(collection2, "abc")传入得实参也是符合invoke的API声明的  所以 javac就认为  这行代码语法正确
这就导致了 整个编译的正确  但是 运行之后取出来的时候  表面上是String类型的对象 但是 实际上是Integer  一传给println(String x)的
参数 x JVM就没办法接受这个骗局了 无法运行  所以 运行时抛出了 异常 但是  javac编译正确的现象

Debug Result.jpg (31.78 KB, 下载次数: 1)

图1 调试结果

图1 调试结果

评分

参与人数 1技术分 +2 收起 理由
刘凯 + 2 一如既往的给力啊 , 流程走的怎么样了?.

查看全部评分

回复 使用道具 举报
mk7 中级黑马 2013-6-26 22:11:57
11#
张歆明 发表于 2013-6-26 17:35
楼主 您问的问题很好
开始我也是被你的问题困惑了

太给力了 谢谢啊
回复 使用道具 举报
mk7 发表于 2013-6-26 22:11
太给力了 谢谢啊

好 能帮到你很高兴 我也学习了 不客气 多多交流:)
回复 使用道具 举报
楼主您好,帖子长时间没有动态我已经将您的帖子改成已解决。如有问题请私密我哦~
回复 使用道具 举报
张歆明 发表于 2013-6-26 17:35
楼主 您问的问题很好
开始我也是被你的问题困惑了

刚遇到,看见了你的回答,佩服佩服
回复 使用道具 举报
张洪慊 发表于 2013-7-23 19:34
刚遇到,看见了你的回答,佩服佩服

过奖了 我也是研究了半天 呵呵  常交流 呵呵
回复 使用道具 举报
张歆明 发表于 2013-7-23 19:38
过奖了 我也是研究了半天 呵呵  常交流 呵呵

得,QQ拿来
回复 使用道具 举报
我的是 245950700:Excalibur
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马