java反射

231 阅读9分钟

通过反射获取类的完整结构

1、获取对应的运行时类的属性(Field)

public void testField1(){
    Class clazz= Person.class;
    System.out.println(clazz);//class com.bjlemon.demo.test.Person
    //1、getFields():只能获取到运行时类中及其父类中声明为public的属性
    //Field[] fields=clazz.getFields();
    //2、getDeclaredFields():获取运行时类本身声明的所有属性
    Field[] fields=clazz.getDeclaredFields();
    for(Field field:fields){
        //field:private java.lang.String com.bjlemon.demo.test.Person.name
        System.out.println(field.getName());//name
    }
}
public void testField2(){
    Class clazz= Person.class;
    Field[] fields=clazz.getDeclaredFields();
    for(Field field:fields){
        //1、getModifiers():以整数形式返回此Field的修饰符(0:默认、1:public、2:private、4:protected)
        int i=field.getModifiers();
        //2、返回数字对应的权限修饰符
        String str=Modifier.toString(i);
        System.out.print(str+" ");//private
        //3、getType():获取属性的变量类型
        Class type=field.getType();
        //type:class java.lang.String
        //type.getName():java.lang.String
        System.out.print(type.getSimpleName()+" ");//String
        //4、getName():获取属性名
        //field:private java.lang.String com.bjlemon.demo.test.Person.name
        System.out.print(field.getName());//name
        System.out.println();
    }
}

2、获取对应的运行时类的方法(Method)

public void testMethod1(){
    Class clazz= Person.class;
    //1、getMethods():只能获取到运行时类中及其父类中声明为public的方法
    //Method[] methods=clazz.getMethods();
    //2、getDeclaredFields():获取运行时类本身声明的所有方法
    Method[] methods=clazz.getDeclaredMethods();
    for(Method method:methods){
        //method:public java.lang.String com.bjlemon.demo.test.Person.toString()
        System.out.println(method.getName());//toString
    }
}
public void testMethod2() {
    Class clazz = Person.class;
    Method[] methods=clazz.getDeclaredMethods();
    for(Method method:methods){
        //1、getAnnotations:获取方法的注解
        Annotation[] annotations=method.getAnnotations();
        for(Annotation annotation:annotations){
            //annotation:@com.bjlemon.demo.test.MyAnnotation(value=abc123)
            System.out.println(annotation);
        }

        //2、getModifiers():以整数形式返回此Method的修饰符(0:默认、1:public、2:private、4:protected)
        int i=method.getModifiers();
        //返回数字对应的权限修饰符
        String type=Modifier.toString(i);
        System.out.print(type+" ");//public

        //3、getReturnType():获取方法的返回值类型
        Class cla=method.getReturnType();
        //cla:class java.lang.String
        //cla.getName():java.lang.String
        //cla.getSimpleName():String
        System.out.print(cla.getSimpleName()+" ");//String

        //4、getName():获取方法的名字
        System.out.print(method.getName()+" ");//toString

        //5、getParameterTypes():获取方法的形参列表类型
        System.out.print("(");
        Class[] params=method.getParameterTypes();
        for(int j=0;j<params.length;j++){
            if(j!=params.length-1){
                System.out.print(params[j].getSimpleName()+" args-"+j+",");
            }else{
                System.out.print(params[j].getSimpleName()+" args-"+j);//String arg-0
            }

        }
        System.out.print(")");

        //6、getExceptionTypes():获取方法的异常信息
        Class[] cl=method.getExceptionTypes();
        if(cl.length>0){
            System.out.print("throws ");
        }
        for(int k=0;k<cl.length;k++){
            System.out.println(cl[k].getSimpleName());//Exception
        }
        System.out.println();
    }
}

3、获取对应的运行时类的构造器(Constructor)

public void testConstructor(){
    Class clazz=Person.class;
    //1、getConstructors():只能获取到运行时类中及其父类中声明为public的构造方法
    //Constructor[] constructors=clazz.getConstructors();
    //2、getDeclaredConstructors():获取运行时类本身声明的所有构造方法
    Constructor[] constructors=clazz.getDeclaredConstructors();
    for(Constructor constructor:constructors){
        //3、getModifiers():以整数形式返回此Method的修饰符(0:默认、1:public、2:private、4:protected)
        int i=constructor.getModifiers();
        //返回数字对应的权限修饰符
        String type=Modifier.toString(i);
        System.out.print(type+" ");//public

        //4、getName():获取构造器的名字
        System.out.print(clazz.getSimpleName());//Person
        System.out.print("(");

        //5、getParameterTypes():获取方法的形参列表类型
        Class[] cls=constructor.getParameterTypes();
        for(int j=0;j< cls.length;j++){
            if(j!=cls.length-1){
                System.out.print(cls[j].getSimpleName()+" args-"+j+",");
            }else{
                System.out.print(cls[j].getSimpleName()+" args-"+j);
            }
        }
        System.out.println("){");
        System.out.println("}");
    }
}

4、获取运行时类的父类(Class)

public void testSuperClass(){
    Class clazz=Person.class;
    //getSuperclass():返回此class所表示的实体的父类
    Class superClass=clazz.getSuperclass();
    //superClass:class com.bjlemon.demo.test.Creature
    //superClass.getName():com.bjlemon.demo.test.Creature
    //superClass.getSimpleName():Creature
    System.out.println(superClass.getSimpleName());//Creature
}

5、获取运行时类的带泛型的父类(Class)

public void testGenericSuperClass(){
    Class clazz=Person.class;
    //getGenericSuperclass():返回此class所表示的实体的带泛型的父类
    Type type=clazz.getGenericSuperclass();
    //type:com.bjlemon.demo.test.Creature<java.lang.String>
    System.out.println(type);
}

6、获取运行时类的父类的泛型(Type)

public void testGenericSuperClassType(){
    Class clazz=Person.class;
    //1、getGenericSuperclass():返回此class所表示的实体的带泛型的父类
    Type type=clazz.getGenericSuperclass();
    //2、instanceof:判断某个对象是否是某个已定义的对象的实例
    //boolean result=type instanceof ParameterizedType;
    //result:布尔类型
    //type:任意对象表达式
    //ParameterizedType:任意已定义的对象类
    //如果type是ParameterizedType的一个实例则返回true,反之亦然
    if(type instanceof ParameterizedType){
        //parameterizedType:com.bjlemon.demo.test.Creature<java.lang.String>
        ParameterizedType parameterizedType=(ParameterizedType) type;
        //3、getActualTypeArguments():返回此类的实际类型参数的Type对象的数组
        Type[]typeArgs=parameterizedType.getActualTypeArguments();
        if(typeArgs!=null && typeArgs.length>0){
            for(Type args:typeArgs){//args:class java.lang.String
                if(args instanceof Class){
                    Class c=(Class)args;
                    System.out.println(c.getSimpleName());//String
                }
            }
        }
    }
}

7、获取运行时类实现的接口(Interface)

public void testInterface(){
    Class clazz=Person.class;
    //getInterfaces():获取运行时类本身所实现的接口
    Class[] cls=clazz.getInterfaces();
    for(Class c:cls){
        //c:interface java.lang.Comparable
        System.out.println(c.getSimpleName());
    }
}

8、获取运行时类所在的包(Package)

public void testPackage(){
    Class clazz=Person.class;
    //getPackage():获取类所在的包
    Package pack=clazz.getPackage();
    //pack:package com.bjlemon.demo.test
    //pack.getName():com.bjlemon.demo.test
    System.out.println(pack.getName());
}

9、获取运行时类的注释()

public void test11(){
    Class clazz=Person.class;
    //getAnnotations():获取运行时类的注释
    Annotation[] annotations=clazz.getAnnotations();
    for(Annotation annotation:annotations){
        //annotation:@com.bjlemon.demo.test.MyAnnotation(value=lemon)
        System.out.println(annotation);
    }
}

小结

1、在实际的操作中,取得类的信息的操作代码,并不会经常开发
2、一定要熟悉java.lang.reflect包的作用,反射机制
3、如何取得属性、方法、构造器的名称,修饰符等

通过反射调用类中的指定方法、指定属性、指定构造器

1、调用运行时类中的指定属性(Field)

public void testField3()throws Exception{
    Class clazz=Person.class;
    //1、getField(String fieldName):获取运行时类中声明为public的指定属性名为fieldName的属性
    //2、getDeclaredField(String fieldName):获取运行时类中指定属性名为fieldName的属性
    Field name=clazz.getDeclaredField("name");
    //3、newInstance():创建运行时类的对象
    Person p=(Person)clazz.newInstance();
    System.out.println(p);
    //4、setAccessible(true):由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作,,取消语言访问检查
    name.setAccessible(true);
    //5、set(Object obj,Object value)将运行时类的指定的属性赋值
    name.set(p,"Jerry");
    
    Field age=clazz.getDeclaredField("age");
    age.setAccessible(true);
    age.set(p,18);
    //6、get(Object obj):取得指定对象obj上此Field的属性值
    int a=(int)age.get(p);//18
    System.out.println(p);
}

2、调用运行时类中的指定方法(Method)

public void testMethod3()throws Exception{
    Class clazz=Person.class;
    //1、getMethod(String methodName,Class...params):获取运行时类中声明为public的指定的方法
    //2、getDeclaredMethod(String methodName,Class...params):获取运行时类中声明了的指定的方法
    Method m1=clazz.getDeclaredMethod("show");
    Person p=(Person) clazz.newInstance();
    //3、Object invoke(Object obj,Object...args):调用指定的方法
    m1.invoke(p);
    System.out.println(m1.invoke(p));//null

    Method m2=clazz.getDeclaredMethod("toString");
    m2.invoke(p);
    System.out.println(m2.invoke(p));//Person{name='null', age=0}

    //4、对于运行时类中静态方法的调用,形参Object obj可为null
    Method m3=clazz.getMethod("info");
    m3.invoke(Person.class);

    Method m4=clazz.getDeclaredMethod("display",String.class,Integer.class);
    //5、setAccessible(true):访问私有方法时,让私有方法可见
    m4.setAccessible(true);
    m4.invoke(p,"中国",10);
    System.out.println(m4.invoke(p,"中国",10));//10
}

3、调用指定的构造器(Constructor),创建运行时类对象

public void testConstructor2()throws Exception{
    Class clazz=Person.class;
    //1、getConstructors():获取public修饰的构造方法
    //2、getDeclaredConstructor():获取本类中的构造方法
    Constructor constructor=clazz.getDeclaredConstructor(String.class,int.class);
    //3、私有构造器,让私有构造器可见
    constructor.setAccessible(true);
    //4、newInstance(Object...args):创建运行时类对象
    Person p=(Person) constructor.newInstance("灵棋",16);
    System.out.println(p);
}

Object invoke(Object obj, Object … args)说明

1、Object 对应原方法的返回值,若原方法无返回值,此时返回null
2、若原方法若为静态方法,此时形参Object obj可为null
3、若原方法形参列表为空,则Object[] args为null
4、若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法

java静态代理

//接口:服装厂生产衣服
interface ClothFactory{
    void productCloth();
}
//被代理类:Nike服装厂
class NikeClothFactory implements ClothFactory{
    @Override
    public void productCloth() {
        System.out.println("Nike工厂生产一批衣服");
    }
}
//代理类
class ProxyFactory implements ClothFactory{
    private ClothFactory cf;
    //创建代理类的对象时,实际传入一个被代理类的对象
    public ProxyFactory(ClothFactory cf){
        System.out.println("代理类开始执行,收代理费$1000");
        this.cf=cf;
    }
    @Override
    public void productCloth() {
        cf.productCloth();
    }
}
public class TestClothProduct {
    public static void main(String[] args) {
        NikeClothFactory nike=new NikeClothFactory();//创建被代理类的对象
        ProxyFactory proxy=new ProxyFactory(nike);//创建代理类的对象
        proxy.productCloth();
    }
}

特征

1、代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展
2、每一个代理类只能为一个接口服务,程序开发中必然产生过多的代理
3、最好可以通过一个代理类完成全部的代理功能(jdk动态代理)

java动态代理

特征

1、客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
2、 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理,代理对象决定是否以及何时将方法调用转到原始对象上
3、Proxy:专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。
//动态代理的使用,反射是动态语言的关键
interface Subject{
    void action();
}
//被代理类
class RealSubject implements Subject{
    @Override
    public void action() {
        System.out.println("我是被代理类,记得要执行我");
    }
}
class MyInvocationHandler implements InvocationHandler{
    Object obj;//实现了接口的被代理类的对象的声明
    //给被代理的对象实例化,返回一个代理类对象
    public Object blind(Object obj){
        this.obj=obj;
        //static Object newProxyInstance(ClassLoader loader,Class<?>[]interface,InvovationHandler h):直接创建一个动态代理对象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
    }

    /**
     * 当通过代理类的对象发起对被重写的方法的调用时,都会转换为对如下invoke方法的调用
     * @param proxy:被代理的对象
     * @param method:要调用的方法
     * @param args:方法调用时所需要的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //method方法的返回值是returnVal
        Object returnVal=method.invoke(obj,args);
        return returnVal;
    }
}
public class TestProxy {
    public static void main(String[] args) {
        //被代理类的对象
        RealSubject real=new RealSubject();
        //创建一个实现了InvocationHandler接口的类的对象
        MyInvocationHandler handler=new MyInvocationHandler();
        //调用blind()方法,动态的返回一个同样实现了real所在类实现的接口Subject的代理类的对象
        Object obj=handler.blind(real);
        //此时的sub就是代理类的对象
        Subject sub=(Subject)obj;
        //转到对InvocationHandler接口的实现类的invoke()方法的调用
        sub.action();

        //被代理类对象
        NikeClothFactory nike=new NikeClothFactory();
        //proxyCloth即为代理类的对象
        ClothFactory proxyCloth=(ClothFactory) handler.blind(nike);
        proxyCloth.productCloth();
    }
}

动态代理与AOP

1、通常都是为指定的目标对象生成动态代理
2、AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理
/**
 * jdk动态代理工厂,作用为对象生成jdk动态代理对象
 * 条件:当前对象必须有接口实现
 */
interface UserService{
    public void login();//登录
    public Integer queryUserCount();//查询用户个数
}
//被代理类
class UserServiceImpl implements UserService{
    @Override
    public void login() {
        System.out.println("userServiceImp login");
    }

    @Override
    public Integer queryUserCount() {
        System.out.println("userServiceImp queryUserCount..");
        return 0;
    }
}

class ServiceUtil{
    public void method1(){
        System.out.println("方法一");
    }

    public void method2(){
        System.out.println("方法二");
    }
}

class JdkProxyFactory{
    private Object obj;//目标对象:被代理类对象的声明UserServiceImpl
    
    public JdkProxyFactory(Object obj){
        this.obj=obj;
    }

    //返回的object对象它是现实了目标对象接口的动态代理对象
    public Object getProxyInstance(){
        //参数:1、类加载器   2、被代理的目标对象类实现的接口数组   3:对目标对象回调处理对象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),new MyFirstInvocationHandler());
    }

    /**
     * 自定义类,实现invocationHanler接口
     * 作用:方法执行时的回调处理对
     */
    class MyFirstInvocationHandler implements InvocationHandler{
        @Override
        //invoke会在每次目标方法执行时都会执行该方法
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            ServiceUtil serviceUtil=new ServiceUtil();
            serviceUtil.method1();
            Object returnVal=method.invoke(obj,args);
            serviceUtil.method2();
            return returnVal;
        }
    }
}
public class TestAOP {
    public static void main(String[] args) {
        UserService service=new UserServiceImpl();//创建了一个被代理类的对象
        JdkProxyFactory factory=new JdkProxyFactory(service);
        UserService proxyService=(UserService)factory.getProxyInstance();
        proxyService.login();//通过代理类的对象调用重写的抽象方法
        System.out.println();
        proxyService.queryUserCount();
    }
}