在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制;
为了 方便理解,我们画个图看看:
这个基本就是反射的基本实现过程。
看图大概可以知道:反射的实现其实是在运行的时候,此时类加载器已经将class文件加载到jvm虚拟机里面去了;
(1)通过.class
// 第一种 获取到类对象
Class class1 = Person.class;
(2)通过创建实例对象来获取类对象
Person person =new Person();
Class class2 = person.getClass();
(3) 通过包名,调用class的forName方法
Class class3 = Class.forName("day07.Person");
其中第3种方式是用的最多,只知道包名类名,其他啥也拿不到那种;个人认为这才是反射存在的意义。
在这之前我们先列出一个测试用的类;
public class Person {
private String name;
private int age;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public void eat() {
this.sex = "女";
System.out.print("hahha");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
}
这个类很简单,不多说;
我们看前面的反射过程图可以知道,我们在实现反射的过程是先获取到class文件的class对象的,所以我们一步一步来;
(1)利用第三种方式获取class对象
// 利用第三种方式获取class对象
Class> dataClass = Class.forName("day07.Person");
class java.lang.String--name
int--age
class java.lang.String--sex
(2)通过class对象调用newInstance创建由class类
对象表示的类的新实例,简单说,这个Object就是Person类,object就是Person的一个实例;
Object object = dataClass.newInstance();
(3)通过Class类的getDeclaredFields()方法,可以获取到要反射的类或接口声明的所有字段 类
对象。
Field[] fields = dataClass.getDeclaredFields();
for(Field field:fields) {
System.out.println(field.getType()+"--"+field.getName());
}
结果(打印出了Person类的所有变量属性):
class java.lang.String--name
int--age
class java.lang.String--sex
(4)既然能拿到所有,肯定也能拿到单个,并且操作改变它的数值;我们继续看,拿单个属性比较简单,直接根据要拿的属性名就可以拿到了,下面我们拿一下Person类的name属性;
Field field = dataClass.getDeclaredField("name");
然后我们尝试给它赋个值;
Field field = dataClass.getDeclaredField("name");
// 注意这段代码, 这段代码的意思就是假如这个属性是私有的,不能拿,那我们就强行把它变成可以拿;
if(!field.isAccessible()) {
field.setAccessible(true);
}
Person object =(Person) dataClass.newInstance();
field.set(object, "大佬");
设置完,我们再取出来看看,有没有赋值成功;
Object obeObject = field.get(object);
System.out.println(field.getName()+"="+obeObject);
结果:
okok,赋值成功,说明我们通过反射操作某个类的属性是没有问题的;
贴个完整代码:
public class RefectFieldTest {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException,
IllegalAccessException, NoSuchFieldException, SecurityException {
Class> dataClass = Class.forName("day07.Person");
Field[] fields = dataClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getType() + "--" + field.getName());
}
Field field = dataClass.getDeclaredField("name");
// 注意这段代码, 这段代码的意思就是假如这个属性是私有的,不能拿,那我们就强行把它变成可以拿;
if (!field.isAccessible()) {
field.setAccessible(true);
}
Person object = (Person) dataClass.newInstance();
field.set(object, "大佬");
Object obeObject = field.get(object);
System.out.println(field.getName() + "=" + obeObject);
}
}
(1)利用第三种方式获取class对象
// 利用第三种方式获取class对象
Class> dataClass = Class.forName("day07.Person");
class java.lang.String--name
int--age
class java.lang.String--sex
(2)通过class对象调用newInstance创建由class类
对象表示的类的新实例,简单说,这个Object就是Person类,object就是Person的一个实例;
Object object = dataClass.newInstance();
(3)通过class的getDeclaredMethods()方法获取所有已经声明的公有或者私有的方法
Method[] methods = dataClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName() + "-" + method.getGenericReturnType().getTypeName());
}
结果:
toString-java.lang.String
getName-java.lang.String
setName-void
setAge-void
setSex-void
eat-void
getSex-java.lang.String
getAge-int
(4)同样的道理,我们也可以单独获取某个指定的方法,并且操作它
//通过setAge方法给age属性赋值
Object obj = dataClass.newInstance();
Method method2 = dataClass.getDeclaredMethod("setAge",int.class);
method2.invoke(obj,27);
(5)设置完,我们可以看下,是否调用成功,我们调用一个getAge()方法就知道了;
// 通过get()方法,获取赋值后的值
Method age = dataClass.getDeclaredMethod("getAge");
Object ageObject= age.invoke(obj);
System.out.println("修改后的age值=" + ageObject);
结果:
修改后的age值=27
总结:反射的目的是为了操作正常情况下,不方便操作或者获取的属性,方法;在很多java开发框架里面会用到;