楼主 您问的问题很好
开始我也是被你的问题困惑了
我现在解释一下
1.最后一个不打印 仅仅是取出 运行时不会报错 我调试了一下 真真正正地看到了 int类型的3被存入collection1这个引用指向的集合对象中- ArrayList<String> collection1 =new ArrayList<String>();
- 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()源码
结果是:- public void println(Object x) {
- String s = String.valueOf(x);
- synchronized (this) {
- print(s);
- newLine();
- }
- }
复制代码 由于 编译阶段 已经确定了你这个参数打印是由哪个println来执行的(println的方法有很多重载:八种基本数据类型+ String + Object)
这里面 由于编译的时候 泛型起作用(运行时不起作用) Integer不属于八种基本数据类型和String 所以 ctrl+f跳转到 public void println(Object x) 这个方法
所以 String s = String.valueOf(x); 会把你的Integer的对象转换成String类型
毕老师讲过:valueOf可以把任意数据类型转换成String (实际上这个方法也是重载的) 所以 后面不会报错
下面我说下 为什么会打印出来"abc"
这个时候 我又一次打开了此处valueOf对应的源码:- public static String valueOf(Object obj) {
- return (obj == null) ? "null" : obj.toString();
- }
复制代码 但是 运行时传进来的实际指向String的对象
所以 打印出来的是abc
****************************
但是:
ctrl+f 打开了System.out.println(collection1.get(0));的println的源代码:- public void println(String x) {
- synchronized (this) {
- print(x);
- newLine();
- }
- }
复制代码 这里直接跳转到了参数为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编译正确的现象
|
|