JAVA-反射

本文最后更新于:2022年9月26日 下午

Java-反射

1.反射的概述:

反射(Reflection),Java反射机制是在运行状态时,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java的反射机制。

反射是java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API获取任何类的内部信息,并且能直接操作任意对象的内部属性及方法。

1
Class c = Class.forName(“Java.lang.String”)

流程示意图:

302415386d4040322023995504e552d

举例理解反射:

假设一个人意外死亡,法医需要解剖来查看他的死因,以及他的属性(年龄,体重,呼吸道,各类器官…)

因为人已经死亡,我们解剖的过程也可以形象的理解为“反射”。得到完整的“包类名称”就相当于解剖获得器官的过程。

反射相关的主要API:

  • java.lang.Class: 代表一个类
  • java.lang.reflect.Method: 代表类的方法
  • java.lang.reflect.Field: 代表类的成员变量
  • java.lang.reflect.Constructor: 代表类的构造器

Class类的常用方法:

方法名 功能
static ClassforName(String name) 返回指定类名name的Class对象
newInstance() 在内存中生成一个实例
getName() 返回当前Class对象所表示的实体(类,接口,数组类或void)的名称
Class getSuperClass() 返回当前Class对象的父类的Class对象
Class[] getinterfaces() 获取当前Class对象的接口
ClassLoader getClassLoader() 返回该类的类加载器
Field[] getDeclaredFields 返回Field对象的一个数组
Method getMethed(String name,Class.. parameterTypes) 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。

2.获取Class对象的三种方式

1: 通过该实例变量提供的getClass()方法获取:

2: 通过静态方法Class.forName()获取(要先知道class的完整类名):

3: 直接通过一个class的静态变量class获取:

如图:

1664167464019

注意

运行期间,一个类中只有一个class对象产生,图中c1,c2,c3的hashCode都是相同的。

且平常最常用的方法就是第二种。

获取运行时类的完整结构

1:获取字段

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
package com.ke1r.reflection;

import java.lang.reflect.Field;

//获取类的信息
public class Test03 {
public static void main(String[] args) throws ClassNotFoundException,NoSuchFieldException {
Class c1 = Class.forName("com.ke1r.reflection.User");

//获得类的名字
System.out.println(c1.getName());//获得包名+类名
System.out.println(c1.getSimpleName());//获得类名
//获得类的属性
System.out.println("=========================");
Field[] fields = c1.getFields();//只能找到public属性

fields = c1.getDeclaredFields();//找到全部的属性
for (Field field : fields) {
System.out.println(field);
}

//获得指定属性的值
Field name = c1.getDeclaredField("name");
System.out.println(name);
}
}

运行结果:

1664175020886

所有通过Class实例的方法可以获取Field实例,getField(),getFields(),getDeclaredField(),getDeclaredFields();

此外Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)来访问非public字段。

2:调用方法

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
package com.ke1r.reflection;

import java.lang.reflect.Method;
public class Test03 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.ke1r.reflection.User");

//获得类的方法
System.out.println("==================");
Method[] methods = c1.getMethods();//获得本类及其父类的全部public方法
for (Method method : methods) {
System.out.println("正常的:"+method);
}//公共的
methods = c1.getDeclaredMethods();//获得本类的所有方法
for (Method method : methods) {
System.out.println("getDeclaredMethods:"+method);
}//全部的

//获得指定方法
//重载
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
}
}

运行结果:

1664175785733

通过Class实例的方法可以获取Method实例:getMethod(),getMethods(),getDeclaredMethod(),getDeclaredMethods();

调用指定方法:

通过getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的类型。之后使用Object invoke(Object obj,Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。若原方法声明为private,则需要在调用此invoke()方法前,显示调用方法对象的setAccessible(true)方法,则可以访问private方法。

注意:Method、Field、Constructor对象都有**setAccessible()**方法。

setAccessible()作用是启用和禁用访问安全检查的开关。

3:获得构造器方法

通过反射来创建新的实例,我们可以调用Class提供的newInstance()方法:

1
Person p = Person.class.newInstance();

但是它只能调用该类的public无参数构造方法。如果想要调用任意构造方法,就需要用到API提供的Constructor对象,Constructor对象和Method很类似,但是不同之处仅在于它是一个构造方法,调用结果总是返回实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 package com.ke1r.reflection;

import java.lang.reflect.Constructor;

public class Test03 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.ke1r.reflection.User");
//获得指定的构造器
System.out.println("=====================");
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println("###"+constructor);
}
//获得指定的构造器
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
System.out.println("指定:"+ declaredConstructor);
}
}

运行结果:

1664177302531

通过Class实例方法可以获取Constructor实例:

getConstructor(),getConstructors(),getDeclaredConstructor(),getDeclaredConstructors()

Java反射的优缺点

优点:

可以实现动态创建对象和编译,有很大的灵活性。

缺点:

对性能有影响,使用反射机制基本上是一种解释操作,我们可以告诉JVM我们希望做什么并且它满足我们的要求,这类操作总是慢于直接执行相同的操作。

测试效果:

1664178340553


JAVA-反射
https://ke1r.cn/2022/09/26/Java-反射/
作者
Ke1R
发布于
2022年9月26日
许可协议