反射:允许对成员变量,成员方法和构造方法的信息进行编程访问。
所以,想要获取 成员变量,成员方法和构造方法,得首先获取 class对象。
1.获取class对象
① Class.forname("全类名")
② 类名.class
③ 对象.getClass()
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
//1.第一种方式(全类名:包名 + 类名)
Class clazz1 = Class.forName("com.mihoyo.classDemo.Student");
System.out.println(clazz1);//class com.mihoyo.classDemo.Student
//2.第二种方式
Class clazz2 = Student.class;
System.out.println(clazz1 == clazz2);//同一个对象 --> true
//3.第三种方式
Student s = new Student();
Class clazz3 = s.getClass();
System.out.println(clazz1 == clazz3);//true
System.out.println(clazz2 == clazz3);//true
}
}
细节:
第一种方式最为常用;
第二种方式一般更多是当作参数使用;
第三种方式较为局限,必须已经有了这个类的对象,才能使用。
2.获取构造方法(Constructor对象)
(1)Class类中用于获取构造方法的方法
Ⅰ. 返回多个构造方法对象
注意:
getConstructors只能获取被 public 修饰的构造方法,否则报错;
getDeclaredConstructors可以获取所有的构造方法。
Ⅱ. 返回单个构造方法对象
注意:
① getConstructor 只能获取被 public 修饰的单个构造方法,否则报错;
getDeclaredConstructor 可以获取所有的单个构造方法。
② 这两个方法的参数必须与对应的构造方法保持一致。
(2)Constructor类中用于创建对象的方法
注意:
① 权限修饰符对应的常量值如下:
② 由于该构造方法是 private 修饰,外界访问不到,所以不能直接创建对象,也就会抛出异常。
可以使用暴力反射,来强行创建其对象。
3.获取成员变量(Field对象)
(1)Class类中用于获取成员变量的方法
Ⅰ. 返回多个成员变量对象
Ⅱ. 返回单个成员变量对象
注意:方法中的实参为:成员变量名
(2)Field类中用于创建对象的方法
public class Demo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
//1.获取class字节码文件对象
Class clazz = Class.forName("com.mihoyo.field.Student");
//2.返回单个成员变量对象
Field field = clazz.getDeclaredField("name");
//获取成员变量的权限修饰符
int modifiers = field.getModifiers();
System.out.println(modifiers);
//获取成员变量的名字
String name = field.getName();
System.out.println(name);
//获取成员变量的数据类型
Class<?> type = field.getType();
System.out.println(type);
//获取成员变量记录的值
Student s = new Student("zhangsan", 23, "男");
//暴力反射
field.setAccessible(true);
//get方法的参数:成员变量所属的对象
String value = (String) field.get(s);
System.out.println(value);
//修改成员变量记录的值(参数:对象名,修改后的值)
field.set(s, "lisi");
System.out.println(s);
}
}
运行结果:
4.获取成员方法(Method对象)
(1)Class类中用于获取成员变量的方法
Ⅰ. 获取多个成员方法对象
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
//1.获取class字节码文件对象
Class clazz = Class.forName("com.mihoyo.method.Student");
//2.获取所有的public成员方法对象
Method[] methods1 = clazz.getMethods();
for (Method method : methods1) {
System.out.println(method);
}
System.out.println("----------------------");
//获取所有的成员方法对象
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
}
}
运行结果:
注意:
① getMethods 获取的 public 成员方法,包含父类中所有的 public 成员方法。
② getDeclaredMethods 获取的成员方法,不能获取父类的,只能获取本类中所有的。
Ⅱ. 获取单个成员方法对象
注意:
指定单个成员方法时,第一个参数为方法名,后面的参数为方法的形参。
(因为方法可以重载,相同的方法名,不同的形参)。
(2)Method类中用于创建对象的方法
public class Demo3 {
public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, IllegalAccessException {
//1.获取class字节码文件对象
Class clazz = Class.forName("com.mihoyo.method.Student");
//2.获取单个指定的成员方法
Method method = clazz.getDeclaredMethod("eat", String.class);
// 获取方法的修饰符
int modifiers = method.getModifiers();
System.out.println(modifiers);
// 获取方法的名字
String name = method.getName();
System.out.println(name);
// 获取方法的形参
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter);
}
//获取方法的抛出的异常
Class[] exceptionTypes = method.getExceptionTypes();
for (Class exceptionType : exceptionTypes) {
System.out.println(exceptionType);
}
//运行方法
Student s = new Student();
//暴力反射
method.setAccessible(true);
//参数一:表示方法的调用者
//参数二:表示在调用方法的时候传递的实参
String result = (String) method.invoke(s, "汉堡包");
System.out.println(result);
}
}
运行结果:
5.作用
Question1:对于任意一个对象,设计一个方法,将对象的所有字段名和值,保存到文件中去。
//把对象里面所有的成员变量名和值保存到本地文件中
public static void saveObject(Object obj) throws IllegalAccessException, IOException {
//1.获取字节码文件的对象
Class clazz = obj.getClass();
//2. 创建IO流
BufferedWriter bw = new BufferedWriter(new FileWriter("reflect\\a.txt"));
//3. 获取所有的成员变量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
//获取成员变量的名字
String name = field.getName();
//获取成员变量的值
Object value = field.get(obj);
//写出数据
bw.write(name + "=" + value);
bw.newLine();
}
bw.close();
}
Question2:利用反射,结合配置文件(.properties),动态创建对象,并调用方法
public class MyReflectDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//1.读取配置文件中的信息
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("myreflect\\prop.properties");
prop.load(fis);
fis.close();
System.out.println(prop);
//2.获取全类名和方法名
String className = (String) prop.get("classname");
String methodName = (String) prop.get("method");
System.out.println(className);
System.out.println(methodName);
//3.利用反射创建对象并运行方法
Class clazz = Class.forName(className);
//获取构造方法
Constructor con = clazz.getDeclaredConstructor();
Object o = con.newInstance();
System.out.println(o);
//获取成员方法并运行
Method method = clazz.getDeclaredMethod(methodName);
method.setAccessible(true);
method.invoke(o);
}
}
细节:
① 由于类名和方法名是通过配置文件得到的,得到的类名也是一个字符串。
所以创建对象时,需要利用多态创建一个 Object 对象。
② 通过修改配置文件中的类名和方法名,就可以动态创建不同的对象,调用不同的方法。