——总结自《java 编程思想》
运行时类型信息使得你可以在程序运行时发现和使用类型信息
RTTI
RTTI(Run-Time Type Identification)翻译过来为运行时类型信息
Class:Class对象就是用来创建类的所有"常规" 对象的。所有的类都是对其第一次使用时,动态加载到JVM中的. 当程序创建第一个对类的静态成员引用时,就会加载这个类.这个也证明构造器也是类的静态方法,即使在构造器之前并没有使用static关键字. 因此,使用new
操作符创建类的新对象也会被当作对类的静态成员的引用.
生成class对象的方法:
-
Class.forName()
-
Calss.getSuperClass()
-
Class.class(类字面常量,可以用于普通类、接口、数组以及基本数据类型)
对于基本数据类型的包装器类,还有一个标准字段TYPE。TYPE是一个引用,指向对应的基本数据类型的Class对象。
当使用".class"来创建Class对象的引用时,不会自动地初始化该class类。实际包含三个步骤:
- 加载。由类加载器指向,查找字节码,并从这些字节码中创建一个Class对象。
- 链接。验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个类创建的其它类的所有引用。
- 初始化。如果该类具有超类,则对其初始化,执行静态初始化其和静态初始化块。
初始化被延迟到了对静态方法或者非常数静态域进行首次引用时才执行。
Class的泛化
class A{}
class B extends A{}
Class obj=Class.forName("A");
Object newA=obj.newInstance();
// 使用泛化Class引用生成带类型的目标实例
Class<A> obj2=A.class;
A newA2=obj2.newInstance();
// 新的转换语法
B b = new B();
class<A> obj3 = A.class;
A newA3 = obj3.cast(b);
newA3 = (A)b;
// 以下语法生成的Class对象可以指向任何类
Class<?> obj=int.class;
obj=double.class;
obj=shapes.class;
当使用这种泛型语法来构建一个Class类的对象的基类对象时,必须采用以下的特殊语法
public class shapes{}
class round extends shapes{}
Class<round> rclass=round.class;
Class<? super round> sclass= rclass.getSuperClass();
//Class<shapes> sclass=rclass.getSuperClass();
即使知道round的基类就是shapes,但是不能直接声明 Class < shapes >,必须使用特殊语法Class < ? super round >
类型转换检查:
-
Class.cast( ) 向下转型,将传入的父类型向下强转为子类型。
ast()方法底层源码: 底层应用Class.isInstance( ).
-
Class.isInstance( ) :自身类.class.isInstance(自身类的实例或子类实例)。
-
instanceof : 自身实例或子类实例 instanceof 自身类。
判断A是否为B的实例,其中A为对象,B为类型。
-
Class.isAssignableFrom( )方法
判断A是否是B的父类 或者 和B类型相同 或者 B实现了A接口。是类或接口之间的比较。
反射
java.lang.reflect类库包含了Field、Method、Constructor类。这些类型的对象由JVM在运行时创建。
关于reflect类库相关对象的方法和属性可以查看java高级特性——反射
动态代理:被代理对象需要实现某个接口(这是前提),代理对象会拦截对被代理对象的方法调用,在其中可以全然抛弃被代理对象的方法实现而完成另外的功能,也可以在被代理对象方法调用的前后增加一些额外的功能。
动态代理使用的主要场景为RPC和AOP
动态代理的核心为生成代理对象:
Proxy.newProxyInstance(classLoader, proxyInterface, handler)。
/*
参数为:
ClassLoader,用于加载代理类的 Loader 类,通常这个 Loader 和被代理的类是同一个 Loader 类。
Interfaces,是要被代理的那些那些接口。
InvocationHandler,就是用于执行除了被代理接口中方法之外的用户自定义的操作,他也是用户需要代理的最终目的。用户调用目标方法都被代理到 InvocationHandler 类中定义的唯一方法 invoke 中。
*/
创建动态代理的步骤:
1、通过实现InvocationHandler接口创建调用处理器
IvocationHandler handler = new InvocationHandlerImpl(...);
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4:
Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{Subject.class}, new InvocationHandlerImpl (real));
生成的proxySubject继承Proxy类实现Subject接口。实现的Subject的方法实际是调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的方法(Object result=method.invoke(proxied,args));
空对象:一个正常的对象,只不过是这个对象来替代null。因为相对于null(虚无),空对象更能表示数据的意思。
class Person{
private String name;
private String address;
public Person(String name,String address) {
this.name = name;
this.address = address;
}
/**
*Person的空对象,它是一个实在的对象,只不过域的值与其他对象不同
*/
public static class NullPerson extends Person implements Null{
private NullPerson(){ super("None","Node");}
public String toString() {
return "NullPerson";
}
}
public String toString() {
return name+" "+address;
}
public static final Person NULL = new NullPerson();
}
空对象的逻辑变体是模拟对象和桩。不过模拟对象和桩都只是假装可以传递实际信息的存活对象,而不是像空对象那样可以成为null的替代物。
模拟对象是轻量级的,自检测的。
桩(桩代码就是用来代替某些代码的代码)因为要返回桩数据,所以有很多复杂的操作,是一个重量级的。
相比之下,空对象的目的明确的多,也巧妙、智能得多。