《Thinking in Java》十四章类型信息_习题解

1~10    Page 318

练习1. 在ToyTest.java中,将Toy的默认构造器注释掉,并解释发生的现象。

书中代码如下(略有改动):

 1 package org.cc.foo_008;
 2 
 3 public class ToyTest {
 4 
 5     static void printInfo(Class c){
 6         log("Class Name: "+c.getName());
 7         log("Is interface? "+c.isInterface());
 8         log("Simple name: "+c.getSimpleName());
 9         log("Canonical name: "+c.getCanonicalName());
10         log();
11     }
12     
13     public static void main(String[] args) {
14         
15         Class c=null;
16         
17         try {
18             c=Class.forName("org.cc.foo_008.FancyToy");
19         } catch (ClassNotFoundException e) {
20             e.printStackTrace();
21         }
22         printInfo(c);
23         for(Class face:c.getInterfaces()){
24             printInfo(face);
25         }
26         Class up=c.getSuperclass();
27         Object obj=null;
28         try {
29             obj=up.newInstance();
30         } catch (InstantiationException | IllegalAccessException e) {
31             e.printStackTrace();
32         }
33         
34         printInfo(obj.getClass());
35         
36     }
37     
38     
39     public static void log(Object ...args){
40         for(Object o:args) System.out.print(o+" ");
41         System.out.println();
42     }
43     
44 }
45 
46 interface HasBatteries {}
47 interface Waterprood {}
48 interface Shoots {}
49 
50 class Toy{
51     Toy(int i) {
52     }
53 //    Toy() {
54 //    }
55 }
56 
57 class FancyToy extends Toy implements HasBatteries, Waterprood, Shoots{
58     FancyToy() {
59         super(1);
60     }
61 }

 将Toy的空构造器注释之后就会出现

 

 首先查看这个异常在什么情况下会被抛出呢:

当应用程序试图使用Foo.class.newInstance()实例化但是失败的时候抛出此异常,可能但不限于的原因也许是:

- 试图实例化的是一个抽象类或接口、原始类型(基本类型)、void。

- 这个类没有默认的无参构造器。

好了,看到这里基本上就可以确定是没有无参构造器引起的异常了,那么为什么呢?

我们跟进执行过程看一下:

这个是newInstance()方法:

 在412行的时候调用了一个私有的方法getConstructor0来获取构造器(顺带一提,好多私有的内部实现都是加个0,难道是什么约定俗成的东西么...)

传入了一个长度为0的Class数组和一个Member.DECLARED,我们先打个岔来看一下Member是干嘛的:

它是用来标识单个成员(方法或变量)或构造器的识别信息的,比如里面的那个PUBLIC常量的意思是访问修饰符为public的,不论是本类的还是继承而来的,而DECLARED的意思就是说只有在本类中显示声明的,不是继承来的,用白话说就是只有你写在Class名那个花括号里面的才算数,其它都不算数。

可以看到这个接口的继承树也很实在:

这几个类都是Member。

好了,我们已经知道了Member.DECLARED的意思是只有写在本类中的才算数,继续看getConstructor0()方法:

可以看到这个方法是使用构造器的参数类型访问类型来确定到底是选择哪一个构造器的,newInstance()方法中调用的时候传进来一个空的Class数组和Member.DECLARED的意思就是说我要找这个类的写上去的参数数量为0即没有参数的构造器(当你没有其它构造器的时候默认无参构造器会自动给你写上去的,所以也算数):

哇去下面的感觉好复杂搞得我有点晕,这个方法的基本逻辑就是获得所有的构造器然后遍历,看看能不能找到,如果找不到就抛出一个NosuchMethodException(),然后结合上上上面的图newInstance()方法:

 

 一个catch到一个NosuchMethodException()就抛出一个new InstantiationException()异常,所以这个异常就是这么来的,总结起来就一句话,没找到无参构造器

 

为什么没找到无参构造器就要抛出异常呢?

这个是newInstance()得到无参构造器之后的逻辑(之前会进行一次判断,因为有一个缓存,如果缓存为空的话才会获得,否则的话就获取填到缓存,所以同一个Class<?>的newInstance()只会在第一次被调用的时候去获取无参构造器,之后就使用缓存了):

很明白了,因为我要用这个构造器来实例化对象啊。

为什么一定要使用这个构造器呢?

因为你丫没给我传递参数我只好使用无参构造器来实例化了....

但是newInstance()没有提供传递参数的重载,不过我们可以手动的得到所有的构造器然后使用有参数的,比如:

 

 

 练习2.

  会。

 

练习3.

编译都通不过,IDE还是很智能的,我猜判断依据的算法是如果它们之间有继承关系的话顺着一个往继承链顶端找必然能找到另一个,比如A、B,B继承了A,顺着A->Object to find B || B->Object find A 必然有一个是成立的。嗯,又胡乱猜测了一条,在进行类型转换前进行编译器检查,这样可以尽量的将可能的错误提前发现,然而这一条对于泛型基本没什么卵用,而只要明确的知道了两个类型就可以判定他们是不是可以进行类型转换了,大概算法如下:

但是这个样呢是不能判断接口的,最主要的是接口是多继承的并且接口的getSuperClass()可能会返回null,如果是多继承的话从下面往上找就是一颗颗的树了,可能会很大很大

 

练习4.

 

转载于:https://www.cnblogs.com/cc11001100/p/5826181.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
写在前面的话 引言 1. 前提 2. Java的学习 3. 目标 4. 联机文档 5. 节 6. 练习 7. 多媒体 8. 源代码 9. 编码样式 10. Java版本 11. 课程和培训 12. 错误 13. 封面设计 14. 致谢 第1 对象入门 1.1 抽象的进步 1.2 对象的接口 1.3 实现方案的隐藏 1.4 方案的重复使用 1.5 继承:重新使用接口 1.5.1 改善基础类 1.5.2 等价和类似关系 1.6 多形对象的互换使用 1.6.1 动态绑定 1.6.2 抽象的基础类和接口 1.7 对象的创建和存在时间 1.7.1 集合与继承器 1.7.2 单根结构 1.7.3 集合库与方便使用集合 1.7.4 清除时的困境:由谁负责清除? 1.8 违例控制:决错误 1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段1:要制作什么? 1.12.4 阶段2:开始构建? 1.12.5 阶段3:正式创建 1.12.6 阶段4:校订 1.12.7 计划的回报 1.13 Java还是C++? 第2 一切都是对象 2.1 用句柄操纵对象 2.2 必须创建所有对象 2.2.1 保存在什么地方 2.2.2 特殊情况:主类型 2.2.3 Java中的数组 2.3 绝对不要清除对象 2.3.1 作用域 2.3.2 对象的作用域 2.4 新建数据类型:类 2.4.1 字段和方法 2.5 方法、自变量和返回值 2.5.1 自变量列表 2.6 构建Java程序 2.6.1 名字的可见性 2.6.2 使用其他组件 2.6.3 static关键字 2.7 我们的第一个Java程序 2.8 注释和嵌入文档 2.8.1 注释文档 2.8.2 具体语法 2.8.3 嵌入 2.8.4 @see:引用其他类 2.8.5 类文档标记 2.8.6 变量文档标记 2.8.7 方法文档标记 2.8.8 文档示例 2.9 编码样式 2.10 总结 2.11 练习 第3 控制程序流程 3.1 使用Java运算符 3.1.1 优先级 3.1.2 赋值 3.1.3 算术运算符 3.1.4 自动递增和递减 3.1.5 关系运算符 3.1.6 逻辑运算符 3.1.7 按位运算符 3.1.8 移位运算符 3.1.9 三元if-else运算符 3.1.10 逗号运算符 3.1.11 字串运算符 3.1.12 运算符常规操作规则 3.1.13 造型运算符 3.1.14 Java没有“sizeof” 3.1.15 复习计算顺序 3.1.16 运算符总结 3.2 执行控制 3.2.1 真和假 3.2.3 反复 3.2.6 中断和继续 3.2.7 切换 3.3 总结 3.4 练习 第4 初始化和清除 4.1 由构建器保证初始化 4.2 方法过载 4.2.1 区分过载方法 4.2.2 主类型的过载 4.2.3 返回值过载 4.2.4 默认构建器 4.2.5 this关键字 4.3 清除:收尾和垃圾收集 4.3.1 finalize()用途何在 4.3.2 必须执行清除 4.4 成员初始化 4.4.1 规定初始化 4.4.2 构建器初始化 4.5 数组初始化 4.5.1 多维数组 4.6 总结 4.7 练习 第5 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:不能接触 5.2.4 protected:“友好的一种” 5.3 接口与实现 5.4 类访问 5.5 总结 5.6 练习 第6 类再生 6.1 合成的语法 6.2 继承的语法 6.2.1 初始化基础类 6.3 合成与继承的结合 6.3.1 确保正确的清除 6.3.2 名字的隐藏 6.4 到底选择合成还是继承 6.6 递增开发 6.7 上溯造型 6.7.1 何谓“上溯造型”? 6.8 final关键字 6.8.1 final数据 6.8.2 final方法 6.8.3 final类 6.8.4 final的注意事项 6.9 初始化和类装载 6.9.1 继承初始化 6.10 总结 6.11 练习 第7 多形性 7.1 上溯造型 7.1.1 为什么要上溯造型 7.2 深入理 7.2.1 方法调用的绑定 7.2.2 产生正确的行为 7.2.3 扩展性 7.3 覆盖与过载 7.4 抽象类和
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值