1. 反射机制允许程序在执行期借助于 Reflection API 取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性以及方法。反射在设计模式和框架底层都会用到。
  2. 加载完类之后,在堆中就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象包含了类的完整结构信息。可以通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射。

在反射中,可以把方法视为对象

Java 程序在计算机执行有三个阶段

img

1. 反射相关的主要类

  1. java.lang.Class 代表一个类,Class 对象表示某个类加载后在堆中的对象
  2. java.lang.reflect.Method 代表类的方法,Method 对象表示某个类的方法
  3. java.lang.reflect.Field 代表类的成员变量,Field 对象表示每个类的成员变量
  4. java.lang.reflect.Constructor 代表类的构造方法,Constructor 对象表示构造器

这些类在 java.lang.reflection

2. Class 类

2.1 基本介绍

  1. Class 也是类,因此也继承 Object 类
  2. Class 类对象不是 new 出来的,而是系统创建的
  3. 对于某个类的 Class 类对象,在内存中只有一份,因为类只加载一次
  4. 每个类的实例都会记得自己是由哪个 Class 实例所生成
  5. 通过 Class 可以完整地得到一个类的完整结构,通过一系列API
  6. Class 对象是存放在堆中的
  7. 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括 方法代码,变量名,方法名,访问权限等)

2.2 Class 类的常用方法

如下表:

方法名 功能说明
static Class forName(String name) 返回指定类名 name 的 Class 对象
Object newInstance() 调用缺省构造函数,返回该 Class 对象的一个实例
getName() 返回此 Class 对象所表示的实体(类、接口、数组类、基本类型等)名称
Class getSuperClass() 返回当前 Class 对象的父类的 Class 对象
Class[] getInterfaces() 返回当前 Class 对象的接口
ClassLoader getClassLoader() 返回该类的类加载器
Class getSuperclass() 返回表示 此 Class 所表示的实体 的超类的 Class
Constructor[] getConstructors() 返回一个包含某些 Constructor 对象的数组
Field[] getDeclaredFields() 返回 Field 对象的一个数组
Method getMethod(String name, Class … paramTypes) 返回一个 Method 对象,此对象的形参类型为 paramType

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public static void main(String[] args) throws Exception {
String classPath = "com.pushihao.Reflection_.Car";

//任何对象都可以通过getName()获取名字
//1. 获取car类对应的Class对象
Class<?> cls = Class.forName(classPath);

//2. 输出cls
System.out.println("=================================");
System.out.println(cls); // 显示cls对象是哪个类的Class对象
System.out.println(cls.getClass()); // 输出cls本身的运行类型

//3. 得到包名
System.out.println("=================================");
System.out.println(cls.getPackage().getName());

//4. 得到全类名
System.out.println("=================================");
System.out.println(cls.getName());

//5. 通过Class对象创建对象实例
System.out.println("=================================");
Car car = (Car)cls.getConstructor().newInstance();
System.out.println(car);

//6. 通过反射获取属性 name
System.out.println("=================================");
Field name = cls.getField("name");
System.out.println(name.get(car));

//7. 通过反射给属性赋值
System.out.println("=================================");
name.set(car, "奔驰");
System.out.println(name.get(car));

//8. 获取类中所有的属性
System.out.println("=================================");
Field[] fields = cls.getFields();
for (Field f: fields) {
System.out.println(f.getName());
}
}

2.3 获取 Class 类对象的常用方法

  1. 前提:已知一个类的全类名,且该类在类路径下,可通过 Class 类的静态方法 forName() 获取,可能抛出 ClassNotFoundException,示例:Class cls1 = Class.forName(“com.pushihao.Cat”);

应用场景:多用于配置文件,读取类全路径,加载类

  1. 前提:若已知具体的类,通过类的 class 获取,该方式最为安全可靠,程序性能最高,示例:Class cls2 = Cat.class;

应用场景:多用于参数传递,比如通过反射得到对应构造器对象

  1. 前提:若已知某个类的实例,调用该实例的 getClass() 方法获取 Class 对象,实例:Class cls3 = 对象.getClass();

应用场景:通过创建好的对象,获取 Class 对象

  1. 其他方式,示例:
1
2
ClassLoader cl = 对象.getClass().getClassLoader();
Class cls4 = cl.loadClass("com.pushihao.Cat");
  1. 基本数据(int, char, boolean, float, double, byte, long, short)可通过.class得到 Class 类对象:Class cls5 = 基本数据类型.class;
  2. 基本数据类型对应的包装类,可以通过.TYPE得到 Class 类对象:Class cls6 = 包装类.TYPE;

2.4 类加载

基本说明:

反射机制是 java 实现动态语言的关键,也就是通过反射实现类动态加载。

  1. 静态加载:编译时就加载相关的类,如果没有此类就报错(不管用到没用到),依赖性太强
  2. 动态加载:运行时才加载需要的类,如果运行时没有用到该类,即使不存在也不报错,降低了依赖性

类加载时机:

前三个都是静态加载,只有4是动态加载

  1. 当创建对象时(new)
  2. 当子类被加载时
  3. 调用类中的静态成员时
  4. 通过反射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//假设没有Dog类,也没有Person类
public class Demo {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
switch (n) {
case 1:
Dog dog = new Dog();//静态加载,虽然不一定用到,但是会报错
break;
case 2:
Class.forName("Person");//动态加载,如果n!=2就不报错,因为它不会加载
break;
case 3:
System.out.println("Do nothing!");
break;
}
}
}

类加载阶段:

加载 -> 准备(验证 -> 准备 -> 解析)-> 初始化

3. 通过反射获取类的结构信息

3.1 java.lang.Class 类

  1. getName 获取全类名
  2. getSimpleName 获取简单类名
  3. getFields 获取所有public修饰的属性,包含本类以及父类的
  4. getDeclaredFields 获取本类中所有属性
  5. getMethods 获取所有public修饰的方法,包含本类以及父类的
  6. getDeclaredMethods 获取本类中所有方法
  7. getConstructors 获取所有public修饰的构造器,只包含本类
  8. getDeclaredConstructors 获取本类中所有构造器
  9. getPackage 以Package形式放回包信息
  10. getSuperClass 以Class形式返回父类信息
  11. getInterfaces 以Class[]形式返回接口信息
  12. getAnnotations 以Annotation[]形式返回所有注解信息

3.2 java.lang.reflect.Field 类

  1. getModifiers 以int形式返回修饰符

    说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16。多个修饰符时就是相加的结果

  2. getType 以Class形式返回类型

  3. getName 返回属性名

3.3 java.lang.reflect.Method 类

  1. getModifiers 以int形式返回修饰符

    说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16。多个修饰符时就是相加的结果

  2. getReturnType 以Class形式获取返回类型

  3. getName 返回方法名

  4. getParameterTypes 以Class[]形式返回参数类型的数组

3.4 java.lang.reflect.Constructor 类

  1. getModifiers 以int形式返回修饰符
  2. getName 返回构造器名(全类名)
  3. getParameterTypes 以Class[]形式返回参数类型数组

4. 反射暴破

假设有这么一个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.pushihao.Reflection_.Explode;

@SuppressWarnings({"all"})
public class Person {
public String name = "Pillage";
private Integer age = 19;
private static Double salary = 100.0d;

public Person() { }
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
private Person(String name, Integer age, Double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}

public void Hi() {
System.out.println(name + ":hi~~");
}

private String UpSalary(Double salary) {
return "salary up to:" + salary;
}

private static String staticUpSalary(String name, Double salary) {
return name + "'s salary up to:" + salary;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}

4.1 通过反射创建对象

  1. 方式一:调用类中的 public 修饰的无参构造器 class 对象
  2. 方式二:调用类中的指定构造器
  3. Class 类相关方法
    • newInstance:调用类中的无参构造器,获取对应类的对象
    • getConstructor:根据参数列表,获取对应的构造器对象
    • getDecalaredConstructor:根据参数列表,获取对应的构造器对象
  4. Constructor 类相关方法
    • setAccessible:爆破(暴力破解:反射可以使用类中的私有构造器/方法)
    • newInstance:调用构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.pushihao.Reflection_.Explode.Person");

//调用 public 无参构造器
Object o1 = cls.newInstance();
System.out.println(o1);

//调用 public 有参构造器
Constructor<?> constructor1 = cls.getConstructor(String.class, Integer.class);
Object o2 = constructor1.newInstance("Daisy", 18);
System.out.println(o2);

//调用非 public 有参构造器
Constructor<?> constructor2 = cls.getDeclaredConstructor(String.class, Integer.class, Double.class);
constructor2.setAccessible(true);//破解私有构造器
Object o3 = constructor2.newInstance("Jane", 18, 10000d);
System.out.println(o3);
}

4.2 通过反射访问类中的属性

  1. 根据属性名获取 Field 对象
1
Field f = class对象.getDeclaredField(属性名);
  1. 暴破
1
f.setAccessible(true);
  1. 访问
1
2
f.set(对象实例, 新值);
f.get(对象实例);

如果是静态属性,则 set 和 get 中的参数可以写成 null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.pushihao.Reflection_.Explode.Person");
Person person = (Person) cls.getConstructor().newInstance();

//获取 public 类型的 name 属性
Field name = cls.getField("name");
System.out.println(name.get(person));
name.set(person, "Daisy");
System.out.println(person);

//获取非 public 类型的 age 属性
Field age = cls.getDeclaredField("age");
age.setAccessible(true);//暴破
System.out.println(age.get(person));
age.set(person, 66);
System.out.println(person);

//获取静态的非 public 类型的 salary 属性
Field salary = cls.getDeclaredField("salary");
salary.setAccessible(true);
System.out.println(salary.get(person));
salary.set(null, 90d);
System.out.println(salary.get(null));
}

4.3 通过反射访问类中的方法

  1. 根据方法名和参数列表获取 Method 方法对象
1
2
Method method = class对象.getMethod(方法名, class对象);
Method method = class对象.getDeclaredMethod(方法名, class对象);
  1. 获取对象实例
1
Object o = class对象.getConstructor().newInstance();
  1. 暴破
1
method.setAccessible(true);
  1. 访问
1
Object returnValue = method.invoke(o, 实参列表);

如果是静态方法,则 invoke 的参数 o 可以写成 null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.pushihao.Reflection_.Explode.Person");
Object o = cls.getConstructor().newInstance();

//调用 public 的 hi 方法
Method hi = cls.getMethod("Hi");
hi.invoke(o);

//调用非 public 的 UpSalary 方法
Method upSalary = cls.getDeclaredMethod("UpSalary", Double.class);
upSalary.setAccessible(true);
Object sal = upSalary.invoke(o, 100.0d);
System.out.println(sal);

//调用静态的非 public 的 staticUpSalary 方法
Method staticUpSalary = cls.getDeclaredMethod("staticUpSalary", String.class, Double.class);
staticUpSalary.setAccessible(true);
System.out.println(staticUpSalary.invoke(null, "Daisy", 666d));
}