Java编程思想—第14章 类型信息

  • 运行时类型信息使得你可以在程序运行时发现和使用类型信息
14.1 为什么需要RTTI(Run-Time Type Identification)
  1. 面向对象编程中最基本的目的是:让代码只操纵对基类的引用
  2. 当从数组中取出元素时,这种容器——实际上它将所有的事物都当做Object持有——会自动将结果转型会Shape
  3. RTTI名字的含义:在运行时,识别一个对象的类型
14.2 Class对象
  1. 类型信息在运行时是如何表示的,这项工作是由称为Class对象的特殊对象完成的,它包含了与类有关的信息
  2. 类是程序的一部分,每个类都有一个Class对象
  3. 类加载器子系统实际上可以包含一条类加载器链,但是只有一个原生类加载器,它是JVM实现的一部分
  4. 所有的类都是在其第一次使用时,动态加载到JVM中的
    a. 一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。
    b. static初始化是在类加载时进行的
    c. 这个方法是Class类的一个static成员:它是用一个包含目标类的文本名的String作输入参数,返回的是一个Class对象的引用
    d. 如果Class.forName()找不到你要加载的类,它会抛出异常ClassNotFoundException
Class.forName("Gum")
  1. 虚拟构造器允许你声明:“我不知道你的确切类型,但是无论如何要正确的创建你自己”
  2. 类字面常量
    a. Java还提供了另一种方法来生产对Class对象的引用,即使用类字面常量
    b. 类字面常量不仅可以应用于普通的类,也可以应用于接口、数组以及基本数据类型
    c. 对于基本数据类型的包装器类,还要一个标准字段TYPETYPE字段是一个引用,指向对应的基本数据类型的Class对象
    d. 使用类而做的准备工作实际包含三个步骤
    (1)加载,这是由类加载器执行的。该步骤将查找字节码(通常在classpath所指定的路径中查找,但这并非是必需的),并从这些字节码中创建一个Class对象
    (2)链接。在链接阶段将验证类中的字节码,为静态域分配存储空间,并且如果必需的话,将解析这个类创建的对其他类的所有引用
    (3)初始化。如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块
    e. 初始化有效的实现了尽可能的“惰性”。仅使用.class语法来获得对类的引用不会引发初始化
FancyToy.class
boolean.class  等价于   Boolean.TYPE
  1. 泛化的Class引用
    a. Class引用总是指向某个Class对象,它可以制造类的实例,并包含可作用于这些实例的所有方法代码
    b. 允许你对Class引用所指向的Class对象的类型进行限定而实现,这里用到了泛型语法
class<Integer> genericIntClass = int.class;

    c. 为了在使用泛化的Class应用时放松限制,使用通配符,它是Java泛型的一部分

Class<?> intClass = int.class;

    d. 为了创建一个Class引用,它被限定为某种类型,或该类型的任何子类型,需要将通配符与extends关键字结合,创建一个范围

Class<? extends Number> bounded = int.class;

    e. 如果你手头的是超类,那编译器将只允许你声明超类引用是“某个类”

Class<FancyToy> ftClass = FancyToy.class;
Class<? super FancyToy> up = ftClass.getSuperclass();
  1. 新的转型语法
public class ClassCasts{
    Building b = new House();
    Class<House> houseType = House.class;
    House h = houseType.cast(b);
    h = (House)b;  //或者这样写,窄化处理
}

    a. cast()方法接收参数对象,并将其转型为Class引用的类型

14.3 类型转换前先做检查
  1. 迄今为止,我们已知的RTTI形式包括:
    a. 传统的类型转换,如“(Shape)”,由于RTTI确保类型转换的正确性,如果执行了一个错误的类型转换,就会抛出一个ClassCastException异常
    b. 代表对象的类型的Class对象。通过查询Class对象可以获取运行时所需的信息
  2. RTTI在Java中还有第三种形式,就是关键字instanceof。它返回一个布尔值,告诉我们对象是不是某个特定类型的实例
if(x instanceof Dog){
    ((Dog)x).bark();
}
  1. 使用类字面常量
    a. 取代Class.forName(),直接使用Pet.class
  2. 动态的instanceof
    a. Class.isInstance方法提供了一种动态地测试对象的途径
if(pair.getKey().isInstance(pet)){
    put(pair.getKey(),pair.getValue()+1)
}
  1. 递归计数
    a. 对基类型和确切类型都进行了计数
14.4 注册工厂
  1. 问题:生成器在其列表中不包含这个类,因此它永远不能创建这个类的对象,而这个类也就不能被加载并置于这个列表中
  2. 我们可以使用工厂方法设计模式,将对象的创建工作交给类自己去完成。工厂方法可以被多态调用,从而为你创建恰当类型的对象
  3. 并非所有在继承结构中的类都应该被实例化
14.5 instanceof与Class的等价性
  1. instanceofisInstance()生成的结果完全一样,equals()与==也一样
  2. instanceof保持了类型的概念,它指的是“你是这个类吗,或者你是这个类的派生类吗?”而如果用==比较实际的Class对象,就没有考虑继承——它或者是这个确切的类型,或者不是。
14.6 反射:运行时的类信息
  1. 如果不知道某个对象的确切类型,RTTI可以告诉你。但有一个限制:这个类型在编译时必须已知,这样才能使用RTTI识别它,并利用这些信息做一些有用的事。换句话说,在编译时,编译器必须知道所有要通过RTTI来处理的类
  2. 假设你获取了一个指向某个并不在你的程序空间中的对象的引用;事实上,在编译时你的程序根本没法获知这个对象所属的类
  3. 反射提供了一种机制——用来检查可用的方法,并返回方法名。Java通过JavaBeans提供了基于构件的编程架构
  4. class类与java.lang.reflect类库一起对反射的概念进行了支持,该类库包含了FieldMethod以及Constructor类(每个类都实现了Member接口)
  5. 当通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类
  6. RTTI反射的区别
    a. 对RTTI来说,编译器在编译时打开和检查.class文件
    b. 对于反射机制来说,.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件
  7. 类方法提取器
    a. 反射机制提供了一种方法,使我们能够编写可以自动展示完整接口的简单工具
    b. 在编程时,特别是如果不记得一个类是否有某个方法,或者不知道一个类究竟能做些什么,例如Color对象,而又不想通过索引或类的层次结构去查看JDK文档,这时这个工具确实能节省很多时间
14.7 动态代理
  1. 代理是基本的设计模式之一,它是为你提供额外的或不同的操作,而插入的用来代替”设计“对象的对象
  2. Java的动态代理比代理的思想更向前迈进了一步,因为它可以动态地创建代理并动态地处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的对策
14.8 空对象
  1. 有时候应用空对象,它可以接收传递给它的所代表的对象的消息,但是将返回表示为实际上并不存在任何“真实”对象的值
  2. 即使空对象可以响应“实际”对象可以响应的所有信息,你仍需要某种方式去测试其是否为空。要达到此目的,最简单的方式是创建一个标记接口
public interface Null {}
  1. 通常,空对象都是单例
  2. 模拟对象与桩
    a. 空对象的逻辑变体是模拟对象。与空对象一样,他们都表示在最终的程序中所使用的“实际”对象
    b. 模拟对象与桩之间的差异在于程度不同。
    (1)模拟对象往往是轻量级和自测试的,通常很多模拟对象被创建出来是为了处理各种不同的测试情况。
    (2)只是返回桩数据,它通常是重量级的,并且经常在测试之间被复用
14.9 接口与类型信息
  1. interface关键字的一种重要目标就是允许程序员隔离构件,进而降低耦合性
  2. 最简单的方式是对实现使用包访问权限,这样在包外部的客户端就不能看到它了
  3. 通过使用反射,仍旧可以到达并调用所有方法,甚至是private方法
  4. 对于来说,反射可以到达并调用那些非公共访问权限的方法
  5. final与实际上在遭遇修改时是安全的,运行时系统会在不抛出异常的情况下接受任何修改尝试,但是实际上不会发生任何修改
  6. 总的来说,反射可以查看并调用任何权限的方法,但是不推荐使用无访问权限的方法,因为类提供者可能会改掉这些方法,导致无法升级
14.10 总结
  1. RTTI允许通过匿名基类的引用来发现类型信息。尽量用多态实现必要的操作
  2. RTTI有时能解决效率问题
  3. 不要太早地关注程序的效率问题,这是个诱人的陷阱。最好首先让程序运作起来,然后再考虑它的速度,如果要解决效率问题可以使用profiler
  4. 一致的错误报告模型的存在使我们能够通过使用反射编写动态代码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值