阅读笔记[2] ——《Think In Java》 Chapter 14 (1)
本章主要问题:运行时识别对象和类的信息。主要有两种方法:传统的RTTI;反射机制。
此博文先介绍传统的RTTI。
RTTI(Runtime type identification): 在运行时识别一个对象的类型
Class 对象就是用来创建类的所有的“常规”对象的。类是程序的一部分,每个类都有一个 Class 对象。每当编写并且编译了一个新类,就会产生一个 Class 对象(更确切的说,是被保存在一个同名的 .class 文件中)
JVM 的类加载器子系统,类加载器会对类进行按需加载,如果尚未加载就会根据类名查找 .class 文件。在这个类的字节码被加载时,它们会接受验证,以确保没有被破坏,并且不包含不良的 Java 代码(安全防范目的的措施之一)
Class 类中的常用方法
方法 | 作用 |
---|---|
Class.forName() | 如果类(参数)没有被加载就加载它,它返回一个Class对象,包含类的信息 |
对象.getInterfaces() | 返回一个Class对象(数组),包含感兴趣的class对象中所实现的接口 |
对象.getSuperclass() | 返回一个Class对象,有关父类的信息 |
对象.newInstance()() | 虚拟构造器,“我不知道你的确切类型,但是无论如何要正确地创建你自己”,需要被创建的类中有默认的构造器 |
对象.getName() | 全限定的类名 |
对象.getSimpleName() | 只是类名 |
对象.getCanonicalName() | 同getName() |
对象.getDeclaredFields() | 获取类中的域 |
递归打印出参数对象所在的递归体系,如下
类字面常量,它在编译时就会得到检查,故更安全高效、不需要使用 try 块
类字面常量,不仅可以应用于普通的类,也可以应用于接口、数组以及基本数据类型。另外对于基本数据类型的包装器类,还有一个标准字段 TYPE
注意:使用“.class”来创建 Class 对象的引用时,不会自动初始化该 Class 对象,有如下三个步骤:
- [1] 加载;
- [2] 链接;
- [3]初始化,初始化被延迟到对静态方法(包含构造方法)或者非常数静态域进行首次引用的时候才会执行;说明:对于常数静态域是指带静态常量(final static),特例对于需要运行时赋值的(比如随机函数 P[319])不属于[编译期常量]
思考对于 Class.forName() 的对类的初始化到底进行到了哪一个程度? 经过测试可得:对于对应类中的静态初始化子句已经执行,而实例初始化子句没有执行,更不用说其构造方法了
使用 Class 引用的习惯(为了提供编译期的类型检查):
a) 利用泛化的 Class 引用可以让编译器强制执行额外的类型检查!(P320)
b) 如果想稍微放松一些限制,比如想限制在由一个父类继承下来的,不能直接使用其父类进行定义(Class < Number > genericNumberClass = Integer.class),因为 Integer Class 对象不是 Number Class 对象的子类;
c) 对于上述问题,可以使用通配符进行解决,比如 Class < ? Extends Number >。并且Class < ? > 要优于 Class,即便它们是等价的,Class < ? > 的好处是它表示并非碰巧或者由于疏忽,而使用了一个具体的类的引用,你就是选择了非具体的版本;
- [其实这些内容应该为泛型的!]
有趣的现象:当使用泛型语法用于 Class 对象时,newInstance() 将返回该对象的确切类型,而不仅仅只是 Object 。但是有种限制:如果手头的是超类,那编译器将只允许你声明超类引用是“某个类,它是 FancyToy 超类”,而此时再次使用 newInstance() 返回值不是精确类型,而只是 Object(如下):[具体原因见下章]
Class 对象的 cast() 新方法,具体讨论在[第15章]
类型转换:
a) 传统的类型转换,由 RTTI 来确保转换的正确性,如果失败会抛出一个 ClassCastException 异常;
b) 通过 Class 对象可以获取运行时所需的信息;
c) 使用关键字 instanceof,它会返回一个布尔值;
小知识:
a) throw new RuntimeException(e); //在 catch 里面,在这里不知道如何处理该异常,所以就通过不必捡把它一层一层往上抛,直到结束;
b) Collections.addAll(list,相应数组); //利用 Collection 类来填充 list;
c) @SuppressWarnings(“unchecked”) //告诉编译器对被批注的代码元素内部的某些警告保持静默,unchecked 指的是这里的强制转换;
d) (P326) 对于里面的静态内部类:需要一个功能,但是这里通过继承又不太好,故可以使用静态内部类来实现,还有要学会使用 HashMap < Object,Object > ;
Instanceof 有比较严格的限制:只可将其与命名类型进行比较,而不能与 Class 对象做比较。这并不是一个很好地限制。假如程序中编写了许多的 instanceof 表达式,就说明设计有缺陷 [这里可以使用动态的instanceof(P329) ]
关于 Collection.unmodifiableList(List) 其返回一个等同于参数 List 的列表但是不可以对其进行修改,用处:比如,在一个学生类中有一个关于已选课的列表,如果该类不加限制的返回该列表会在不知不觉中被修改,这是可以使用该方法来是的获取的列表不可更改
动态的 instanceof —— Class.isInstance ,注意在使用的时候需要获取全部的继承类(可以使用 LinkedHashMap < Class < ? extend … > ,Integer>)
Class 对象 .isAssignableFrom() //当调用对象是参数的超类或超接口或同一个返回为true;