Java反射机制

Java反射机制

反射

反射:就是将类的各个组成部分封装为其他对象,是框架设计的灵魂。
我们在程序运行过程中,操作通过反射得到的对象,可以实现解耦,可以提高程序的可扩展性。

  • 成员变量 –> Field
  • 构造方法 –> Constructor
  • 成员方法 –> Method

Java代码三个阶段

源码阶段(包括字节码) –> Class类对象阶段 –> RunTime运行时阶段

源码到Class类对象这个过程就是反射

获取Class对象

一个类字节码文件在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

  1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
    • 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
  2. 类名.class:通过类名的属性class获取
    • 多用于参数的传递
  3. 对象.getClass()getClass()方法在Object类中定义着。
    • 多用于对象的获取字节码的方式

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Person {
private String name;

public void eat(String food) {
System.out.println(getName() + "正在吃" + food);
}
...
}

// 获取Class对象
Class cls1 = Class.forName("Demo15.reflect.Person");
Class cls2 = Person.class;
Class cls3 = p.getClass();

System.out.println(cls1 + "\n" + cls2 + "\n" + cls3);
// cls1=cls2=cls3: class Demo15.reflect.Person

Class对象常用方法

  • 获取成员变量们
    • Field[] getFields() :获取所有public修饰的成员变量
    • Field getField(String name) 获取指定名称的 public修饰的成员变量
    • Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
    • Field getDeclaredField(String name) 获取指定名称的成员变量
  • 获取构造方法们
    • Constructor<?>[] getConstructors()
    • Constructor<T> getConstructor(Class<?>... parameterTypes)
      • 传入构造方法对应形参的Class对象。如:int.class, String.class
    • Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
    • Constructor<?>[] getDeclaredConstructors()
  • 获取成员方法们:
    • Method[] getMethods()
    • Method getMethod(String name, Class<?>... parameterTypes)
    • Method[] getDeclaredMethods()
    • Method getDeclaredMethod(String name, Class<?>... parameterTypes)
  • 获取类上注解:
    • Annotation[] getAnnotations()
    • <A extends Annotation> A getAnnotation(Class<A> annotationClass)
  • 获取全类名:
    • String getName()

Field对象

成员变量对象

  1. 设置值:void set(Object obj, Object value)
  2. 获取值: get(Object obj)
  3. 忽略访问权限修饰符的安全检查:
    • setAccessible(true):暴力反射, 设置后访问权限修饰符不起作用

例子

1
2
3
4
5
6
Field[] fields = cls1.getDeclaredFields();
fields[0].setAccessible(true); // 忽略访问权限修饰符的安全检查
System.out.println(fields[0].getName());

fields[0].set(p, "tt");
System.out.println(p.getName());

Constructor对象

构造方法对象

  • T newInstance(Object... initargs): 创建一个对象

例子

1
2
3
4
5
6
7
8
9
Constructor constructor = cls1.getConstructor(String.class);
System.out.println(constructor);

Object p2 = constructor.newInstance("tan2"); // 有参
System.out.println((Person)p2);

Object p3 = cls1.getDeclaredConstructor().newInstance();// 空参
((Person)p3).setName("tan3");
System.out.println((Person)p3);

Method对象

成员方法对象

  • 执行方法:Object invoke(Object obj, Object... args)
  • 获取方法名称:String getName():获取方法名
1
2
3
4
5
6
7
8
9
Method[] methods = cls1.getMethods();
for (Method method : methods) {
if (method.getName().equals("eat")) {
method.invoke(p, "棒棒糖"); // tt正在吃棒棒糖
method.invoke(p2, "饭"); // tan2正在吃棒棒糖
} else {
System.out.println(method.getName());
}
}

案例

写一个”框架”,实现在不改变任何代码的前提下,通过改变配置文件,来创建任意类的对象,并且执行其中任意方法。

配置文件

1
2
3
4
5
# src/conf.properties
ClassName=Demo15.reflect.Person
PersonName=tan
MethodName=eat
EatFood=banana

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Properties prop = new Properties();

// 获取配置文件中的内容
ClassLoader classLoader = this.getClass().getClassLoader(); // 类加载器
InputStream in = classLoader.getResourceAsStream("conf.properties");
prop.load(in);

String ClassName = prop.getProperty("ClassName");
String PersonName = prop.getProperty("PersonName");
String MethodName = prop.getProperty("MethodName");
String EatFood = prop.getProperty("EatFood");

// 获取类对象
Class cls = Class.forName(ClassName);

// 创建对象
Object p = cls.getConstructor(String.class).newInstance(PersonName);
Method method = cls.getMethod(MethodName, String.class);
method.invoke(p, EatFood); // tan正在吃banana

 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×