java反射

faith team

Class类

通过Class实例获取class信息的方法称为反射(Reflection)
除了int的一些基本类型外 java的其他类型都是class(包括interface)。例如

  • String
  • Object
  • Runnable

!!class(包括接口) 的本质是数据类型(type)

class 是jvm在执行过程中动态加载的 jvm在第一次读取到一种class类型时 将其加载进内存
每次加载一种class,jvm就为其创建一个Class类型的实例 并关联起来,这里的Class类型是一个名叫Class的class

1
2
3
public final class Class {
private Class() {}
}

以String类为例,当JVM加载String类时,它首先读取String.class文件到内存,然后,为String类创建一个Class实例并关联起来:

1
Class cls = new Class(String);

这个Class实例是JVM内部创建的,如果我们查看JDK源码,可以发现Class类的构造方法是private,只有JVM能创建Class实例,我们自己的Java程序是无法创建Class实例的。

所以,JVM持有的每个Class实例都指向一个数据类型(class或interface):

Test

一个Class实例包含了该class的所有完整信息:

Test

因为jvm为每个实例都创建了对应的Class实例 在Class当中保存了实例的各种信息,所以如果获取了某个class实例,我们可以通过Class类来找到class的各种信息

??如何获取一个class的Class实例呢??
1.方法一:直接调用class的静态变量

1
Class cls = String.class;

2.方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取

1
2
String s = "Hello";
Class cls = s.getClass();

3.方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取

1
Class cls = Class.forName("java.lang.String");

因为Class实例在JVM中是唯一的,所以,上述方法获取的Class实例是同一个实例。可以用==比较两个Class实例:

1
2
3
4
5
6
Class cls1 = String.class;

String s = "Hello";
Class cls2 = s.getClass();

boolean sameClass = cls1 == cls2; // true

Class实例比较和instanceof的差别

1
2
3
4
5
6
7
Integer n = new Integer(123);

boolean b1 = n instanceof Integer; // true,因为n是Integer类型
boolean b2 = n instanceof Number; // true,因为n是Number类型的子类

boolean b3 = n.getClass() == Integer.class; // true,因为n.getClass()返回Integer.class
boolean b4 = n.getClass() == Number.class; // false,因为Integer.class!=Number.class

instanceof 不仅仅匹配指定类型 还匹配子类 而使用==判断class实例可以精确的判断数据类型 不会匹配子类
如果获取到一个Class 可以通过Class实例来创建对应类型的实例

1
2
Class cls = String.class;
String a = (String)cls.newInstance()//等同与new String();

Class的newInstance()可以创建类实例,但是它局限与只能调用public无参构造函数,带参数的或者private的就不行

动态加载

jvm在执行java程序的时候 不会一次性把所有用到的class全部加载到内存中 只会在第一次用到的时候加载

1
2
3
4
5
6
7
8
9
10
//Main.java
public static void main( String[] args )
{
if (true){
test();
}
}
static void test(){
Person p=new Person();
}

当执行main.java jvm知道了你要使用main.java 所以就会动态创建一个Main的Class对象 但是没有执行test方法是不会创建Person对象

访问字段

前面也说了当我们获取了任意一个object创建出它的class对象 那么我们就可以获得它的一切信息
如何通过class实例获取字段信息呢?

  • Field getField(name):根据字段名获取某个public的field(包括父类)
  • Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
  • Field[] getFields():获取所有public的field(包括父类)
  • Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
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
//获取字段名
Class sclass = Student.class;
System.out.println(sclass.getDeclaredField("score"));
//获得私有的字段
System.out.println(sclass.getDeclaredField("age"));
Field age = sclass.getDeclaredField("age");
//字段名称
System.out.println(age.getName());
//字段类型
System.out.println(age.getType());
//返回修饰符 bit类型
int modifiers = age.getModifiers();
System.out.println(age.getModifiers());
//是否为public修饰符
System.out.println(Modifier.isPublic(modifiers));

//获取字段值 更改字段值
Person p = new Person("faith");
Class aClass = p.getClass();
//获取字段
Field declaredField = aClass.getDeclaredField("name");
declaredField.setAccessible(true);
//当name字段是私有的时候 就会报NoSuchFieldException 需要修改权限
Object o = declaredField.get(p);
System.out.println(o);
//更改字段值
declaredField.set(p,"evil");
System.out.println("update" + declaredField.get(p));

调用方法

Class类提供了以下几个方法来获取Method

  • Method getMethod(name, Class…):获取某个public的Method(包括父类)
  • Method getDeclaredMethod(name, Class…):获取当前类的某个Method(不包括父类)
  • Method[] getMethods():获取所有public的Method(包括父类)
  • Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)
1
2
3
4
5
//调用方法
Class<Person> personClass = Person.class;
//获取person的getAge方法 私有的getScore方法
System.out.println(personClass.getMethod("getAge", int.class));
System.out.println(personClass.getDeclaredMethod("getScore"));

一个Method对象包含了方法的所有信息

  • getName():返回方法名称,例如:”getScore”;
  • getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;
  • getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
  • getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。

通过反射来调用substring方法

1
2
3
4
5
String str = "hello world";
str.substring(6);
Method substring = String.class.getMethod("substring", int.class);
String strSub = (String) substring.invoke(str,6);
System.out.println(strSub);

调用构造方法

我们通过new来创造类的实例 也可以通过反射的机制来创造类的实例

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

调用Class.newInstance()的局限就是它只能调用类的public 类型的无参构造函数 如果类的构造函数不是public 并且不是无参就无法调用
为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。Constructor对象和Method非常类似,不同之处仅在于它是一个构造方法,并且,调用结果总是返回实例:

通过Class实例获取Constructor的方法如下:

  • getConstructor(Class…):获取某个public的Constructor;
  • getDeclaredConstructor(Class…):获取某个Constructor;
  • getConstructors():获取所有public的Constructor;
  • getDeclaredConstructors():获取所有Constructor。
    注意Constructor总是当前类定义的构造方法,和父类无关,因此不存在多态的问题。

调用非public的Constructor时,必须首先通过setAccessible(true)设置允许访问。setAccessible(true)可能会失败。

获取父类 getSuperclass()

1
2
3
4
//获取父类
Class<Integer> integerClass = Integer.class;
Class<? super Integer> superclass = integerClass.getSuperclass();
System.out.println(superclass);

获取接口 getInterfaces()

1
2
3
4
5
6
7
//获取接口
Class<Integer> integerClass1 = Integer.class;
Class<?>[] interfaces = integerClass1.getInterfaces();
for (Class i:
interfaces) {
System.out.println(i);
}

getinterfaces不会返回它父类继承的接口

  • Title: java反射
  • Author: faith team
  • Created at: 2023-08-01 11:21:05
  • Updated at: 2025-11-29 09:01:08
  • Link: https://redefine.ohevan.com/2023/08/01/20230801java反射/
  • License: This work is licensed under CC BY-NC-SA 4.0.
 Comments