反射调用方法

2021/04/12 18:47
阅读数 70

反射调用构造方法

在一个类中除了有继承的关系外,最为重要的操作就是类中的结构处理了,而类中的结构首先需要观察的就是构造方法的使用问题,实际上在之前通过反射实例化对象的时候就已经接触到构造方法的问题了:
实例化方法替代:clazz.getDeclaredConstructor().newInstance()
所有类的构造方法的获取都可以直接通过Class类来完成,该类中定义有如下的几个方法:
获取所有构造方法:
public Constructor<?>[] getDeclaredConstructors() throws SecurityException
获取指定构造方法:
public Constructor<T> getConstructor​() throws SecurityException
获取所有构造方法:
public Constructor<?>[] getConstructors()throws SecurityException
获取指定构造方法:
public Constructor<T> getConstructor​(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

范例:修改Person类的定义
AbstractBase:

public abstract class AbstractBase {
    public AbstractBase() {}
    public AbstractBase(String msg) {}
}

Person:

public class Person extends AbstractBase implements  IChannelService,IMessageService {
    public Person() {}
    public Person(String name, int age) {
}

范例:获取构造
JavaAPIDemo

public class JavaAPIDemo {
    public static void main(String[] args) {
        Class<?> cls = Person.class;//获取指定类的Class对象
        Constructor<?>[] constructors = cls.getDeclaredConstructors();  //获取全部构造
        for (Constructor<?> cons : constructors) {
            System.out.println(cons);
        }
    }
}

//执行结果:public cn.mldn.vo.Person()
//public cn.mldn.vo.Person(java.lang.String,int)

此时获取的是类中的全部构造方法,但是也可以获取一个指定参数的构造。例如:现在的Person类之中有两个构造方法:
修改Person:

public class Person extends AbstractBase implements  IChannelService,IMessageService {
    private String name;
    private int age;
    public Person() {}
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "姓名:" + this.name + "、年龄:" + this.age;
    }
}

此时调用Person类中的有参构造方法进行Person类对象实例化,此时必须指明要调用的构造,而后通过Constructor类中提供的实例化方法操作:
public T newInstance​(Object... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException

范例:调用指定构造实例化对象

import java.lang.reflect.Constructor;

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Person.class;
        Constructor<?> constructor = cls.getConstructor(String.class, int.class);
        Object obj = constructor.newInstance("小强", 78);
        System.out.println(obj);  //姓名:小强、年龄:78
    }
}

虽然程序代码本身允许开发者调用有参构造,但是如果从实际的开发角度出发,所有使用反射的类中最好使用无参构造,因为这样的实例化可以达到统一性。

反射调用普通方法

在进行反射处理的时候也可以通过反射来获取类之中的全部方法,但是需要提醒的是,如果想通过反射调用这些方法,必须有一个前提条件:类之中要提供实例化对象。
在Class类中提供了如下的操作可以获取方法对象:

获取全部方法:
public Method[] getMethods() throws SecurityException
获取指定方法:
public Method getMethod​(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
获取本类全部方法:
public Method[] getDeclaredMethods() throws SecurityException
获取本类指定方法:
public Method getDeclaredMethod​(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
范例:获取全部方法

import java.lang.reflect.Method;
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Person.class;
        {
            Method methods [] = cls.getMethods();//获取全部方法(包括父类中的方法)
            for (Method met : methods){
                System.out.println(met);
            }
        }
        System.out.println("---------------难以忘怀的愚人节的分割线-----------")
        {  // 获取本类方法
            Method methods [] = cls.getDeclaredMethods();
            for (Method met : methods){
                System.out.println(met);
        }

        }
    }

执行结果:
image.png
但是需要注意的是,这时的方法信息的获取是依靠Method类提供的toString()方法完成的,很多时候也可以由用户来拼凑方法信息的展示形式。
范例:自定义方法信息显示

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Person.class;  // 获取指定类的Class对象
        Method methods[] = cls.getMethods();
        for (Method met:methods){
            int mod=met.getModifiers();//修饰符
            System.out.print(Modifier.toString(mod)+" ");
            System.out.print(met.getReturnType().getName()+" ");
            System.out.print(met.getName()+"(");
            Class<?> params [] = met.getParameterTypes();//获取参数类型
            for (int x = 0; x < params.length; x ++) {
                System.out.print(params[x].getName() + " " + "arg-" + x);
                if(x < params.length - 1) {
                    System.out.println(",");
                }
            }
            System.out.print(")");
            class<?> exp [] = met.getExceptionTypes();
            if(exp.length > 0) {
                System.out.print("throws");
            }
            for(int x = 0 ; x < exp.length ; x ++) {
                System.out.print(exp[x].getName());
                if(x < exp.length - 1) {
                    System.out.println(",");
                }
            }
            System.out.println();  //换行
        }
    }
}

这种代码只需要清楚可以根据反射获取方法的结构即可,不需要过多深入了解,但在Method类中有一个致命的重要方法:
public Object invoke​(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException

在Person类里面为name属性追加有setter、getter方法。

public class Person extends AbstractBase implements  IChannelService,IMessageService {
    private String name;
    private int age;
    public Person() {}
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    // 其他操作代码略
}

需要通过反射机制来实现Person类之中的setter与getter方法的调用处理。
范例:在不导入指定类开发包的情况下实现属性的配置

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("cn.mladn.vo.Person"); //获取指定类的Class对象
        // String attribute = "name"; //要操作的类属性
        String value = "小强子";  //要设置的属性内容
        //1.任何情况下如果要想保存类中的属性或者调用类中的方法都必须保证存在有实例化对象,既然不允许导入包,那么就反射实例化
        Object obj = cls.getDeclaredConstructor().newInstance();  //调用无参构造实例化
        //2.如果要想进行方法的调用,那么一定要获取方法的名称
        String setMethodName = "setName";  //方法名称 
        Method method = cls.getDeclaredMethodsetMethodName, String.class);   //获取指定的方法
        method.invoke(obj, value);  //等价于:Person对象.setName(value);
        String getMethodName="getName";
        method=cls.getDeclaredMethod(getMethodName);   //getter没有参数
        System.out.println(getMethod.invoke(obj););  //等价于:Person对象.getName();
    } 
}

利用此类操作整体的形式上不会有任何明确的类对象产生,一切都是依靠反射机制处理的,这样的处理避免了与某一个类耦合问题。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部