反射

反射的好处:在程序运行过程中操作对象
可以提高程序的扩展性,解耦

获取Class对象的时机方式:

1. .class文件未加载阶段

在未加载阶段,将字节码文件加载进内存,返回Class对象:Class.forName(“包名+类名”)

  • 多用于配置文件,将类名定义在配置文件中,读取文件,加载类
    1
    2
    3
    Class<?> clas1 = Class.forName("com.learn.reflect.Scholar");
    System.out.println(clas1);
    //class com.learn.reflect.Scholar

2. Class类对象阶段(对象已经被ClassLoader加载入内存),通过类名的属性:类名.class

  • 多用于参数的传递
    1
    2
    3
    Class<Scholar> clas2 = Scholar.class;
    System.out.println(clas2);
    //class com.learn.reflect.Scholar

3. 在程序Runtime阶段,使用Object类的方法:对象.getClass()

  • 多用于对象获取字节码的方式
    1
    2
    3
    4
    Scholar scholar1 = new Scholar();
    Class<? extends Scholar> clas3 = scholar1.getClass();
    System.out.println(clas3);
    //class com.learn.reflect.Scholar

比较三种方式

内存地址的比较

1
2
3
System.out.println(clas1 == clas2);//true
System.out.println(clas2 == clas3);//true
System.out.println(clas1 == clas3);//true

结论:不管用哪种方式获取的class对象是同一个,区别仅仅是获取的时机不同。

Class对象的操作

1.获取成员变量

  • Field[] getFields() 获取public修饰的成员变量
  • Field getField(String name) 获取指定名字的public修饰的成员变量

  • Field[] getDeclaredFields() 获取所有定义了的成员变量

  • Field getDeclaredFields(String name)

获取构造方法

  • Constructor<?> [] getConstructors()
  • Constructor getConstructor(类<?>… parameterTypes)
  • Constructor<?> [] getDeclaredConstructors()
  • Constructor getDeclaredConstructor(类<?>… parameterTypes)

获取成员方法

  • Method[] getMethods()
  • Method getMethod(String name, 类<?>… parameterTypes)

  • Method[] getDeclaredMethods()

  • Method getDeclaredMethod(String name, 类<?>… parameterTypes)

获取类名

  • String getName()

获取后的操作set() get()方法

1
2
3
4
5
6
7
Scholar scholar = new Scholar();
Field fields = clas1.getDeclaredField("name");
//忽略权限修饰符的检查,将私有变量设置为可访问,暴力反射
fields.setAccessible(true);
System.out.println(fields.get(scholar));//null
fields.set(scholar,"王麻子");
System.out.println(fields.get(scholar));

类<?>… parameterTypes的含义

1
2
3
4
5
Class<Scholar> clas2 = Scholar.class;

Constructor<Scholar> constructor = clas2.getConstructor(int.class, String.class);
Scholar obj = constructor.newInstance(40, "老妈子");
System.out.println(obj);

getMehod如何执行

如果使用getMethods 返回的方法包含了Object的方法,因为所有类都是Object的子类
直接引用上一个代码块的Scholar对象obj

1
2
3
Method setAge = clas2.getMethod("setAge",int.class);
setAge.invoke(obj,50);
System.out.println(obj);

配置文件作为中间数据,加载执行其他类对象、方法

1
2
3
4
5
6
7
8
9
10
11
12
13
Properties properties = new Properties();
//获取、加载配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream in = classLoader.getResourceAsStream("config.propertites");
properties.load(in);
//获取配置文件中定义的数据
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
//将目标类加载进内存
Class clas = Class.forName(className);
Object obj = clas.newInstance();
Method method = clas.getMethod(methodName);
method.invoke(obj);