一、什么是反射?
Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。
简单的说,反射机制就是在程序的运行过程中被允许对程序本身进行操作,比如自我检查,进行装载,还可以获取类本身,类的所有成员变量和方法,类的对象,还可以在运行过程中动态的创建类的实例,通过实例来调用类的方法,这就是反射机制一个比较重要的功能了。
二、反射机制的实现
利用反射机制可以获取类对象,有以下三种方法:
1.类名.class()
2.对象名.getClass()
3.Class.forName(具体的类名)
在类加载的三个阶段里都可以获取类对象,其中第三种方法,在源码中获取类对象是最常用的,也是反射机制在框架中的应用,在框架中的应用可以是通过配置文件写入所创建的类名,再利用第三种方法获取类对象。(可以用到上个笔记所写的properties文件解析工具)
给出一个接口,两个实现类,以及反射机制实现类和一个测试类。
package stu.crayue.about_reflect.core;
/**
* @author Crayue
* @version 2019年11月16日 下午6:05:08
*/
public interface IDoSomething {
void doSomething(String message);
}
package stu.crayue.about_reflect.core;
/**
* @author Crayue
* @version 2019年11月16日 下午6:06:57
*/
public class Wangdadong implements IDoSomething {
public Wangdadong() {
}
@Override
public void doSomething(String message) {
System.out.println("龙纹鏊\n"+message);
}
}
package stu.crayue.about_reflect.core;
/**
* @author Crayue
* @version 2019年11月16日 下午6:09:18
*/
public class Yasewang implements IDoSomething {
public Yasewang() {
}
@Override
public void doSomething(String message) {
System.out.println("石中剑\n"+message);
}
}
package stu.crayue.about_reflect.core;
import stu.crayue.util.PropertiesParser;
/**
* @author Crayue
* @version 2019年11月16日 下午6:10:15
*/
public class Zhongjiyiban {
private IDoSomething something;
static {
PropertiesParser.loadProperties("/class.properties");
}
public Zhongjiyiban() {
}
public void dodo() {
String className=PropertiesParser.value("class");
try {
Class<?> klass = Class.forName(className);
Object obj=klass.newInstance();
if(!(obj instanceof IDoSomething)) {
return ;
}
something=(IDoSomething) obj;
something.doSomething("谁能阻止少年武士去赴死,他们听不到");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
package stu.crayue.about_reflect.test;
import stu.crayue.about_reflect.core.Zhongjiyiban;
/**
* @author Crayue
* @version 2019年11月16日 下午6:50:14
*/
public class Test {
public static void main(String[] args) {
Zhongjiyiban src = new Zhongjiyiban();
src.dodo();
}
}
如果此时class.properties文件中的内容是
class=stu.crayue.about_reflect.core.Wangdadong
输出结果为:
如果此时class.properties文件中的内容是
class=stu.crayue.about_reflect.core.Yasewang
输出结果为:
通过上述例子,我们可以看出 只需要改变配置文件中的类名称,不用改变代码就可以获得不同的输出结果。
三、通过反射获取类的构造方法、方法以及成员变量
给出一个实体类Student
package stu.crayue.about_reflect.test;
/**
* @author Crayue
* @version 2019年11月16日 下午7:50:08
*/
public class Student {
public String name;
private int birthday;
public Student() {
}
public Student(String name, int birthday) {
this.name = name;
this.birthday = birthday;
}
public Student(int birthday) {
this.birthday = birthday;
}
public Student(String name) {
super();
this.name = name;
}
public void login(String username,String password)
{
System.out.println("用户名:"+username);
System.out.println("密码:"+password);
}
public void hello() {
System.out.println("hello"+name);
}
private String HappyBirthday()
{
return "生日:"+birthday;
}
@Override
public String toString() {
return "Student [name=" + name + ", birthday=" + birthday + "]";
}
}
1.创建对象
Object obj = 类对象.newInstance();
Class cls=Class.forName("stu.crayue.about_reflect.test.Student");
System.out.println(cls);
Object obj = cls.newInstance();
System.out.println(obj);
输出结果为:
2.调用方法
Method md1 = 类对象.getMethod(“类中的公有方法名”);//无参的方法只加方法名
Object obj1 = md1.invoke(类的对象);
Method md2 = 类对象.getMethod(“有参方法方法名”,参数的类对象);
//有参的方法要获取参数的类对象,格式为:参数类型.class
Object obj2 = md2.invoke(类的对象,“参数”);
Method md3 = 类对象.getDeclaredMethod(“类中的私有方法名”);
Student stu1=new Student("柯林",20000229);
Student stu2=new Student("某幻",19970406);
Class cls=Class.forName("stu.crayue.about_reflect.test.Student");
Method md1=cls.getMethod("hello");
Object obj1 = md1.invoke(stu1);
System.out.println(obj1);
Method md2=cls.getMethod("login",String.class,String.class);
Object obj2=md2.invoke(stu2, "小杨","987220415");
System.out.println(obj2);
Method md3=cls.getDeclaredMethod("HappyBirthday");
md3.setAccessible(true);
Object obj3= md3.invoke(stu1);
System.out.println(obj3);
输出结果为:
3.访问成员变量
Field fd1 = 类对象.getField(“成员变量名”);
Field fd2= 类对象.getDeclaredField(“私有成员变量名”);
fd2.setAccessible(true);
使用之前的setAccessible(true)方法会破坏私有成员变量。
我们尽量换一种方法,先得到setBirthday方法,然后调用setBirthday方法。
Class<?> cls=Class.forName("stu.crayue.about_reflect.test.Student");
Object obj = cls.newInstance();
Field fd1=cls.getField("name");
fd1.set(obj, "Colin");//访问name这个公有变量
Field fd2=cls.getDeclaredField("birthday");
String fieldName=fd2.getName();
String methodName="set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);//得到setBirthday这个方法
Method md = cls.getDeclaredMethod(methodName, fd2.getType());
md.invoke(obj, 20000229);
System.out.println(obj);
输出结果为: