读《java编程思想》14-RTTI

Posted shineon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了读《java编程思想》14-RTTI相关的知识,希望对你有一定的参考价值。

Java如何识别对象和类的信息,主要方式有两种:
(1) RTTI,它假定我们在编译时已经知道了所有的类型。
(2)反射,允许我们在运行时发现和使用类信息。
 
1、为什么需要RTTI
RTTI(run-time type information)即:运行时类型信息。
当从数组中取出元素时,这种容器--实际上将所有事物都当做Object持有--会将结果自动转换为所需的父类(如Shape)。这是RTTI 最基本的使用形式。因为所有类型转换都是在运行时进行正确性检查的。这也是RTTI名字的含义:在运行时,识别一个对象的类型。
 
2、Class对象
Class对象就是用来创建类的所有的“常规”对象的。每一个类都只有一个Class对象(单例),为了生成这个类的对象,JVM会使用“类加载器”。
 技术图片
技术图片
注意:
Java程序中,常量用关键字static final修饰,又分为:
(1)编译期常量: public static final int a = 5;
(2)运行时常量: public static final int b = rand.nextInt(47);
当访问编译期常量时,虽然也是静态引用,但是不会引起类加载。(已经编译到字节码中)
 
3、获取Class类引用的方式
(1)Class.forName 引起类加载
(2)XXXX.class 类字面常量,不引起类加载。
注意:
基本类型也都有对应的Class对象,
int.class (和 Integer.class equals或== 为false)
double.class等
 
4、泛化的Class引用
(1)如果需要Class类型变得更具体些,就通过允许Class引用所指向Class对象的类型进行限定实现。这里用了泛型。如:
Class<Integer> intClass = int.class;
(2)如果不确定限定类型,则使用Class<?> 优与 Class。
(3)如果要扩大限定类型范围,如:
Class<Number> numClass = int.class 错误,应该使用Class<? extends Number> numClass = int.class
因为Number虽是Integer的父类,但是Class<Number> 不是 Class<Integer>的子类。
(4)使用泛化Class引用 创建的对象(newInstance),得到的不再是Object,而是具体的限定类型,如:
Integer intObj = intClass.newInstance();
Number numObj = numClass.newInstance();
 
5、使用Class引用做类型转换
xxxClass.cast(xxxObj)
如:
Number numObj= new Integer(1);
Class<Integer> intClass = Integer.class;
Integer intObj = intClass.cast(numObj);
 
6、RTTI形式包括:
(1)传统的类型转换(向上、向下),RTTI保证类型正确性,错误则抛出ClassCastException。
(2)通过查询Class对象获取运行时信息。(注意并不是使用Class就是用了反射)
Class c = Class.forName(“RTTITest”);
Object o = c.newInstance();
(3)类型检查,如:instanceof可以看做是java的一种双目运算符。
boolean result = obj instanceof Class
a、obj 必须是引用类型,基本类型编译报错。
b、obj 为null,返回false,因此使用instanceof时不用先判断不为空。
c、obj 为Class的实例对象,子类对象,或接口实现对象时,返回true。
d、obj的声明类 和 Class 必须有父子关系(存在转换的可能),否则编译报错。如:
"abc" instanceof Integer 编译报错
 
7、类型转换前先做类型检查
(1)使用instaceof判断对象是否属于某个类、子类、接口实现,也可以使用Class引用的isInstance()方法。如:
String.class.isInstance(new Test())
相比前一种,Class.isInstance更加纯粹,不会出现instanceof中编译报错的情况。
 
(2)使用aClass.isAssignableFrom(bClass) 可以判断aClass引用是否为bClass引用的超类,如:
Exception.class.isAssignableFrom(IOException.class) 结果为true
 
8、反射:运行时的类信息。
如果不知道某个类的信息,RTTI可以告诉你,前提:在编译时,编译器必须知道所有要通过RTTI来处理的类。
如果你获取了一个指向某个并不在你的程序控件的对象的引用,如:RMI,则需要反射。Class类与java.lang.reflect类库一起给反射提供支持。
 
他们之间的区别:
RTTI:编译器在编译时打开和检查.class文件。
反射:运行时打开和检查.class文件。
 
PS:实际上是概念的区别,JDK 文档中,没有RTTI,只有Reflection。
 
 
9、动态代理
代理是基本设计模式之一,他用来代替“实际”的对象,为你在调用方法的同时提供额外的功能或不同的操作。
动态代理比代理的思想更向前迈进一步,因为他可以动态创建代理并动态的处理对所有代理方法的调用。
在动态代理上所做的所有调用都会重定向到单一的调度处理器上(InvocationHandler),他的工作揭示了调用的类型并确定响应的对策。
public interface IReal
void method1();
void method2();
public class Real implements IReal
public void method1()
System.out.println("method1");
 
public void method2()
System.out.println("method1");
 
public class DynamicProxyHandler implements InvocationHandler
private Object proxied;
 
public DynamicProxyHandler(Object proxied)
this.proxied = proxied;
 
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
System.out.println(method.getName() + " before");
Object result = method.invoke(proxied, args); //参数为proxied 被代理对象
System.out.println(method.getName() + " after");
return result;
 
public class Test
public static void main(String[] args)
IReal real = (IReal) Proxy.newProxyInstance(
Test.class.getClassLoader(),
new Class[]IReal.class,
new DynamicProxyHandler(new Real()));
real.method1();
real.method2();
 
输出:
method1 before
method1
method1 after
method2 before
method1
method2 after
 
10、空对象
创建一个名为Null的标识接口,然后针对不同的业务类创建不同的空对象。如:NullPerson等。
感觉用处很小,不如null 或者状态码来的方便。
 
11、接口与类型信息
接口并不能保护客户端不调用实现类中其他的方法,比如通过反射。
哪怕是private的方法或字段,都可以使用Method.setAccessible(true)之后访问。
 
 
 
 
 
 
 
 
 
 

以上是关于读《java编程思想》14-RTTI的主要内容,如果未能解决你的问题,请参考以下文章

一起读《Java编程思想》(第四版)

点读系列《Java编程思想 前6章》

读《Java编程思想第五版》心得体会

1.JAVA 编程思想——对象入门

《Java编程思想》读书笔记

《Java编程思想》 读后感