Java反射初相识

什么是反射

在了解反射之前先来看下面的Demo,首先定义了一个简单的学生类,其中有两个成员变量,分别是姓名:name 年龄:age 还有一个方法: void study(String val);然后通过两种方式进行调用.

public class Student {
    public String name;
    private int age;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void study(String val){
        System.out.println(name + "喜欢学习" + val);;
    }
}

调用方式一

//初始化对象
Student student = new Student();
//为变量赋值
student.setName("小舍");
//为变量赋值
student.study("Java");
//输出结果为:小舍喜欢学习Java

调用方式二

//获取Student的Class
Class student = Class.forName("com.demo.Student");
//通过Class创建Student对象
Object instance = student.newInstance();
//获取setName方法给对象属性赋值
//获取setName方法,setName有一个字符串的参数
Method setName = student.getMethod("setName", String.class);
//执行setName
//相当于instance.setName"小舍"
setName.invoke(instance, "小舍");
Method study = student.getMethod("study", String.class);
study.invoke(instance,"Java");
//输出结果为:小舍喜欢学习Java

运行后可以发现这两种调用方式的执行结果一致,方式一想必大家都不陌生,开发中经常用到,而方式二便是所谓的反射了.

这两种方式有什么区别呢?

在方式一中,已经确定了初始化的对象是什么,然后可以通过new关键字来创建对象,再通过对象调用方法;而在方式二中,则是在运行时,通过输入的字符串(com.demo.Student)才确认要运行的类什么,调用方法时,则通过类中的方法名和参数类型来调用.

可以说反射就是在运行时,可以创建任意一个对象,并且可以操作其任意一个变量或者方法.

现在看看来源于百度百科的定义,还有什么疑问吗?

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。--- 百度百科

如何使用

使用反射大致分为三个步骤

  1. 获取类的Class对象;
  2. 通过Class对象获取类的内容(构造方法,成员方法, 成员变量)
  3. 对获取的内容进行操作

1.获取Class对象

//1. 通过对象调用getClass方法获取。
Student stu0 = new Student();
Class stu0Class = stu0.getClass();
//2. 通过类的class属性获取。
Class stu1Class = Student.class;
//3. 通过Class的静态方法forName获取
Class stu2Class = Class.forName("com.demo.Student");

一个类只有一个Class类型的对象,无论怎么获取,获取的都是同一个Class

System.out.println(stu0Class == stu1Class);//true
System.out.println(stu0Class == stu2Class);//true
System.out.println(stu1Class == stu2Class);//true

2.通过Class对象获取类的内容

/**
 * T newInstance()创建对象
 */
Student student = (Student)stuClass.newInstance();
/**
 * Constructor getConstructor(Class... parameterTypes):获取空参构造方法
 */
Constructor constructor = stuClass.getConstructor();
/**
 *  Constructor[] getConstructors():获取所有的构造方法
 */
Constructor[] constructors = stuClass.getConstructors();
/**
 * Method[] getMethods():获取类中所有成员方法
 */
Method[] methods = stuClass.getMethods();
/**
 *  Method getMethod(String name, Class... parameterTypes):获取到的类中的指定的成员方法。 第一个参数是方法名, 第二个参数是该方法的参数列表。
 */
Method study = stuClass.getMethod("study", String.class);
/**
 * 反射获取set/get方法分别完成赋值和取值的操作
 */
Method setName = stuClass.getMethod("setName", String.class);
/**
 *  Object invoke(Object obj, Object... args):执行获取的方法 第一个参数是调用者对象,第二个对象表示调用方法时传递的实际参数
 */
setName.invoke(student,"小舍");
study.invoke(student,"Java");//小舍喜欢学习Java

/**
 *  Field getField(String name):获取成员变量, 只能获取到public权限的成员变量。
 */
Field nameField = stuClass.getField("name");//public java.lang.String com.demo.Student.name
String name = nameField.getName();//name
Field ageField = stuClass.getField("age");//java.lang.NoSuchFieldException: age(权限为 private)

3.对获取的内容进行操作

/**
 *  Object invoke(Object obj, Object... args):执行获取的方法 第一个参数是调用者对象,第二个对象表示调用方法时传递的实际参数
 */
setName.invoke(student,"小舍");
study.invoke(student,"Java");//小舍喜欢学习Java

存在的意义

通过反射,可以让程序创建和控制任何类的对象,无需提前硬编码目标类.例如,如果没有反射,每当我们构建对象时,都要同过new 关键字来创建对象,提前对对象进行编码,如果要改变对象,那么必须修改源代码,并重新编译.如果使用反射,可以将类(com.demo.Student)写在配置文件中,这样可以通过修改参数,来改变实例化对象.

反射机制极大的提高了程序的灵活性和扩展性,降低模块的耦合性,提高自身的适应能力,是构建框架技术的基础所在.框架就是抽取重复的代码,提高编码效率,如果提前硬编码目标类,那如何抽取重复代码呢?

判断对象成员变量是否为空

    public static void main(String[] args) throws IllegalAccessException {
        Student student = new Student();
        student.setAge(18);
        student.setName("小舍");
        String[] fieldArray = {"name", "age"};
        List<String> fieldList = Arrays.asList(fieldArray);
        if (checkObjectFiledIsNull(student,fieldList)){
            System.out.println("参数不得为空");
        }
    }

	/**
     * 校验对象字段是否为空
     *
     * @param object    对象
     * @param fieldList	需要判空的变量名
     * @return 为空 返回true
     * @throws IllegalAccessException
     */
    public static boolean checkObjectFiledIsNull(Object object, List<String> fieldList) throws IllegalAccessException {
        if (object == null) {
            return true;
        }
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            if (fieldList.contains(field.getName())) {
                Object obj = field.get(object);
                if (StringUtils.isEmpty(obj)) {
                    return true;
                }
            }
        }
        return false;
    }

(1)student.setAge(18);

(2)student.setName("小舍");

注意:去掉(1)并不会打印"参数不得为空",而去掉(2)则会打印,是因为int变量的默认初始值为0

上一篇:java_多线程 (二)


下一篇:Java HashSet基本使用方法 常见API