0%

Java-40-反射

反射的基本概念

Java程序中,所有的对象都有两种类型:编译时类型和运行时类型,而很多时候对象的编译时类型和运行时类型不一致。

例如:某些变量或形参的类型是Object类型,但是程序却需要调用该对象运行时类型的方法,该方法不是Object中的方法,那么如何解决呢?

为了解决这些问题,程序需要在运行时发现对象和类的真实信息,现在有两种方案:

  • 方案1:在编译和运行时都完全知道类型的具体信息,在这种情况下,我们可以直接先使用instanceof运算符进行判断,再利用强制类型转换符将其转换成运行时类型的变量即可。
  • 方案2:编译时根本无法预知该对象和类的真实信息,程序只能依靠运行时信息来发现该对象和类的真实信息,这就必须使用反射。

因为加载完类之后,就产生了一个Class类型的对象,并将引用存储到方法区,那么每一个类在方法区内存都可以找到唯一Class对象与之对应,这个对象包含了完整的类的结构信息,我们可以通过这个对象获取类的结构。这种机制就像一面镜子,Class对象像是类在镜子中的镜像,通过观察这个镜像就可以知道类的结构,所以,把这种机制形象地称为反射机制。

  • 非反射:类(原物)—>类信息
  • 反射:Class对象(镜像)—>类(原物)

获取类类对象的四种方法

要想解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API(1)java.lang.Class(2)java.lang.reflect.*。所以,Class对象是反射的根源。

获取类类对象的四种方法:

  1. 使用类名获取该类的类类对象
  2. 使用类对象获取该类的所属的类类对象
  3. 使用字符串类获取类类对象
  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
package com.tipdm.demo8;

public class demo01 {
public static void main(String[] args) {
// 获取类类对象的四种方法
// 1. 使用类名获取该类的类类对象
Class classone = Student.class;
System.out.println(classone.getName());

// 2. 使用类对象获取该类的所属的类类对象
Student student = new Student();
Class classtwo = student.getClass();

// 3. 使用字符串类获取类类对象
try {
Class classThree = Class.forName("com.tipdm.demo8.Student");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

// 4. 使用类加载器获取类类对象
try {
Class classFore = Student.class.getClassLoader().loadClass("com.tipdm.demo8.Student");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

// 注意:一个类的类类对象在方法区中只会有一个对象
System.out.println(classone == classtwo);
}
}
1
2
3
4
com.tipdm.demo8.Student
true

进程已结束,退出代码0

注意:一个类的类类对象在方法区中只会有一个对象。

类类对象的常用方法

Class类的常用方法

定用代码Class classOne = Student.class;定义好一个类类对象后,其有以下方法可以调用。

方法 意义
getName() 获取类的完整类名
getSimpleName() 获取类的简单类名
getPackage() 获取类的包对象
getSuperclass() 获取父类的类
getFields() 返回类中所有public修饰的属性,不仅限本类,包含从父类继承到的属性
getDeclaredFields() 返回所有修饰符修饰的属性,仅限本类
getField("two") 根据参数属性名获取单个属性,仅能获取public修饰的属性,不仅限本类,包含从父类继承到的属性
getDeclaredField("one") 根据参数属性名获取单个属性,可以获取所有修饰符修饰的属性,仅限本类
getMethods() 获取所有public修饰的方法,不仅限本类,包含从父类继承到的
getDeclaredMethods() 获取所有修饰符修饰的方法,仅限本类
getMethod("methodOne") 根据参数方法名获取单个的public修饰的方法,不仅限本类,包含从父类继承到的方法
getDeclaredMethod
("sayHello",String.class,int.class)
获取单个方法,无参方法只写方法名,有参方法除了方法名还需要写出参数组
getConstructors() 获取所有public修饰的构造器
getDeclaredConstructors() 获取所有修饰符修饰的构造器
getConstructor() 根据参数列表获取public修饰的构造器
getDeclaredConstructor
(String.class,int.class,String.class)
根据参数列表获取所有修饰符修饰的构造器

Field类的常用方法

方法 意义
getName() 获取属性名
getType() 获取属性类型
getModifiers() 获取属性的修饰符(返回为int,可以使用Modifier中的toString将其对应为具体的修饰符)
set(obj, 'Tom') 给obj对象传参(静态属性可以省略obj,设置为null
get(obj) 获取obj对象的属性

代码验证:

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
43
44
45
46
package com.tipdm.demo8;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class demo02 {
public static void main(String[] args) {
// Class类的常用方法
Class classOne = Student.class;

System.out.println(classOne.getName());// 获取类的完整类名
System.out.println(classOne.getSimpleName());// 获取类的简单类名
System.out.println(classOne.getPackage());// 获取类的包对象
System.out.println(classOne.getSuperclass().getName());// 获取父类的类类对象
Field[] fields = classOne.getFields();// 返回类中所有public修饰的属性,不仅限本类,包含从父类继承到的属性
System.out.println(fields.length);
System.out.println("-------------------------------------");
for (Field field : fields) {
System.out.println(field.getModifiers());
System.out.println(field.getName());// 获取属性名
}
System.out.println("-----------------------------------");
Field[] fields1 = classOne.getDeclaredFields();// 返回所有修饰符修饰的属性,仅限本类
// System.out.println(fields1.length);
for (Field field : fields1) {
System.out.println(field.getModifiers());
String modStr = Modifier.toString(field.getModifiers());
System.out.println(modStr);
System.out.println(field.getType().getSimpleName());
System.out.println(field.getName());
}
System.out.println("-----------------------------------");
try {
Field field = classOne.getField("two");// 根据参数属性名获取单个属性,仅能获取public修饰的属性,不仅限本类,包含从父类继承到的属性
System.out.println(field);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
try {
Field field = classOne.getDeclaredField("one");// 根据参数属性名获取单个属性,可以获取所有修饰符修饰的属性,仅限本类
System.out.println(field);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}

Method方法的常用方法

方法 意义
getName() 获取方法名
getSimpleName() 获取简单的方法名
getModifiers() 获取所有方法的修饰符(返回为int,可以使用Modifier中的toString将其对应为具体的修饰符)
getParameterTypes() 获取所有参数类型
getReturnType() 获取方法的返回值类型
invoke(obj, "xxxx") 运行obj对象的某个方法(如果是静态方法可以省略对象)

代码验证:

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
43
package com.tipdm.demo8;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class demo03 {
public static void main(String[] args) {
// Class类的常用方法
Class classOne = Student.class;

Method[] methods = classOne.getMethods();// 获取所有public修饰的方法,不仅限本类,包含从父类继承到的
for (Method method : methods) {
String str = Modifier.toString(method.getModifiers());
System.out.println(str);
System.out.println(method.getReturnType().getSimpleName());
System.out.println(method.getName());
Class[] classes = method.getParameterTypes();
for (Class aClass : classes) {
System.out.print(aClass.getSimpleName()+" ");
}
System.out.println();
}
System.out.println("---------------------------------------------");
Method[] methods1 = classOne.getDeclaredMethods();// 获取所有修饰符修饰的方法,仅限本类
for (Method method : methods1) {
System.out.println(method.getName());
}
System.out.println("---------------------------------------------");
try {
Method method = classOne.getMethod("methodOne");// 根据参数方法名获取单个的public修饰的方法,不仅限本类,包含从父类继承到的方法
System.out.println(method);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}

try {
// 获取单个方法,无参方法只写方法名,有参方法除了方法名还需要写出参数组
Method method = classOne.getDeclaredMethod("sayHello",String.class,int.class);// 获取所有修饰符修饰的方法,仅限本类
System.out.println(method);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}

构造器

其他内容都有getName方法去获取相应名称,但是构造器没有,因为构造器的名称与本类一致。

并且构造方法无法从父类继承,所以在获取构造器时只能获取本类的。

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
package com.tipdm.demo8;

import java.lang.reflect.Constructor;

public class demo04 {
public static void main(String[] args) {
// Class类的常用方法
Class classOne = Student.class;

Constructor[] cs1 = classOne.getConstructors();// 获取所有public修饰的构造器
System.out.println(cs1.length);
Constructor[] cs2 = classOne.getDeclaredConstructors();// 获取所有修饰符修饰的构造器
System.out.println(cs2.length);
try {
Constructor c1 = classOne.getConstructor();// 根据参数列表获取public修饰的构造器
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
Constructor c2 = classOne.getDeclaredConstructor(String.class,int.class,String.class);// 根据参数列表获取所有修饰符修饰的构造器
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}

应用

利用反射的方式访问类中的属性和方法。

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
package com.atguigu.test1;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestFive {
public static void main(String[] args) {
try {
// 利用反射的方式创建对象并调用属性或者方法
// 获取类类对象
Class classOne = Student.class;
// Student stu = new Student();
Object obj = classOne.newInstance();// 创建类类对象代表的类的对象(无参构造器)
// 有参构造器
// Constructor constructor = classOne.getDeclaredConstructor(String.class,int.class,String.class);
// Object obj = constructor.newInstance("Tom",20,"男");
System.out.println(obj);
// stu.stuName = "Tom";
Field field = classOne.getDeclaredField("stuName");
field.setAccessible(true);// 设置私有成员的公开访问特性
field.set(obj,"Tom");
System.out.println(field.get(obj)); // 打印obj对象
Field field1 = classOne.getDeclaredField("school");
field1.set(null,"minglog123");// 静态属性可以在第一个参数传null
System.out.println(field1.get(obj));
// stu.setStuName("Tom");
Method method = classOne.getDeclaredMethod("setStuName", String.class);
method.invoke(obj,"Jerry");
// System.out.println(stu.getStuName());
Method method1 = classOne.getDeclaredMethod("getStuName");
System.out.println(method1.invoke(obj));
Method method2 = classOne.getDeclaredMethod("methodThree");
method2.invoke(null); // 静态方法可以在第一个参数传null
}catch (Exception ex){
ex.printStackTrace();
}
}
}
-------------本文结束感谢您的阅读-------------