运行时类型信息使得你可以在程序运行时发现和使用类型信息
如何让我们在运行时识别对象和类的信息的
1.RTTI,假定我们在编译时已经知道所有的类型
2.“反射”机制,允许我们在运行时发现和使用类的信息
RTTI运行时类型判定
可以用来查询某个父类引用所指向的对象的确切类型,然后选择或者剔除特例。
类型转换。
java使用Class对象来执行其RTTI。每个类都有一个Class对象,每当编写并且编译了一个新类,就会产生一个Class对象(更恰当的说,是被保存在一个同名的.class文件中)。为生成这个类的对象,运行这个程序的jvm将使用被称为“类加载器”的子系统。
所有的类都是在对其第一次使用,动态加载到jvm中。
java程序在它开始运行之前并非被完全加载,其各个部分是在必需时才加载的。
类加载器的工作流程
1.类加载器首先检查这个类的Class对象是否已加载。若未加载,默认的类加载器就会根据类名查找.class文件。
2.在这个类的字节码被加载时,它们会接受验证,以确保其没有被破坏,并且不包含不良java代码。
3.一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。
java还提供了另一种方法生成对Class对象的引用,即类字面常量。
类字面常量不仅可以应用于普通的类,也可应用于接口,数组以及基本数据类型。对于基本数据类型的包装器类,还有标准字段TYPE,TYPE字段是个引用,指向对应的基本数据类型的Class对象
boolean.class 等价于 Boolean.TYPE
当使用“.class”来创建对Class对象的引用不会自动初始化该Class对象
使用类的过程
1.加载。类加载器执行,将查找字节码(通常在classpath所指定的路径中查找,但这并非是必需的),并从这些字节码中创建一个Class对象。
2.链接。验证类中的字节码,为静态域分配存储空间,若必需的话,将解析这个类创建的对其他类的所有引用。
3.初始化。若该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。初始化延迟到对静态方法(构造器隐式地是静态的)或者非常数静态域进行首次引用时才执行。
通配符“?”,表示任何事物
Class<?> intClass = int.class; intClass = double.class;为创建一个Class引用,它被限定为某种类型,或该类型的任何子类型,你需要将通配符与extends关键字相结合,创建一个范围。super则表示是其自己或其超类;向Class引用添加泛型语法仅仅是为了提供编译器类型检查
Class<? extends Number> bounded = int.class; bounded = double.class; bounded = Number.class;newInstance():弱类型,效率低,只能调用无参构造
new():强类型,高效率,能调用任何public构造器
public class GenericToyTest { public static void main(String[] args) throws Exception { Class<FancyToy> ftClass = FancyToy.class; // Produces exact type: FancyToy fancyToy = ftClass.newInstance(); Class<? super FancyToy> up = ftClass.getSuperclass(); // This won't compile: //Class<Toy> up2 = ftClass.getSuperclass();//报错 // Only produces Object: Object obj = up.newInstance(); } } ///:~Class引用的转型语法,即cast()方法;cast()方法接受参数对象,并将其转型为Class引用的类型。
Class Building{} Class House extends Building{} main{ Building b = new Building(); Class<House> houseType = House.class; House h = houseType.cast(b); h = (House)b; }RTTI形式
1.传统类型转换,就抛出ClassCastException异常
2.代表对象的类型的Class对象。通过查询Class对象可获取运行时所需的信息
3.关键字instanceof。返回一个布尔值,告诉我们对象是不是某个特定类型的实例
@SuppressWarnings(“unchecked”)告诉编译器忽略 unchecked 警告信息,如使用List,ArrayList等未进行参数化产生的警告信息。
//用法: boolean result = object instanceof class //参数: //result :boolean类型。 //object :必选项。任意对象表达式。 //class:必选项。任意已定义的对象类。 //(如果该x是该class的一个实例,那么返回true。如果该object 不是该class的一个实例,或者object是null,则返回false) //一般用于向下转型 if(x instanceof Dog){ ((Dog)x).bark(); }Class.isInstanceof方法提供了一种动态地测试对象的途径。完美代替了instanceof语句
将对象的创建工作交给类自己去完成。工厂方法可被动态调用,从而为你创建恰当类型的对象。
public class Filter extends Part{ } public class FuelFilter extends Filter { public static class Factory implements com.agree.ShiSi.Factory<FuelFilter>{ @Override public FuelFilter create() { // TODO Auto-generated method stub return new FuelFilter(); } } } public class AirFilter extends Filter { public static class Factory implements com.agree.ShiSi.Factory<AirFilter>{ @Override public AirFilter create() { // TODO Auto-generated method stub return new AirFilter(); } } } public class Belt extends Part { } public class FanBelt extends Belt { public static class Factory implements com.agree.ShiSi.Factory<FanBelt>{ @Override public FanBelt create() { // TODO Auto-generated method stub return new FanBelt(); } } } public class GeneratorBelt extends Belt{ public static class Factory implements com.agree.ShiSi.Factory<GeneratorBelt>{ @Override public GeneratorBelt create() { // TODO Auto-generated method stub return new GeneratorBelt(); } } } public class Part { @Override public String toString() { // TODO Auto-generated method stub return getClass().getSimpleName(); } static List<Factory<? extends Part>> partFactories = new ArrayList<Factory<? extends Part>>(); static { partFactories.add(new FuelFilter.Factory()); partFactories.add(new AirFilter.Factory()); partFactories.add(new FanBelt.Factory()); partFactories.add(new GeneratorBelt.Factory()); } private static Random rand = new Random(47); public static Part createRandom() { int n = rand.nextInt(partFactories.size()); return partFactories.get(n).create(); } } public class RegisteredFactories { public static void main(String[] args) { for(int i=0;i<10;i++) { System.out.println(Part.createRandom()); } } } //output FanBelt AirFilter FanBelt FuelFilter FuelFilter FanBelt FuelFilter AirFilter FanBelt FanBeltinstanceof和isInstance()生成结果一样,equals()和==也一样
x instanceof Base Base.class.isInstance(x) x.getClass == Base.class x.grtClass.equals(Base)概念:运行时获取类的信息。
快速应用开发(RAD);集成开发环境(IDE);图形化用户界面(GUI)
人们想要在运行时获取类的信息的另一个动机,便是希望提供在跨网络的远程平台上创建和运行对象的能力,称为远程方法调用(RMI)
Class类与java.lang.reflect类库一起对反射的概念进行了支持,该类库包含了Field、Method以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行时创建的,用以表示未知类里对应的成员。这样就可以使用Constructor创建新的对象,用get()和set()方法读取和修改与Filed对象管理的字段,用invoke()方法调用与Method对象关联的方法。还可以调用getFields()、getMethods()和getConstructors()等方法,以返回表示字段、方法以及构造器的对象的数组。Class.forName()生成的结果在编译时是不可知的,因此所有方法的特征签名信息都是在执行时被提取出来的。
RTTI和反射的区别在于
RTTI编译器在编译时打开和检查.class文件。反射是说.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件
反射的作用:反射机制提供了足够的支持,使得能够创建一个在编译时完全未知的对象,并调用此对象的方法。反射在java中是用来支持其他特性的,例如对象序列化和JavaBean。
Class<?> c = Class.forName(args[0]); Method[] methods = c.getMethods(); Constructor[] ctors = c.getConstructors();为了提供额外的或不同操作,而插入的用来代表“实际”对象的对象。这些操作通常涉及与“实际”对象的通信,因此通常充当中间人的角色。
只要是你想将额外的操作从“实际”对象中分离到不同的地方,特别是当你希望能够很容易地做出修改,从没有使用额外操作转为使用这些操作,或者反过来,代理就显得很有用。
interface Interface { void doSomething(); void somethingElse(String arg); } class RealObject implements Interface { public void doSomething() { print("doSomething"); } public void somethingElse(String arg) { print("somethingElse " + arg); } } class SimpleProxy implements Interface { private Interface proxied; public SimpleProxy(Interface proxied) { this.proxied = proxied; } public void doSomething() { print("SimpleProxy doSomething"); proxied.doSomething(); } public void somethingElse(String arg) { print("SimpleProxy somethingElse " + arg); proxied.somethingElse(arg); } } class SimpleProxyDemo { public static void consumer(Interface iface) { iface.doSomething(); iface.somethingElse("bonobo"); } public static void main(String[] args) { consumer(new RealObject()); consumer(new SimpleProxy(new RealObject())); } } /* Output: doSomething somethingElse bonobo SimpleProxy doSomething doSomething SimpleProxy somethingElse bonobo somethingElse bonobo *///:~java的动态代理可动态的创建代理并动态的处理对代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的对策。
import java.lang.reflect.*; class DynamicProxyHandler implements InvocationHandler { private Object proxied; public DynamicProxyHandler(Object proxied) { this.proxied = proxied; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("**** proxy: " + proxy.getClass() + ", method: " + method + ", args: " + args); if(args != null) for(Object arg : args) System.out.println(" " + arg); return method.invoke(proxied, args); } } class SimpleDynamicProxy { public static void consumer(Interface iface) { iface.doSomething(); iface.somethingElse("bonobo"); } public static void main(String[] args) { RealObject real = new RealObject(); consumer(real); // Insert a proxy and call again: Interface proxy = (Interface)Proxy.newProxyInstance( Interface.class.getClassLoader(), new Class[]{ Interface.class }, new DynamicProxyHandler(real)); consumer(proxy); } } /* Output: (95% match) doSomething somethingElse bonobo **** proxy: class $Proxy0, method: public abstract void Interface.doSomething(), args: null doSomething **** proxy: class $Proxy0, method: public abstract void Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@42e816 bonobo somethingElse bonobo *///:~通过调用了Proxy.newProxyInstance()可创建动态代理,这是这个方法所要传递的参数,动态代理可将所有调用重定向到调用处理器,通常会向调用处理器的构造器传递给一个“实际”对象的引用,从而使得调用处理器在执行其中介任务时,可请求转发。
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentExceptioninvoke()方法传递进来代理对象,以防你需要区分的请求的来源。在invoke()内部,对接口的调用将被重定向为对代理的调用。
至于它是怎么自动执行invoke()的方法,需要看产生的内部类的源码。
代理对象调接口中的方法—代理对象的真身是$proxy0 调用了对应的方法—此方法内部调用其父类的成员h调用h的invoke方法—就是调用传入了InvocationHandler的invoke方法,至于返回值,那就看我们的InvocationHandler的实现类怎么写了。
动态代理之代理工厂实现
public class ProxyFactory { private Object obj;//目标对象 private BeforeAdvice before; private AfterAdvice after; public Object newProxyInstance() { Object object = Proxy.newProxyInstance( this.getClass().getClassLoader(), obj.getClass() .getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (before != null) { before.before(); } //result即为接口的真实实现类的返回值 Object result = method.invoke(obj, args); if (after != null) { after.after(); } return result; } }); //object即为代理对象 return object; } public ProxyFactory() { super(); // TODO Auto-generated constructor stub } public ProxyFactory(Object obj, BeforeAdvice before, AfterAdvice after) { super(); this.obj = obj; this.before = before; this.after = after; } public Object getObj() { return obj; } public void setObj(Object obj) { this.obj = obj; } public BeforeAdvice getBefore() { return before; } public void setBefore(BeforeAdvice before) { this.before = before; } public AfterAdvice getAfter() { return after; } public void setAfter(AfterAdvice after) { this.after = after; } } //前置增强 interface BeforeAdvice { public void before(); } //后置增强 interface AfterAdvice { public void after(); }空迭代器模式,它使得在组合层次结构中遍历各个节点的操作对客户端透明(客户端可使用相同的逻辑来遍历组合和叶子节点)。
需要测试一对象是否为空可创建一个空对象接口(r instanceof Null),或者用“==”和对象名.equals()
空对象的逻辑变体是模拟对象和桩。
模拟对象往往是轻量级和自测试的,通常是为了处理各种不同的测试。
桩值返回桩数据,通常为重量级的,经常在测试间被复用。