Java中的反射是一种强大的工具,可以创建灵活的代码,这些代码可以在运行时装配,无须在组件中进行链接。反射允许在编写与执行时,使程序代码能接触到装载到JVM中类的内部信息。Java中的类的反射Reflectio是Java程序对自身进行检查或者‘自审’,并且能直接操作程序内部的属性,实际上它的应用不多,但在框架开发中会经常用到,其他的程序设计中也根本本不存在这样的反射机制。下面是一些常用的代码片段:
package com.java24hours.reflection;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.junit.Test;
import com.java24hours.Person;
public class TestReflection {
//在反射以前,创建对象并调用其方法属性
@Test
public void test1() throws Exception {
Person p = new Person();
p.setName("test");
p.setAge(27);
System.out.println(p);
p.show();
p.display("HK");
}
//有了反射之后,做同样的事情这么来做
@Test
public void test2() throws Exception {
Class<Person> clazz = Person.class;
//1.创建clazz对应的运行时类Person,用的是clazz.newInstance()
//这里的clazz.newInstance()实际上是调用的是Person类的无参构造器
Person p = clazz.newInstance();
System.out.println(p);
//2.然后在调用,调用的时候先用clazz.getField(“属性名”)的方法来获取public作用域的属性,然后再往对象里面设值
Field f1 = clazz.getField("name");
f1.set(p, "test2");
System.out.println(p);
//获取private作用域的属性值方法比一样,用上面的方法获取不到
//首先应该获取声明的属性
Field f2 = clazz.getDeclaredField("age");
//将访问权限改为true
f2.setAccessible(true);
f2.set(p, 20);
System.out.println(p);
//3.通过反射调用运行类的方法,首先获取方法
Method m1 = clazz.getMethod("show");
//然后调用方法,没有形参的直接m1.invoke(p);
m1.invoke(p);
//调用有形参的方法,clazz.getMethod时需要指明参数的类型
Method m2 = clazz.getMethod("display", String.class);
//有形参的话m2.invoke(p,形参);传入形参
m2.invoke(p, "HK");
}
/*
* java.lang.Class是反射的源头
* 创建的类通过编译(javac.exe),生成对应点.class文件,之后我们使用的java.exe加载(JVM的类加载器)
* .class文件,此。class文件加载到内存以后,就是一个运行时类,放在缓存中,那么这个运行时类本身就是一个Class类
* 1.每个运行时类只加载一次
* 2.有了class实例以后才能进行如下操作
* 1)创建对应的运行时类的对象
* 2)获取对应的运行时类的完整结构(属性、方法、构造器、内部类..)
* 3)调用对应的运行时类的指定的结构(属性、方法、构造器)
* 4)反射的应用:动态代理
*/
@Test
public void test3() {
Person p = new Person();
//得到运行时类
Class myclass = p.getClass();
System.out.println(myclass);
}
//如何获取Class的实例
@Test
public void test4() throws Exception {
//方法1
Class clazz = Person.class;
System.out.println(clazz.getName());
System.out.println(String.class.getName());
//方法2.通过运行时类的对象获取
Person p = new Person();
Class clazz2 = p.getClass();
System.out.println(clazz2.getName());
//方法3.通过Class的静态方法
String classNme = "com.java24hours.Person";
Class clazz3 = Class.forName(classNme);
System.out.println(clazz3.getName());
//方法4.用类加载器
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(classNme);
//利用类加载器来加载配置文件
// InputStream is = cl.getResourceAsStream("xx");
// Properties pro = new Properties();
// pro.load(is);
// String 属性名 = pro.getProperty("xx配置文件的属性名");
System.out.println(clazz4.getName());
}
//获取对应的运行时类的属性
@Test
public void testField() {
Class clazz = Person.class;
//getFields获取该类及其父类中权限为public的属性
Field[] fields = clazz.getFields();
System.out.println(fields[0]);
//2.获取该类本身类中的属性用如下的方法getDeclaredFields(),
Field[] fields2 = clazz.getDeclaredFields();
for(int i=0;i<fields2.length;i++) {
System.out.println(fields2[i].getName());
}
}
//权限修饰符 变量类型 变量名
@Test
public void test5() {
Class clazz = Person.class;
Field[] fields = clazz.getDeclaredFields();
for(Field f: fields) {
//1.获取每个属性的权限修饰符
int i = f.getModifiers();
//将权限的索引转变成对应的权限名字
String m = Modifier.toString(i);
//2.获取每个属性的变量类型
String type = f.getType().getName();
//3.获取每个属性 的变量名
String name = f.getName();
System.out.println(m + "-" + type + "-" + name);
}
}
//获取方法的一些属性
@Test
public void test06() {
Class clazz = Person.class;
//获取运行时类及其父类中public权限的方法
Method[] methods = clazz.getMethods();
for(Method m : methods) {
System.out.println(m.getName() + ": " + Modifier.toString(m.getModifiers()) );
}
System.out.println("===============");
//getDeclaredMethods是获取该类的中所有方法
Method[] methods2 = clazz.getDeclaredMethods();
for(Method m : methods2) {
//获取注解
Annotation[] ann = m.getAnnotations();
for(Annotation a : ann) {
System.out.println(a);
}
//获取形参列表
Class rt = m.getReturnType();
//获取异常类型
System.out.println("方法名:" + m.getName() + "-权限修饰符:" + Modifier.toString(m.getModifiers()) + "-注解:" + ann + "-返回值类型:" + rt);
}
System.out.println("===================");
}
}
Java中反射机制主要是应用在动态代理上,这里面分为静态代理和动态代理区别在于,
静态代理:代理类和目标对象的类都是在编译期间确定下来的,而且每个代理只能为一个接口服务,这样在开发过程中必然产生过多的代理,不利于程序的扩展,于是产生了动态代理。
这里注意一下,正常情况下,被代理对象是一个实现一个抽象接口的类,代理对象是一个实现该抽象接口的代理类,而动态代理是一个实现了InvocationHandler接口的类。
下面是一段静态代理的一个实例:
/**
* 静态代理模式
* @author Weiguo Liu
*
*/
//接口
interface ClothFactory {
void productCloth();
}
//被代理类
class NikeClothFactory implements ClothFactory {
@Override
public void productCloth() {
System.out.println("Nike生产衣服");
}
}
//代理类,这里实现了接口,很明显他只能服务于ClothFactory这个接口
class ProxyFactory implements ClothFactory {
//声明的时候声明成ClothFactory
ClothFactory cf;
//创建代理类,传入的是一个被代理类的对象
public ProxyFactory(ClothFactory cf) {
this.cf = cf;
}
@Override
public void productCloth() {
System.out.println("代理类执行");
cf.productCloth();
}
}
public class TestSaticProxy {
public static void main(String[] args) {
//创建被代理的对象
NikeClothFactory nike = new NikeClothFactory();
//创建代理对象,因为是静态代理,所以这个代理只能为NikeClothFactory这一个对象做代理,
//当创建新的对象时,将又要创建代理对象,如果被代理的对象很多的话,就要创建很多的代理对象,很不方便
ProxyFactory pf = new ProxyFactory(nike);
pf.productCloth();
}
}
下面来看动态代理的实现:
package com.java24hours.reflection;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理
* @author Weiguo Liu
*
*/
//被代理类
interface Subject {
void action();
}
//代理类
class RealSubject implements Subject {
@Override
public void action() {
System.out.println("代理类执行");
}
}
//创建动态代理类,注意动态代理都要实现一个InvocationHandler的接口
class MyInnvocationHandler implements InvocationHandler {
//实现了接口的被代理类的对象声明,这里由于是因为不能确定被代理类的对象的类,所以同一用Object类,在调用的时候
//根据被代理类的类型做适当的强转即可
Object obj;
//给被代理对象实例化,返回一个代理类的对象
public Object blind(Object obj) {
this.obj = obj;
//三个参数意义(被代理类的类加载器, 被代理类实现的接口, 实现InvocationHandler的类,即动态代理的类)
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}
//当通过代理类的对象发起对被重写的方法调用时,都会转换为如下的invoke方法的调用,即调用具体接口中的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用被代理对象的方法,获取返回对象
Object rv = method.invoke(obj, args);
return rv;
}
}
public class TestDynamicProxy {
public static void main(String[] args) {
//被代理对象
RealSubject rs = new RealSubject();
//创建实现InnvocationHandler接口的动态代理类对象
MyInnvocationHandler mi = new MyInnvocationHandler();
//调用blind()方法,将被代理对象绑定到代理对象上,即设置代理对象
Object obj = mi.blind(rs);
//根据具体的被代理对象做强转,都是强转成对象的服务接口,这里的sub就是转成Subject接口对象
Subject sub = (Subject)obj;
//调用接口方法
sub.action();
NikeClothFactory nike = new NikeClothFactory();
obj = mi.blind(nike);
ClothFactory pf = (ClothFactory)obj;
pf.productCloth();
}
}
这里在举一个动态代理的AOP的典例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理与AOP面向切面编程
* @author Weiguo Liu
*
*/
//创建接口
interface Human {
void info();
void fly();
}
//创建实现接口的被代理类
class SuperMan implements Human {
@Override
public void info() {
System.out.println("我是超人");
}
@Override
public void fly() {
System.out.println("我可以飞");
}
}
class HumanUtil {
public void method1(){
System.out.println("====method1====");
}
public void method2() {
System.out.println("====method2=====");
}
}
//创建实现InvocationHandler接口的动态代理类
class MyInvocationHandler implements InvocationHandler {
Object obj;
public void setObject(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
HumanUtil hu = new HumanUtil();
hu.method1();
Object rv = method.invoke(obj, args);
hu.method2();
return rv;
}
}
//代理类
class MyProxy {
public static Object getProxyInstance(Object obj) {
MyInvocationHandler handler = new MyInvocationHandler();
handler.setObject(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
}
}
public class TestAOProxy {
public static void main(String[] args) {
SuperMan man = new SuperMan();
Object obj = MyProxy.getProxyInstance(man);
Human hu = (Human)obj;
hu.info();
System.out.println("888888888888888888");
hu.fly();
System.out.println("====================");
NikeClothFactory nf = new NikeClothFactory();
ClothFactory cf = (ClothFactory)MyProxy.getProxyInstance(nf);
cf.productCloth();
}
}