




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第十四章 类型信息 RTTI 反射与代理第1页,共103页。一、为什么需要RTTI1、 RTTI的概念 RTTI(Run-Time Type Identification) 运行时类型识别第2页,共103页。一、为什么需要RTTI2、 多态的复习 从我们熟悉的一个例子开始:第3页,共103页。 这是一个典型的类层次结构图:基类位于顶部, 派生类向下发展 面向对象的基本的目:让代码只操纵对基类的 引用,这样,如果要添加一个新类来扩展程序, 就不会影响到原来的代码第4页,共103页。 abstract class Shape void draw() System.out.println(this
2、+ “.draw()”); / 注意:这里需要的是字符串,但是提供的是 对象,所以需要toString方法转换 abstract public String toString(); / 定义为抽象,以强制继承者覆盖该方法,并 可以防止对Shape的实例化 第5页,共103页。 class Circle extends Shape public String toString() return “Circle”; / 派生类仅仅覆盖了toString方法,下同 class Square extends Shape . class Triangle extends Shape . 第6页,共103
3、页。 public class Shapes / 测试程序 public static void main(String args) List shapeList = Arrays.asList (new Circle(),new Square(), new Triangle(); for(Shape shape:shapeList) / 类型转换 shape.draw(); 第7页,共103页。 当把Shape对象放入List数组时向上转型 但是在向上转型为Shape的时候,也丢失了Shape 对象的具体类型 对数组而言,它们只是Shape类的对象第8页,共103页。 当从数组中取出元素时,
4、这种容器(实际上它将 所有事物都当成Object引用)会自动将结果转型 回Shape 这是RTTI最基本的使用形式:在运行时, 识别 一个对象的类型 但是RTTI类型转换却不彻底: Object被转型为 Shape,而不是转型为Circle、Square或Triangle第9页,共103页。 接下来就是多态机制的事情了: 多态机制保证:Shape对象实际执行什么样代码, 是由引用所指向的具体对象Circle、 Square或者 Triangle决定的,即动态绑定 这正是面向对象编程的基本目标:你希望大部分 的代码能尽可能少地了解对象的具体类型,而是 只是与对象家族中的一个通用表示打交道第10页
5、,共103页。一、为什么需要RTTI3、 问题的提出 值得注意的是:动态绑定是在运行时, 由系统自动完成的第11页,共103页。 假如我们的要求比这个还高: 我们不想在运行时,由系统自动进行动态绑定 而是希望在运行的过程中,能够根据某个引用的 确切类型,使用某种特别的方式来处理它,此时 该怎么办?第12页,共103页。 例如:假设我们允许用户将某一具体类型的几何 形状全部变成某种特殊的颜色,以突出显示它们 或者,可能要用某个方法来旋转列出的所有图形 但想跳过圆形,因为对圆形旋转没有意义 充分使用RTTI,可以查询某个Shape引用所指向 对象的确切类型,然后选择或者剔除特例第13页,共103页
6、。二、Class对象1、 Class对象的概念和作用 要理解RTTI的工作原理,首先必须知道 类型信息在运行时是如何表示的? 利用Class对象来描述和表示第14页,共103页。 每个类都有一个Class对象,它描述了与类相关的 信息 利用图形分析清楚:类和类的Class对象的关系 因为后面还会有新的概念加进来第15页,共103页。 每当我们编写并且编译了一个新的类, 就会生成 一个与此类相关的Class对象 该Class对象是如何生成的? Java虚拟机利用类加载器生成 该Class对象保存在哪里? 保存在一个同名的.class文件中第16页,共103页。二、Class对象2、 类加载器 类
7、加载器的组成 实际上可以包含一条类加载器链,但是 只有一个原生类加载器第17页,共103页。 原生类加载器加载的是所谓的可信类,包括Java API类,它们通常是从本地盘加载的 通常不需要添加额外的类加载器,但是如果你有 特殊的需求(例如以某种特殊的方法加载类,以 支持Web服务器应用,或者在网络中下载类), 那么你有一种方式可以挂接额外的类加载器第18页,共103页。二、Class对象 类加载器的作用 保证Java是一种动态加载语言 Java程序在它开始运行之前并非被完全 加载,其各个部分是在必需时才加载的第19页,共103页。 深入分析: 所有的类都是在对其第一次使用时,动态加载到 Jav
8、a虚拟机中的 例如:当程序创建第一个对类的静态成员的引用 时,就会加载这个类第20页,共103页。 构造器也是类的静态方法 即使构造器之前并没有使用static关键字 所以,使用new操作符创建类的新对象, 也会被 当作对类的静态成员的引用,从而导致类的加载第21页,共103页。二、Class对象 类加载器的执行过程 详见P315分析:第22页,共103页。1、首先,检查这个类的Class对象是否已经加载 如果还没有加载, 那么默认的类加载器,就会 根据类名查找.class文件2、一旦某个类的Class对象被载入内存,它就会被 用来创建这个类的所有对象第23页,共103页。3、值得思考的问题:
9、 当一个Class对象被载入内存以后,它所描述的类 是否也立即会被载入内存? 答案:不会,而是尽可能被推迟第24页,共103页。二、Class对象 典例分析: P315316 forName ()的调用将会导致:如果所 描述的类没有加载就加载它第25页,共103页。 class Candy / 糖果 static print(“Loading Candy”); class Gum / 口香糖 static print(“Loading Gum”); class Cookie / 饼干 static print(“Loading Cookie”); 第26页,共103页。 class publi
10、c class SweetShop / 甜品店 public static void main(String args) print(“inside main”); new Candy(); print(“After creating Candy”); 第27页,共103页。 try Class.forName(“Gum”); / 返回一个Class对象的引用 catch(ClassNotFoundException e) print(“Couldnt find Gum”); print(“After Class.forName(“Gum”)”); new Cookie(); print(“A
11、fter creating Cookie”); 第28页,共103页。二、Class对象3、 Class类的声明(Java.lang.Class ) public final class Class extends Object implements Serializable, GenericDeclaration, Type,AnnotatedElement第29页,共103页。二、Class对象4、 Class类的构造器 Class类没有公共构造器 Class对象是在加载类时,由Java虚拟机 通过调用类加载器的defineClass() 方法自动构造的 第30页,共103页。二、Clas
12、s对象5、 Class类的方法 Class类共有57个方法 详细资料请参阅官方文档第31页,共103页。1、public static Class forName(String className) throws ClassNotFoundException 功能:返回描述指定类的Class对象 调用forName(“X”) 将导致名为X的类被初始化 参数className:需要类的完全限定名第32页,共103页。2、public String getName() 以String形式返回此Class对象所描述的实体名称 即:返回此Class对象所描述的实体(类、接口、 数组类、基本类型或者vo
13、id)的完全限定名第33页,共103页。3、public String getCanonicalName() 返回规范化名称(标准名称/完全限定名)4、public String getSimpleName() 返回简称第34页,共103页。5、public Class getSuperclass() 返回表示此Class对象所描述的实体(类、接口、 基本类型或void)的超类的Class对象6、public Class getInterfaces() 此Class对象描述的类所包含的接口第35页,共103页。7、public T newInstance() throws Instantiat
14、ionException, IllegalAccessException 创建此Class对象所描述类的一个新实例 该方法的作用:如同用一个带有空参数列表的 new表达式实例化该类第36页,共103页。 典例:利用上述解释分析课本P316317代码 解释: Class c=null; / Class对象就和其它对象一样, 你可以获取并操作它的引用 obj.getClass() / 来源于Object的方法第37页,共103页。第38页,共103页。第39页,共103页。二、Class对象6、 类字面常量 类字面常量的作用 作用:利用它生成对Class对象的引用 格式:类名.class 优点:比
15、Class.forName更简单和安全第40页,共103页。二、Class对象 类字面常量的本质 类名.class仅仅只创建了对Class对象的 引用, 但并没有初始化该Class对象 利用图形分析这个复杂的概念第41页,共103页。二、Class对象 类字面常量的重要性质 我们首先分析: 为了使用一个类,需要做的准备工作 实际包含三个步骤:第42页,共103页。1、加载 这是由类加载器执行的 该步骤将查找字节码文件,并且根据这个字节码 文件创建一个Class对象 如何查找:通过Classpath类路径第43页,共103页。2、链接 在链接阶段将验证类中的字节码(安全) 为类的静态域分配存储空
16、间(但并未初始化) 如果有必要的话,将解析这个类创建的对其它类 的引用(类的组合)第44页,共103页。3、初始化 首先:按照先基类,后子类的原则 然后:按照初始化的第一基本原则和初始化的 第二基本原则进行初始化第45页,共103页。 重要性质:使用Class对象的三个阶段 创建对Class对象的引用 初始化该Class对象 初始化该Class对象所描述的类 第46页,共103页。 重要推论:类的初始化被推迟到: 对静态方法或者非常数静态域进行首次引用时才 进行 非常数静态域是指:static但非final的域第47页,共103页。第48页,共103页。第49页,共103页。 解释: stat
17、ic final可以分为编译期常量和非编译器常量 对编译期常量(如Initable.StaticFinal)的访问不 需要先对Initable类进行初始化 而对非编译期常量(如Initable.StaticFinal2 )的 访问将导致:强制Initable类进行初始化第50页,共103页。 解释: 如果一个域是static而非final的: 从前面的分析可知:访问非参数静态域将导致类 的初始化 例如:对Initable2.StaticNotFinal的访问第51页,共103页。二、Class对象7、 泛化的Class引用 普通的Class引用(复习) 总是用来指向某个Class对象第52页,
18、共103页。 Class对象可以用来创建类的实例 Class对象包含了: 可作用于这些实例的所有方法代码 该类的静态成员第53页,共103页。二、Class对象 限定的Class引用 通过泛型语法来实现第54页,共103页。 Java允许你,对Class引用所指向的Class对象的 类型进行限定 也就是对它指向的Class对象所描述的类的类型 进行限定 典例分析:第55页,共103页。 public class GenericClassReferences public static void main(String args) Class intClass = int.class; Clas
19、s genericIntClass = int.class; / 声明一个Class引用:genericIntClass / 并且限定它所指向Class对象是用来描述 Integer类的信息第56页,共103页。 genericIntClass = Integer.class; / Integer.class与int.class等价 intClass = double.class; / 普通的类引用可以被重修赋值为指向其它的 class对象 / genericIntClass = double.class; / 泛型类引用只能赋值为指向其声明的类型 第57页,共103页。二、Class对象 带
20、通配符?的Class引用 Java泛型可以使用通配符?,用来表示 任何事物第58页,共103页。 典例: Class intclass = int.class; intclass引用指向这样的一个Class对象, 它所描述的类暂时不能确定第59页,共103页。二、Class对象 带extends关键字的Class引用 详见P321分析:第60页,共103页。 为了创建这样的一个Class引用,它被限定为某种 类型,或者该类型的任何子类型 需要将通配符与extends关键字相结合,创建一个 范围 典例分析:第61页,共103页。 归纳: 向Class引用添加泛型语法的原因仅仅是为了提供 编译期类
21、型检查 而使用普通的Class引用,直到运行时你才会发现 它的错误,显得很不方便第62页,共103页。三、类型转换前先做检查1、 复习与归纳 迄今为止,我们已知的RTTI形式包括 以下两种:第63页,共103页。三、类型转换前先做检查 传统的类型转换 例如:本章开始范例中的(shape) 由RTTI确保类型转换的正确性, 如果 执行了一个错误的类型转换,就会抛出 一个ClassCastException异常第64页,共103页。 解释:1、在C+中,经典的类型转换(Shape)并不使用 RTTI 它只是简单的告诉编译器:将这个对象作为新的 类型看待第65页,共103页。2、Java要执行类型检
22、查,保证类型安全的向下转型 向上转型肯定是安全的,所以编译器允许自由地 做向上转型的赋值操作,而并不需要任何显式的 转型操作 向下转型不安全,除非你拥有额外的信息,这些 信息使你知道该类型是某种特定类型第66页,共103页。三、类型转换前先做检查 代表对象类型的Class对象 通过查询Class对象可以获取运行时所需 的信息第67页,共103页。三、类型转换前先做检查2、 RTTI在Java中还有第三种形式,就是 关键字instanceof 它返回一个布尔值, 告诉我们:对象 是不是某个特定类型的实例第68页,共103页。 instanceof的典型使用方式: if(x instanceof
23、Dog) (Dog)x.bark(); 典例分析:P323327第69页,共103页。 程序的第一部分:P323325 通过一个类的继承体系,描述了各种各样的Pet 以及它们的主人(通过类的层次结构图分析) 部分代码如下:第70页,共103页。 package typeinfo.pets; public class Person extends Individual public Person(String name) super(name); public class Pet extends Individual public Pet(String name) super(name); pu
24、blic Pet() super(); 第71页,共103页。 程序的第二部分:P325 该部分代码的功能是:定义出一种方法,通过它 可以随机地创建不同类型的宠物,并且为了方便 起见,还可以创建宠物数组和宠物List 为了使该工具能够适应多种不同的实现,我们将 其定义为抽象类第72页,共103页。 public abstract class PetCreator / 宠物生成器 private Random rand = new Random(47); 创建一个随机数的种子第73页,共103页。 public abstract ListClass types(); types是一个抽象方法,它
25、的返回值为List 而List是由这样的Class对象所构成的列表,这些 Class对象所描述的类都是Pet的子类第74页,共103页。 该方法的主要功能是:利用前面已定义好的List (由描述各个Pet子类的Class对象组成) ,随机的 创建一个Pet子类的实例public Pet randomPet() / 随机产生List中的索引 int n = rand.nextInt(types().size(); 第75页,共103页。try / 通过Class.newInstance来创建该类的新实例 return types().get(n).newInstance(); catch(Ins
26、tantiationException e) throw new RuntimeException(e); catch(IllegalAccessException e) throw new RuntimeException(e); 第76页,共103页。public Pet createArray(int size) / 宠物数组 / 声明并创建数组result,该数组元素类型为Pet Pet result = new Petsize; for(int i = 0; i size; i+) resulti = randomPet(); / 使用randomPet随机的创建一个Pet并放入数组
27、 return result;第77页,共103页。public ArrayList arrayList(int size)/ 宠物List/ 利用宠物数组求得宠物List ArrayList result = new ArrayList(); 声明一个指向ArrayList类型的引用result,而该 ArrayList存放的数据元素为Pet第78页,共103页。 将普通数组转换为ArrayList类型的典型方法/ 将createArray返回的数组全部添加到result集合Collections.addAll(result,createArray(size);return result;
28、第79页,共103页。 程序的第三部分:P325326 在PetCreator中所声明的抽象方法types,在其 子类中必须要提供实现 types方法返回类型的限定:使用randomPet() 和其它方法来创建的宠物类型的List第80页,共103页。 public class ForNameCreator extends PetCreator / 使用完全限定名的生成器 private static ListClass types = new ArrayListClass(); 声明指向List类型的一个引用types 该List是由这样的Class对象构成的列表,这些 Class对象所描述
29、的类都是Pet的子类第81页,共103页。private static String typeNames = / 声明数组typeNames并立即初始化 / 使用包名来引用这些类(参见程序的第一部分) typeinfo.pets.Mutt, typeinfo.pets.Pug, typeinfo.pets.EgyptianMau, typeinfo.pets.Manx, “typeinfo.pets.Cymric”, . ; 第82页,共103页。SuppressWarnings(“unchecked”) 为了产生具有实际类型的Class对象的List,必须 使用转型,而这将会产生编译器警告
30、SuppressWarnings注释作用:抑制未检查异常 该注释不能直接置于静态初始化语句之上,所以 此程序采用一个变通的办法:将loader ()方法 单独定义,然后再置于一个静态初始化语句中第83页,共103页。private static void loader() try for(String name : typeNames) / 强制转换并添加到types所指向的List types.add( (Class) Class.forName(name); catch(ClassNotFoundException e) throw new RuntimeException(e); 第84
31、页,共103页。 / 静态初始化语句 static loader(); / 在程序的最后,提供types方法的具体实现 public ListClass types() return types; 第85页,共103页。 程序的第四部分:P326327 为了对Pet进行计数,我们需要一个能够跟踪各种 不同类型的Pet的数量的工具 Map是此需求的首选,其中键是Pet类型名,而值 是保存Pet数量的Integer第86页,共103页。 public class PetCount / 测试程序,对宠物进行计数 static class PetCounter extends HashMap 嵌套类(
32、静态内部类) PetCounter:宠物计数器第87页,共103页。 public void count(String type) / 调用Map接口的get方法,返回键对应的值 Integer quantity = get(type); if(quantity = null) put(type,1); else put(type,quantity + 1); / 内部类定义结束第88页,共103页。public static void countPets(PetCreator creator) PetCounter counter= new PetCounter(); for(Pet pet
33、 : creator.createArray(20) 取出容器中的每一个Pet并作进一步处理 print(pet.getClass().getSimpleName() + ); / 输出Pet的名字第89页,共103页。 / 使用一串繁琐的instanceof对该数组中的每个Pet 进行测试和计数 / 注意:pet是动态的,即运行时才能决定 if(pet instanceof Pet) counter.count(“Pet”); if(pet instanceof Dog) counter.count(“Dog”); / 输出统计结果 print(counter); 第90页,共103页。 归
34、纳:RTTI的三种使用形式 传统的类型转换(发生在编译时) Class对象(通过查询Class对象可以获取运行时 所需的信息) 关键字instanceof(发生在运行时)第91页,共103页。 public static void main(String args) countPets(new ForNameCreator(); / 使用PetCreator来随机地向数组中填充Pet / 这是一个比较庞大而复杂的程序,而如果想要理解 该程序,就需要深入分析它的各个组成部分第92页,共103页。三、类型转换前先做检查3、 使用类字面常量 如果我们采用类字面常量来重新实现 PetCreator,那么,改写后的结果在 许多方面都会显得更加清晰 对程序的第三部分进行优化第93页,共103页。 package typeinfo.pets;import java.util.*;public class LiteralPetCreator extends PetCreator/ 使用类字面常量的生成器 / Literal就是字面上的意思 /
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 工程扩大劳务合同
- 半成品材料采购合同
- 半自动帐篷采购合同书
- 核酸检测委托合同协议书
- 合同承担协议
- 定量销售合同协议
- 居间协议合同销售
- 房产赠与协议合同
- 养鱼合作协议书合同
- 旧车买卖协议合同
- 华师大版七年级数学上册知识点
- 高中英语外研版必修第二册Unit 3 Period 6 Writing-Writing a sports story
- 心血管麻醉思考与实践读书随笔
- 作家雨果课件
- HYT 191-2015 海水冷却水中铁的测定(正式版)
- 煤矿职工应知应会知识考试题库(含答案)
- 行政复议法-形考作业2-国开(ZJ)-参考资料
- 深基坑开挖与支护施工监理实施细则
- 中班-社会语言-好邻居-课件(互动版)
- MSDS中文版(锂电池电解液)
- 2024年新乡职业技术学院单招职业适应性测试题库及答案解析
评论
0/150
提交评论