什么是反射
在了解反射之前先来看下面的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语言的反射机制。反射被视为动态语言的关键。--- 百度百科
如何使用
使用反射大致分为三个步骤
- 获取类的Class对象;
- 通过Class对象获取类的内容(构造方法,成员方法, 成员变量)
- 对获取的内容进行操作
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