------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------
黑马程序员反射和代理模式
反射是Java被视为动态语言的一个关键性质。这个机制允许程序在运行时透过反射APIs取得任何一个已知名称的类的内部信息,包括其访问修饰符、父类、实现它的接口,也包括属性和方法的所有信息,并可于运行时改变属性内容或调用方法。Class类代表Java类,它的各个实例对象分别对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,即Class类。
数组的反射:
(1) 具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
(2) 代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
(3) 基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
反射的作用是用来实现框架。
反射是把Java类中的各种成分映射成相应的java类。在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
1. Class类:代表一个类,运行时判断任意一个对象所属的类。
2. Field类:代表类的成员变量。
3. Method类:代表类的方法。
4. Constructor类:代表类的构造方法。
5. Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
在java.lang.Object类中定义了getClass()方法,因此对于任意一个Java对象都可以通过此方法获得对象的类型。Class类是反射API中的核心类,它有以下方法:
1. getName():获得类的完整名字。
2. getFields():获得类的public类型的属性。
3. getDeclaredFields():获得类的所有属性。
4. getMethods():获得类的public类型的方法。
5. getDeclaredMethods():获得类的所有方法。
Class类十分特殊。它和一般类一样继承自Object,其实体用以表达java程序运行时的类和接口,也用来表达enum、array、原生数据类型。当一个class被加载,或当加载器的defineClass()被JVM调用,JVM便自动产生一个Class对象。如果你想借由“修改Java标准库源码”来观察Class对象的时间生成时机,不能够。因为Class并没有public构造函数。Class是反射的开始。针对任何你想探看的类,唯有先为它产生一个Class类对象,接下来才能经由后者唤起为反射APIs。
Java从多种途径为一个类生成对应的Class对象。
1. 运用getClass();//每个对象都要此函数;
String str = “abc”;
Class cl = str.getClass();
2. 运用Class.getSuperclass();
Button b = new Button();
Class c1 = b.getClass();
Class c2 = c1.getSuperclass();
3. 运用static方法Class.forName()
Class c1 = Class.forName(“java.lang.String”);
4. 运用.class语法
Class c1 = String.class;
5. 运用原生数据类型的包装类的TYPE语法
Class c1 = Boolean.TYPE;
欲生成对象实体,在反射动态机制中有两种做法,一个针对“无自变量构造函数”,一个针对“带参数构造函数”。如果欲调用的是“带参数构造函数”就比较麻烦些,不再调用Class的newInstance(),而是调用构造方法的newInstande()。首先准备一个Class[]作为构造方法的参数类型,然后以此为自变量调用getConstructor(),获得一个专属构造方法。接下来再准备一个Object[]做为构造方法实际参数值,调用上述专属构造方法的newInstance()。
动态生成String所对应的Class对象实体:无自变量。
Class c = Class.forName(“java.lang.String”);
Object obj = null;
obj = c.newInstance();
动态生成测试类对应的Class对象实体:自变量以Object[]表示。
Class c = Class.forName(“dynTest”);
Class[] pTypes = new Class[] {double.class,int.class};
Constructor ctor = c.getConstructor(pTypes);
Object obj = null;
Object[] arg = new Object[] {3.14,12};
obj = ctor.newInstance(arg);
运行时调用methods。首先准备一个Class[]作为参数类型,然后以此为自变量调用getMethod(),获得特定的Method对象。接下来准备一个Object[]放置自变量,然后调用上述所得之特定Method对象的invoke()。代码如下
package com.heima.exam;
import java.lang.reflect.Method;
publicclass InvokeTester
{
publicint add(int param1, int param2)
{
return param1 + param2;
}
public String echo(String message)
{
return"hello: " + message;
}
publicstaticvoid main(String[] args) throws Exception
{
Class<?> classType = InvokeTester.class;
Object invokeTester = classType.newInstance();
Method addMethod = classType.getMethod("add", new Class[] { int.class,
int.class });
Object result = addMethod.invoke(invokeTester, new Object[]{1, 2});
System.out.println((Integer)result);
System.out.println("---------------------");
Method echoMethod = classType.getMethod("echo", new Class[]{String.class});
Object result2 = echoMethod.invoke(invokeTester, new Object[]{"tom"});
System.out.println((String)result2);
}
}
运行时变更fields内容。首先调用Class的getFiels()并指定field名称。获得特定的Field对象之后便可直接调用Field的get()和set()
package com.heima.exam;
import java.lang.reflect.Field;
publicclass FieldTest {
publicdoubled=0;
publicstaticvoid main(String[] args) throws Exception
{
Class c = FieldTest.class;
Field f = c.getField("d");
FieldTest obj = new FieldTest();
f.set(obj, 1.23);
System.out.println("d="+obj.d);
}
}
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理模式一般涉及到的角色有:
1. 抽象角色:声明真实对象和代理对象的共同接口。
2. 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口以便在任何时候都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
3. 真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
下面是抽象角色
package com.heima.exam;
publicabstractclass Subject
{
publicabstractvoid request();
}
这是真实角色
package com.heima.exam;
publicclass RealSubject extends Subject
{
publicvoid request()
{
System.out.println("From real subject.");
}
}
以下是代理角色
package com.heima.exam;
publicclass ProxySubject extends Subject
{
private RealSubject realSubject; //代理角色内部引用了真实角色
publicvoid request()
{
this.preRequest(); //在真实角色操作之前所附加的操作
if(null == realSubject)
{
realSubject = new RealSubject();
}
realSubject.request(); //真实角色所完成的事情
this.postRequest(); //在真实角色操作之后所附加的操作
}
privatevoid preRequest()
{
System.out.println("pre request");
}
privatevoid postRequest()
{
System.out.println("post request");
}
}
客户端调用
package com.heima.exam;
publicclass Client
{
publicstaticvoid main(String[] args)
{
Subject subject = new ProxySubject();
subject.request();
}
}
上述的方法使用的是静态代理模式。按照这种方式,真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。在实际使用时,一个真实角色必须对应一个代理角色,但是如果大量使用会导致类的急剧膨胀;此外,如果事先不知道真实角色,该如何使用代理呢?这个问题可通过java的动态代理类来解决。
动态代理类是在运行时生成的类,在生成它时你必须提供一组接口给它,然后该类就宣称它实现了这些接口。你当然可以把该类的实例当作这些接口中的任何一个来用。当然,这个动态代理类其实就是一个代理,它不会替你做实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
Java动态代理类位于java.lang.reflect包下,一般主要涉及到两个类:
(1) Interface InvocationHandler:该接口中仅定义了一个方法
public Object invoke(Object proxy,Method method,Object[] args)
在实际使用时,第一个参数proxy指代理类,一般我们用不到。method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。
(2) Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包括以下内容
protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。
static Class getProxyClass(ClassLoader loader,Class[] interfaces):获得一个代理类,其中loader是类加载器,interfaces是真实类所拥有的全部接口的数组。
static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。
通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。
动态代理步骤:
1. 创建一个实现接口InvocationHandler的类,它必须实现invoke方法。
2. 创建被代理的类以及接口。
3. 通过Proxy的静态方法newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)创建一个代理。
4. 通过代理调用方法。
下面是抽象角色
package com.itheima;
publicinterface Subject {
publicvoid request();
}
下面是真实角色
package com.itheima;
publicclass RealSubject implements Subject
{
publicvoid request()
{
System.out.println("From real subject!");
}
}
下面是动态代理
package com.itheima;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个对象
* 此外,该类还实现了invoke方法,该方法中的method.invoke其实就是调用被代理对象的将要
* 执行的方法,方法参数是sub,表示该方法从属于sub,通过动态代理类,我们可以在执行真实对象的方法前后
* 加入自己的一些额外方法。
*
*/
publicclass DynamicSubject implements InvocationHandler
{
private Object sub;
public DynamicSubject(Object obj)
{
this.sub = obj;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
System.out.println("before calling: " + method);
method.invoke(sub, args);
System.out.println(args == null);
System.out.println("after calling: " + method);
returnnull;
}
}
下面是客户端
package com.itheima;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
publicclass Client
{
publicstaticvoid main(String[] args)
{
RealSubject realSubject = new RealSubject();
InvocationHandler handler = new DynamicSubject(realSubject);
Class<?> classType = handler.getClass();
// 下面的代码一次性生成代理
Subject subject = (Subject) Proxy.newProxyInstance(classType
.getClassLoader(), realSubject.getClass().getInterfaces(),
handler);
subject.request();
System.out.println(subject.getClass());
}
}