1.Java 反射:
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
2.Java 反射的作用:
1)在运行时判断任意一个对象所属的类
2)在运行时构造任意一个类的对象
3)在运行时判断任意一个类所具有的成员变量和方法
4)在运行时调用任意一个对象的成员变量和方法
5)生成动态代理
3.反射的API
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造方法
实例:
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.junit.Test;
public class TestReflection {
@Test
public void testClass() throws Exception{
Class<Person> clazz = Person.class;
/**
* 创建对象
*/
//1.创建运行时类Person的对象:newInstance()
Person person = clazz.newInstance();
/**
* 属性操作
*/
//1.获取public属性名:getFields()
Field[] fields = clazz.getFields();
for(int i = 0;i < fields.length;i++){
System.out.println("###" + fields[i].getName());
}
//2.获取全部属性名:getDeclaredFields()
Field[] fields1 = clazz.getDeclaredFields();
for(int i = 0;i < fields1.length;i++){
System.out.println("!!!" + fields1[i].getName());
}
//3.获取public属性:getField(String fieldname)
Field field = clazz.getField("name");
//通过set方法,为对应的类的对应属性赋值
field.set(person, "LiuDeHua");
System.out.println("name : " + person.getName());
//4.获取非public属性:getgetDeclaredField(String fieldName)
Field field2 = clazz.getDeclaredField("age");
//通过setAccessible方法,改变private属性的封装性
field2.setAccessible(true);
field2.set(person, 40);
System.out.println("age : " + person.getAge());
/**
* 方法操作
*/
//1.获取运行时类方法的名称:getDeclatedMethods() getMethods()
Method[] methods = clazz.getDeclaredMethods();
for(int i = 0;i < methods.length;i++){
System.out.println("@@@" + methods[i].getName());
}
//2.获取运行时类指定的方法:getMethod(String methodName,Class...paramtype)
Method method = clazz.getMethod("show");
method.invoke(person);
Method method2 = clazz.getMethod("display",String.class,int.class);
method2.invoke(person, "xiaoming",10);
}
}
4.java.lang.Class:是反射的源头
1)public final Class getClass():在Object类中定义了该的方法,此方法将被所有子类继承。该方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称
2)生成Class实例之后,我们可以进行的操作
(1)创建对应运行时类对象
(2)获取对应运行时类的完整结构(属性、方法、构造器,父类,所在的包,注解,异常等等)
(3)调用对应的运行时类指定的结构(属性,方法,构造器)
(4)反射的应用:动态代理
3)如何获取Class实例(3种)
/**
* 获取Class实例方法
* @throws Exception
*/
@Test
public void testGetClassInstance() throws Exception{
//1.调用运行时类本身的.class属性
Class clazz = Person.class;
System.out.println(clazz.getName());
//2.通过运行时类的对象获取
Person person = new Person();
Class clazz1 = person.getClass();
System.out.println(clazz1.getName());
//3.通过Class静态方法forName(String classpath)
Class clazz2 = Class.forName("reflection.Person");
System.out.println(clazz2.getName());
//4.通过类的加载器:
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz3 = classLoader.loadClass("reflection.Person");
System.out.println(clazz3.getName());
}
4)Class类的常用方法
5)ClassLoader
/**
* ClassLoader获取输入流
* @throws Exception
*/
@Test
public void testClassLoeader() throws Exception{
ClassLoader classLoader = this.getClass().getClassLoader();
//通过getResourceStream(String filepath)可以得到一个输入流
InputStream inputStream = classLoader.getResourceAsStream("classloader.txt");
FileOutputStream fos = new FileOutputStream("classloader1.txt");
byte[] b = new byte[10];
int len;
while((len = inputStream.read(b)) != -1){
fos.write(b,0,len);
}
}
5.创建类对象:调用Class对象的newInstance()方法 要 求:1)类必须有一个无参数的构造器。2)类的构造器的访问权限需要足够。
/**
* 创建运行时类对象,使用newInstance(),实际上就是调用了运行时类的空参构造器
* 要求:1)对应的运行时类要有空参构造器
* 2)构造器的权限要足够
* @throws Exception
*/
@Test
public void testNewInstance() throws Exception{
String className = "reflection.Person";
Class clazz = Class.forName(className);
Person person = (Person)clazz.newInstance();
System.out.println(person);
}
6.通过反射调用类的完整结构
1)获取属性
/**
* 获取属性相关信息
* @throws SecurityException
* @throws Exception
*/
@Test
public void testGetFieldInfo() throws Exception{<span style="background-color: rgb(255, 153, 0);">
</span> Class clazz = Person.class;
//1.获取public属性名:getFields()
Field[] fields = clazz.getFields();
//2.获取全部属性名:getDeclaredFields()
Field[] fields1 = clazz.getDeclaredFields();
//3.获取public属性:getField(String fieldname)
Field field = clazz.getField("name");
//4.获取非public属性:getgetDeclaredField(String fieldName)
Field field2 = clazz.getDeclaredField("age");
//5.获取属性的权限符 变量类型 变量名称
for(Field f : fields1){
//1)权限符
int i = f.getModifiers();
String name = Modifier.toString(i);
System.out.println(name);
//2)变量类型
Class type = f.getType();
System.out.println(type.getName());
//3)变量名称
String methodName = f.getName();
System.out.println(methodName);
}
}
2)获取方法
/**
* 获取方法相关属性
* @throws SecurityException
* @throws Exception
*/
@Test
public void testGetMethodInfo() throws Exception{
Class clazz = Person.class;
//1.getMethods():返回运行时类及其父类中所有public的方法
Method[] methods = clazz.getMethods();
for(Method m : methods){
System.err.println(m);
}
//getDeclatedMethods():返回运行时类自己本身声明的所有方法
Method[] methods2 = clazz.getDeclaredMethods();
for(Method m : methods2){
System.out.println(m);
}
//2.获取运行时类指定的方法:getMethod(String methodName,Class...paramtype)
Method method = clazz.getMethod("show");
Method method2 = clazz.getMethod("display",String.class,int.class);
}
3)获取父类泛型
/**
* 获取父类的泛型:JDBC中会用到
* @throws SecurityException
* @throws Exception
*/
@Test
public void testGenericType() throws Exception{
Class clazzClass = Person.class;
Type type = clazzClass.getGenericSuperclass();
ParameterizedType paraType = (ParameterizedType)type;
Type[] types = paraType.getActualTypeArguments();
for(Type t : types){
System.out.println(((Class)t).getName());
}
}
7.通过反射调用类中指定方法和属性
1)调用指定属性
public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。
public Object get(Object obj) 取得指定对象obj上此Field的属性内容
public void set(Object obj,Object value) 设置指定对象obj上此Field的属性
public void setAccessible(true)访问私有属性时,让这个属性可见
/**
* 调用指定属性
*/
@Test
public void test() throws Exception{
Class clazz = Person.class;
//1.创建Person对象
Person person = (Person)clazz.newInstance();
//2.为public属性赋值并获取
Field field = clazz.getField("name");
field.set(person, "xiaoming");
field.get(person);
System.out.println(field.get(person));
//3.为非public属性赋值并获取
Field field2 = clazz.getDeclaredField("age");
field2.setAccessible(true);
field2.set(person, 10);
field2.get(person);
System.out.println(field2.get(person));
}
2)调用指定方法
通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
/**
* 调用指定方法
*/
@Test
public void test2() throws Exception{
Class clazz = Person.class;
Person person = (Person)clazz.newInstance();
Method method = clazz.getMethod("show");
Object returnVal = method.invoke(person);
System.out.println("show's returnVal : " + returnVal);
Method method2 = clazz.getMethod("toString");
Object returnVal2 = method2.invoke(person);
System.out.println("toString's returnVal : " + returnVal2);
Method method3 = clazz.getMethod("display", String.class,int.class);
Object returnVal3 = method3.invoke(person, "xiaohong",10);
System.out.println("display's returnVal : " + returnVal3);
}
8.动态代理
1)动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
2)代理设计模式的原理:使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上
3)代理类的作用:
(1)授权机制: 不同级别的用户对同一对象拥有不同的访问权利,如Jive论坛系统中,就使用Proxy进行授权机制控制,访问论坛有两种人:注册用户和游客(未注册用户),Jive中就通过类似ForumProxy这样的代理来控制这两种用户对论坛的访问权限.
(2)某个客户端不能直接操作到某个对象,但又必须和那个对象有所互动。举例两个具体情况:如果那个对象是一个是很大的图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,打开文档必须很迅速,不能等待大图片处理完成,这时需要做个图片Proxy来代替真正的图片。如果那个对象在Internet的某个远端服务器上,直接操作这个对象因为网络速度原因可能比较慢,那我们可以先用Proxy来代替那个对象。总之原则是,对于开销很大的对象,只有在使用它时才创建,这个原则可以为我们节省很多宝贵的Java内存. 所以,有些人认为Java耗费资源内存,我以为这和程序编制思路也有一定的关系。
(3)现实中,Proxy应用范围很广,现在流行的分布计算方式RMI和Corba等都是Proxy模式的应用
静态代理--接口
interface ClothFactory{
public void productCloth();
}
静态代理--被代理类
class NikeClothFactory implements ClothFactory{
@Override
public void productCloth() {
System.out.println("生产一批Nike衣服");
}
}
class ADDSClothFactory implements ClothFactory{
@Override
public void productCloth() {
System.out.println("生产一批ADDS衣服");
}
}
静态代理--代理类
class Proxy implements ClothFactory{
<span style="white-space:pre"> </span>private ClothFactory cf;
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>public Proxy(ClothFactory cf) {
<span style="white-space:pre"> </span>this.cf = cf;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>@Override
<span style="white-space:pre"> </span>public void productCloth() {
<span style="white-space:pre"> </span>System.out.println("Proxy fee is $2000");
<span style="white-space:pre"> </span>cf.productCloth();
<span style="white-space:pre"> </span>}
}
静态代理--实体类
public class StaticProxy {
@Test
public void test(){
Proxy proxy = new Proxy(new NikeClothFactory());
proxy.productCloth();
Proxy proxy2 = new Proxy(new ADDSClothFactory());
proxy2.productCloth();
}
}
动态代理类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.junit.Test;
interface Subject {
void action();
}
// 被代理类
class RealSubject implements Subject {
@Override
public void action() {
System.out.println("real subject ...");
}
}
/**
* 动态代理类: 1)实现InvocationHandler接口 2)调用java.lang.Reflect.Proxy类中的静态方法:
* newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
* InvocationHandler,h) ①.loader:与被代理类的类加载器一致 ②.interfaces:与被代理类实现的接口一致
* ③.h:实现了InvocationHandler接口的类对象,通常为this
*/
class MyInvocationHandler implements InvocationHandler {
// 实现了接口的被代理类的对象声明(这里指RealSubject类对象)
Object object;
/**
* 作用: 1.给被代理类对象实例化:就是给上面的Object对象实例化(RealSubject)
* 2.返回一个代理类对象:返回MyInvocationHandler对象
*
* @param obj
* @return 返回MyInvocationHandler对象
*/
public Object blind(Object obj) {
this.object = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(), this);
}
/**
* 当代理对象发起对重写方法的调用时,都会转为对如下invoke方法的调用
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
method.invoke(object, args);
return null;
}
}
public class DynamicProxy {
@Test
public void test() {
// 创建被代理类RealSubject对象
RealSubject realSubject = new RealSubject();
// 创建实现了InvocationHandler接口的类对象
MyInvocationHandler handler = new MyInvocationHandler();
// 调用blind(realSubject)方法,动态的返回一个同样实现了被代理(RealSubject)
// 所实现接口的代理类(MyInvocationHandler)对象
Object obj = handler.blind(realSubject);
Subject subObject = (Subject)obj;
//转到实现了InvocationHandler接口的类对象的invoke()方法
subObject.action();
}
}