Java中的反射机制

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();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值