Java反射
Java高级是面向高级语法部分,主要是服务于一些高级的操作,例如文件、异常、注解、数据结构、多任务、网络通信和数据库等。
Java的反射(reflection)机制是指在程序运行状态中,可以获取和调用任意类的属性和方法。这种动态获取类的信息和动态调用的方法的功能称为Java语言的反射机制。
# 类加载器
# 类加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将class文件读入内存,并为之创建一个Class对象。
连接
- 验证:是否有正确的内部结构,并和其他类协调一致
- 准备:负责为类的静态成员分配内存,并设置默认初始化值
- 解析:将类的二进制数据中的符号引用替换为直接引用
初始化
# 类初始化
- 创建类的实例
- 类的静态变量,或者为静态变量赋值
- 类的静态方法
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
# 类加载器
负责将.class文件加载到内存中,并为之生成对应的class对象。
# 类加载器的组成
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载。
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录
System ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
# 反射
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意方法和属性。这种动态获取类的信息以及动态调用对象的方法的功能称为Java的反射机制。
想要解剖一个类,就必须先要获取该类的字节码文件对象。而解剖的关键就是使用Class类中的方法。因此先要对Class类进行一定的了解。
# Class类型
阅读API的Class类得知,Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
获取Class对象的三种方式:
通过Object类的
getClass()
方法Person p = new Person(); Class c = p.getClass();
1
2通过类名.class获取字节码文件对象(任何数据类型都具备一个class静态属性)
Class c2 = Person.class;
1通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName)
Class c3 = Class.forName("Person");
1
注意:第三种和前两种的区别。前两种你必须明确Person类型,后面是指定这种类型的字符串就行,这种扩展更强。我不需要知道你的类,我只提供字符串,按照配置文件加载就可以了。
Person类
public class Person05 {
// 成员变量
public String name;
public int age;
private String address;
// 构造方法
public Person05() {
System.out.println("空参数构造方法");
}
public Person05(String name) {
this.name = name;
System.out.println("带有String的构造方法");
}
private Person05(String name, int age) {
this.name = name;
this.age = age;
System.out.println("带有String,int的构造方法");
}
public Person05(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
System.out.println("带有String, int, String的构造方法");
}
//成员方法
//没有返回值没有参数的方法
public void method1(){
System.out.println("没有返回值没有参数的方法");
}
//没有返回值,有参数的方法
public void method2(String name){
System.out.println("没有返回值,有参数的方法 name= "+ name);
}
//有返回值,没有参数
public int method3(){
System.out.println("有返回值,没有参数的方法");
return 123;
}
//有返回值,有参数的方法
public String method4(String name){
System.out.println("有返回值,有参数的方法");
return "哈哈" + name;
}
//私有方法
private void method5(){
System.out.println("私有方法");
}
@Override
public String toString() {
return "Person05 [name=" + name + ", age=" + age + ", address=" + address+ "]";
}
}
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
Class类获取演示
/*
* 获取.class字节码文件对象的方式
* 1:通过Object类中的getObject()方法
* 2: 通过 类名.class 获取到字节码文件对象
* 3: 反射中的方法,
* public static Class<?> forName(String className) throws ClassNotFoundException
* 返回与带有给定字符串名的类或接口相关联的 Class 对象
*/
public class ReflectDemo01 {
public static void main(String[] args) throws ClassNotFoundException {
// 1. 通过Object类中的getClass()方法
Person05 p5 = new Person05();
Class<? extends Person05> c1 = p5.getClass();
System.out.println("c1 = " + c1);
// 2. 通过类名.class 获取字节码对象
Class<Person05> c2 = Person05.class;
System.out.println("c2 = " + c2);
//3. 反射中的方法
Class<?> c3 = Class.forName("Person05");
System.out.println("c3 = " + c3);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 通过反射获取构造方法并使用
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可以通过Class类提供的方法获取构造方法。
返回一个构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes) //获取public修饰, 指定参数类型所对应的构造方法 public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //获取指定参数类型所对应的构造方法(包含私有的)
1
2返回多个构造方法
public Constructor<?>[] getConstructors() //获取所有的public 修饰的构造方法 public Constructor<?>[] getDeclaredConstructors() //获取所有的构造方法(包含私有的)
1
2
import java.lang.reflect.Constructor;
/**
* 反射演示:通过反射获取构造方法并使用
*/
public class ReflectDemo02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
// 获取Class对象
Class<?> c = Class.forName("Person05");
// 获取所有的构造方法
Constructor<?>[] cons = c.getDeclaredConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
System.out.println("-------------------------");
// 获取一个构造方法
// 空参构造 public Person()
Constructor<?> con1 = c.getConstructor(null);
System.out.println(con1);
// 单个参数构造 public Person(String name)
Constructor<?> con2 = c.getConstructor(String.class);
System.out.println(con2);
// 私有多个参数构造 private Person(String name, int age)
Constructor<?> con3 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(con3);
// 公共多个参数构造 public Person(String name, int age, String address)
Constructor<?> con4 = c.getDeclaredConstructor(String.class, int.class, String.class);
System.out.println(con4);
}
}
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
# 获取公共构造方法和创建对象
获取构造方法和创建对象,步骤如下:
获取到Class对象
获取指定的构造方法
通过构造方法Constructor中的方法,创建对象
public T newInstance(Object... initargs)
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 反射演示:通过反射获取公共构造方法并使用
*/
public class ReflectDemo03 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 1.获取Class对象
Class<?> c = Class.forName("Person05");
// 2. 获取指定的构造方法
// 空参构造 public Person5()
Constructor<?> con = c.getConstructor(null);
// 公共多个参数构造 public Person5(String name, int age, String address)
Constructor<?> con1 = c.getConstructor(String.class, int.class, String.class);
// 3. 通过构造方法类中newInstance的方法,创建对象
Object obj = con.newInstance(null); // 空参数构造
Object obj1 = con1.newInstance("小米", 25, "长沙");
System.out.println(obj);
System.out.println(obj1);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 获取私有构造方法和创建对象
AccessibleObject
类是Field、Method和Constructor对象的父类。它提供了将反射的对象标记为在使用时取消默认Java语言访问控制检查的能力。
对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用Field、Method或Constructor对象来设置或者获取字段、调用方法、或者创建和初始化类的新实例的时候,会执行访问检查。常用的方法如下:
public void setAccessible(boolean flag) throws SecurityException
- 参数值为true则表示反射的对象在使用时应该取消Java语言访问检查。
- 参数为false则表示反射的对象要实施Java语言访问检查。
获取私有构造方法和创建对象,步骤如下:
- 获取到Class对象
- 获取指定的构造方法
- 暴力访问, 通过setAccessible(boolean flag)方法
- 通过构造方法类Constructor中的方法,创建对象
public T newInstance(Object... initargs)
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 反射演示:通过反射获取私有构造方法并使用
*/
public class ReflectDemo04 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 1.获取Class对象
Class<?> c = Class.forName("Person05");
// 2. 获取指定的构造方法
// 空参构造 private Person5(String name, int age)
Constructor<?> con = c.getDeclaredConstructor(String.class, int.class);
//3. 暴力反射
con.setAccessible(true); // 取消Java语言访问检查
// 4. 通过构造方法类中newInstance的方法,创建对象
Object obj = con.newInstance("小米", 24); // 空参数构造
System.out.println(obj);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 通过反射获取成员变量并使用
在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量。
返回一个成员变量
public Field getField(String name) //获取指定的 public修饰的变量 public Field getDeclaredField(String name) //获取指定的任意变量
1
2返回多个成员变量
public Field[] getFields() //获取所有public 修饰的变量 public Field[] getDeclaredFields() //获取所有的 变量 (包含私有)
1
2
import java.lang.reflect.Field;
/**
* 反射演示:通过反射获取成员变量
*/
public class ReflectDemo05 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
// 获取Class对象
Class<?> c = Class.forName("Person05");
// 获取成员变量
// 多个公共变量
Field[] fields = c.getFields();
for(Field field: fields) {
System.out.println(field);
}
System.out.println("--------------------------");
// 单个公共变量
Field ageField = c.getField("age");
System.out.println(ageField);
// 多个私有变量
Field[] declaredFields = c.getDeclaredFields();
for(Field field : declaredFields) {
System.out.println(field);
}
// 单个私有变量
Field addressField = c.getDeclaredField("address");
System.out.println(addressField);
}
}
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
# 获取成员变量和进行赋值和取值操作
获取成员变量,步骤如下:
获取Class对象
获取构造方法
通过构造方法,创建对象
获取指定的成员变量(私有成员变量,通过setAccessible(boolean flag)方法暴力访问)
通过方法,给指定对象的指定成员变量赋值或者获取值
public void set(Object obj, Object value)
在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值
public Object get(Object obj)
返回指定对象obj中,此 Field 对象表示的成员变量的值
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
* 反射演示:通过反射获取成员变量并使用
*/
public class ReflectDemo06 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
// 1.获取Class对象
Class<?> c = Class.forName("Person05");
// 2.获取构造方法
//public Person(String name)
Constructor con = c.getConstructor(String.class);
//3,通过构造方法,创建对象
Object obj = con.newInstance("小明");
//4,获取指定的成员变量
//public String name;
Field nameField = c.getField("name");
//public int age;
Field ageField = c.getField("age");
//private String address;
Field addressField = c.getDeclaredField("address");
addressField.setAccessible(true); //取消 Java 语言访问检查
//5. 通过方法,给指定对象的指定成员变量赋值或者获取值
System.out.println("name = " + nameField.get(obj));
System.out.println("age = " + ageField.get(obj));
System.out.println("address = " + addressField.get(obj));
// 赋值
ageField.set(obj, 23);
addressField.set(obj, "凯莉广场");
System.out.println("------------------------");
System.out.println("name = "+ nameField.get(obj));
System.out.println("age = "+ ageField.get(obj));
System.out.println("address = "+ addressField.get(obj));
}
}
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
# 通过反射获取成员方法并使用
在反射机制中,把类中的成员方法使用类Method表示。可以通过Class类中提供的方法获取成员方法。
返回一个方法
public Method getMethod(String name, Class<?>... parameterTypes) //获取public 修饰的方法 public Method getDeclaredMethod(String name, Class<?>... parameterTypes) //获取任意的方法,包含私有的 // 参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型
1
2
3返回多个方法
public Method[] getMethods() //获取本类与父类中所有public 修饰的方法 public Method[] getDeclaredMethods() //获取本类中所有的方法(包含私有的)
1
2
import java.lang.reflect.Method;
/**
* 反射演示:通过反射获取成员方法
*/
public class ReflectDemo07 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//获取Class对象
Class c = Class.forName("Person05");
// 获取多个公共方法
Method[] methods = c.getMethods();
for(Method method : methods) {
System.out.println(method);
}
System.out.println("---------------");
// 获取单个公共方法
// public void method1() 没有返回值没有参数的方法
Method method1 = c.getMethod("method1", null);
// public void method2(String name) 没有返回值,有参数的方法
Method method2 = c.getMethod("method2", String.class);
// public String method3() 有返回值,没有参数
Method method3 = c.getMethod("method3", null);
// public String method4(String name) 有返回值,有参数
Method method4 = c.getMethod("method4", String.class);
System.out.println(method1);
System.out.println(method2);
System.out.println(method3);
System.out.println(method4);
System.out.println("-----------------");
// 获取多个私有方法
Method[] declaredMethods = c.getDeclaredMethods();
for(Method method : declaredMethods){
System.out.println(method);
}
// 获取单个私有方法
Method method5 = c.getDeclaredMethod("method5", null);
System.out.println(method5);
}
}
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
43
44
45
46
# 获取公共成员方法和调用
获取公共成员方法和调用,步骤如下:
- 获取Class对象
- 获取构造方法
- 通过构造方法,创建对象
- 获取指定的方法
- 执行找到的方法
public Object invoke(Object obj, Object... args)
执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 反射演示:通过反射获取公共成员方法并调用
*/
public class ReflectDemo08 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 1. 获取Class对象
Class<?> c = Class.forName("Person05");
//2,获取构造方法
//public Person(String name, int age, String address){
Constructor con = c.getConstructor(String.class, int.class, String.class);
//3,通过构造方法,创建对象
Object obj = con.newInstance("小明", 23, "哈尔滨");
// 4.获取指定方法
// public void method1() 没有返回值没有参数的方法
Method m1 = c.getMethod("method1", null);
//public String method4(String name)
Method m4 = c.getMethod("method4", String.class);
// 5. 执行方法
m1.invoke(obj, null);
Object result = m4.invoke(obj, "snake8859");
System.out.println("result = " + result);
}
}
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
# 获取私有方法和调用
获取私有成员方法和调用,步骤如下:
- 获取Class对象
- 获取构造方法
- 通过构造方法,创建对象
- 获取指定的方法
- 开启暴力访问
- 执行找到的方法
public Object invoke(Object obj, Object... args)
执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 反射演示:通过反射获取私有成员方法并调用
*/
public class ReflectDemo09 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 1. 获取Class对象
Class<?> c = Class.forName("Person05");
//2,获取构造方法
//public Person(String name, int age, String address){
Constructor con = c.getConstructor(String.class, int.class, String.class);
//3,通过构造方法,创建对象
Object obj = con.newInstance("小明", 23, "哈尔滨");
// 4.获取指定方法
//private void method5(){ 没有返回值没有参数的四月方法
Method m5 = c.getDeclaredMethod("method5", null);
// 5.开启暴力访问
m5.setAccessible(true);
// 6. 执行方法
m5.invoke(obj, null);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 总结
# 如何获取.Class文件对象
- 通过Object类 getClass()方法获取 Class对象
- 通过类名.class 方式 获取 Class对象
- 通过反射的方式, Class.forName(String classname) 获取Class对象
public static Class<?> forName(String className)throws ClassNotFoundException
返回与带有给定字符串名的类或接口相关联的 Class 对象
# 通过反射、获取构造方法和创建对象
# 构造方法获取
- 获取指定的构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
:获取指定的public修饰的构造方法public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
:获取指定的构造方法,包含私有的 - 获取所有的构造方法
public Constructor<?>[] getConstructors()
:获取所有的public 修饰的构造方法public Constructor<?>[] getDeclaredConstructors()
: 获取所有的构造方法,包含私有的
# 创建对象
公共构造方法
- 获取字节码文件对象
- 通过字节码文件对象 ,获取到指定的构造方法 getConstructor(参数);
- 通过构造方法,创建对象 public T newInstance(Object... initargs)
私有构造方式
获取字节码文件对象
通过字节码文件对象 ,获取到指定的构造方法 getDeclaredConstructor (参数);
暴力访问
con.setAccessible(true);
通过构造方法,创建对象 public T newInstance(Object... initargs)
# 通过反射,获取成员变量和赋值取值
# 成员变量获取
- 获取指定的成员变量
public Field getField(String name) 获取public修饰的成员变量
public Field getDeclaredField(String name) 获取任意的成员变量,包含私有
- 获取所有的成员变量
public Field[] getFields() 获取所有public修饰的成员变量
public Field[] getDeclaredFields() 获取所有的成员变量,包含私有
# 赋值和取值
- 公共
- 获取字节码文件对象
- 获取构造方法,创建对象
- 获取指定的成员变量
- 对成员变量赋值\获取值操作 public void set(Object obj, Object value) 赋值 public Object get(Object obj) 获取值
- 私有
- 获取字节码文件对象
- 获取构造方法,创建对象
- 获取指定的成员变量
- 开启暴力访问
- 对成员变量赋值\获取值操作 public void set(Object obj, Object value) 赋值 public Object get(Object obj) 获取值
# 通过反射,获取成员方法和调用
# 成员方法获取
- 获取指定的方法
public Method getMethod(String name, Class<?>... parameterTypes) 获取指定的public方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 获取指定的任意方法,包含私有的
- 获取所有的方法
public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
public Method[] getDeclaredMethods()获取本类中所有的方法,包含私有的
# 成员方法调用
公共
获取Class对象
获取构造方法,创建对象
获取指定的public方法
执行方法
public Object invoke(Object obj, Object... args)
私有
获取Class对象
获取构造方法,创建对象
获取指定的private方法
开启暴力访问
执行方法
public Object invoke(Object obj, Object... args)