目录
一、前言
写框架需要用到反射,而用框架不需要。
将字节码文件中的成员变量、构造方法、成员方法通过类加载器ClassLoader封装成一个个的对象。(这一部分并不是特别清晰,先看后面的网课)
二、获取class对象的三种方式
package cn.itcast.reflect;
//获取class对象的三种方式
import cn.itcast.domain.Person;
import cn.itcast.domain.Student;
public class PersonTest {
public static void main(String[] args) throws ClassNotFoundException {
//1: Class.forName("全类名")
//全类名:包名.类名
Class c1 = Class.forName("cn.itcast.domain.Person");
//上一行需要处理异常,可以直接通过main函数抛出:ClassNotFoundException
System.out.println(c1);
//2: 类名.class
Class c2 = Person.class; //这里需要导入Person
System.out.println(c2);
//3: 对象名.getClass();
Person p = new Person();
Class c3 = p.getClass();
System.out.println(c3);
//用==来判断两个对象是否相同(因为==是判断地址)
System.out.println(c1==c2);//true
System.out.println(c1==c3);//true
Class c = Student.class;//需要导入Student
System.out.println(c==c1);//false
}
}
三、Class对象功能
代码格式化快捷键:Ctrl+Alt+L
(1)获取成员变量
Class personClass = Person.class;
Field[] Fields = personClass.getFields();
for (Field field : Fields) {
System.out.println(field);
}
遍历Person的Class对象(字节码文件对象)中的所有成员变量(需要注意的是,这里只能遍历用public修饰的成员变量)
//获取Person的Class对象,获取名称为a的成员变量,获取并修改其值
Field a = personClass.getField("a");
Person p = new Person();
Object value = a.get(p);
System.out.println(value);
//设置值
a.set(p,"张三");
System.out.println(p);
a为Person的Class对象中名称为a的成员变量对象,此时,分别获取和设置该成员变量的值,并打印:
由于a是String对象,所以其初始值为null;重写toString方法后,可以看到a的值被修改为张三。
//getDeclaredFields方法可以获取Class对象中的所有成员变量(不管是什么修饰符修饰的)
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
//获取用private修饰的成员变量d的值
Field d = personClass.getDeclaredField("d");
Object value2 = d.get(p);
System.out.println(value2);
getDeclaredFields方法可以获取所有变量(不局限于用public修饰的变量),当获取d变量值时,由于其并不是用public修饰的变量,所以会产生异常(也就是说,declared方法只能获得所有成员变量及用public修饰的变量的值)。若想要获取所有变量的值,则需要加入另一个方法:
Field d = personClass.getDeclaredField("d");
d.setAccessible(true);//暴力反射
Object value2 = d.get(p);
System.out.println(value2);
此时,我就可以获取用private修饰的d变量的值而产生异常,可见其值为null。
暴力反射:d.setAccessible(true); //让调用该方法的变量的值直接被访问(忽视访问修饰符的安全检查)
总结: 加上declared可以帮助获取非public修饰的成员变量,但是想要获取该变量的值,则还需要该变量调用setAccessible(true),从而暴力反射。
(2)获取构造方法
public static void main(String[] args) throws Exception {
Class personClass = Person.class;
//获取参数分别为String和int类型的构造方法
Constructor constructor = personClass.getConstructor(String.class, int.class);
Object person = constructor.newInstance("张三", 23);//用该构造方法创建实例对象
System.out.println(person);
/* 以下是黑马老师讲的代码,但是一运行就会显示异常,据说是已经过时?
//获取无参构造方法,但是这个方法比较麻烦,可以使用方法2
Constructor constructor1 = personClass.getConstructor();
Object person1 = constructor.newInstance();//用该构造方法创建实例对象
System.out.println(person);
//方法2
Object person2 = personClass.newInstance();
System.out.println(person2);
*/
}
第一个有参构造创建对象的方法可以使用,注释内的无参构造方法不能使用,可能是版本问题。
Object person1 = personClass.getConstructor().newInstance();
System.out.println(person1);
Object person2 = personClass.getConstructor(String.class,int.class).newInstance("李四",25);
System.out.println(person2);
这个方法既可以调用无参构造,也可以调用有参构造,而且形式较为简单,建议以后使用这个,下面是运行截图:
(3)获取成员方法
获取指定名称的方法:
public static void main(String[] args) throws Exception {
//获取Person的Class对象,获取对象的所有成员变量,遍历打印
Class personClass = Person.class;
//获取无参数的eat方法
Method method = personClass.getMethod("eat");
Person p = new Person();
method.invoke(p);
//获取有参数的构造方法
Method method1 = personClass.getMethod("eat", String.class);// (方法名,参数类型)
method1.invoke(p,"饭");// (对象,实参)
}
获取所有用public修饰的成员方法:
//获取所有用public修饰的方法
Method[] methods = personClass.getMethods();
for (Method method2 : methods) {
System.out.println(method2);
}
需要注意的是,这里的方法不仅包含Person类中显而易见的方法,还包含了Object类中的所有public修饰的方法。
(4)获取类名
//获取类名
String name = personClass.getName();
System.out.println(name);
此时获取的是全类名(包名.类名)