继上一篇博客【注解和反射】类加载器-CSDN博客
目录
七、获取类运行时结构
测试
getFields()和getDeclaredFields()
getMethods()和getDeclaredMethods()
七、获取类运行时结构
获取类运行时结构通常指的是在Java等面向对象编程语言中,使用反射(Reflection)机制来检查类、接口、字段(Field)和方法(Method)等程序元素的能力。这种能力允许你在运行时动态地获取类的信息,并且可以调用类的方法、改变字段的值等。
在Java中,获取类运行时结构主要包括以下几个方面:
-
获取Class对象: 要获取类的运行时结构,首先需要获取代表该类的
Class
对象。这可以通过多种方式实现,例如使用.class
语法、Class.forName()
方法或通过对象的getClass()
方法。 -
获取类的名称: 通过
Class
对象的getName()
方法可以获取类的全限定名(包括包名)。 -
获取类的修饰符: 使用
getModifiers()
方法可以获取类的修饰符(如public
、abstract
、final
等),通常与Modifier.toString()
方法结合使用以获取可读的修饰符字符串。 -
获取类的父类和实现的接口: 通过
getSuperclass()
方法可以获取类的直接父类,而getInterfaces()
方法则返回当前类实现的接口数组。 -
获取类的字段: 使用
getDeclaredFields()
方法可以获取类中声明的所有字段,无论访问权限如何。而getFields()
方法仅返回public
字段。 -
获取类的方法: 类似地,
getDeclaredMethods()
方法返回类中声明的所有方法,而getMethods()
方法仅返回public
方法,包括继承自父类的方法。 -
获取类的构造器: 通过
getDeclaredConstructors()
方法可以获取类的所有构造器,而getConstructors()
方法仅返回public
构造器。 -
获取类的注解: 如果类、方法、字段等上面有注解(Annotation),可以使用
getAnnotations()
、getDeclaredAnnotation()
等方法来获取这些注解信息。 -
获取类的内部类和接口: 通过
getDeclaredClasses()
方法可以获取当前类中声明的所有内部类和接口。 -
判断类的特性:
Class
类提供了一系列的方法来判断类的特性,如isInterface()
、isEnum()
、isAnnotation()
、isAnonymousClass()
、isArray()
、isPrimitive()
等。
获取类运行时结构的能力在Java中非常强大,它允许开发者在程序运行时动态地分析和操作类,这在很多场景下都很有用,比如框架设计、单元测试、序列化和反序列化、依赖注入等。然而,使用反射也需要谨慎,因为它可能会破坏封装性,降低性能,并且使代码更加复杂和难以维护。
测试
首先,我们需要定义一个 User
类,
class User{
private String name;
private int id;
private int age;
public User(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", id=" + id +
", age=" + age +
'}';
}
}
在代码示例中,Test05
类有一个 main
方法,该方法执行了以下操作:
- 创建了一个
User
类的对象user
。 - 通过调用
user.getClass()
获取了User
类的Class
对象,并将其存储在变量c1
中。 - 打印了
c1
所代表的类的全名(包名 + 类名)和简单类名(仅类名)。 - 打印了一行分隔符
-------
。 - 获取了
c1
所代表的类声明的所有字段(不包括继承的字段),并遍历打印了每个字段的信息。
public class Test05 {
public static void main(String[] args) throws ClassNotFoundException {
User user = new User();
Class c1 = user.getClass();
System.out.println(c1.getName());
System.out.println(c1.getSimpleName());
System.out.println("-------");
// Field[] fields = c1.getFields();
Field[] fields = c1.getDeclaredFields();
for(Field f: fields){
System.out.println(f);
}
}
}
这里是输出结果的详细分析:
-
System.out.println(c1.getName());
打印了User
类的完全限定名,即包括包名(如果有的话)和类名。因为User
类没有指定包名,所以输出就是User
。 -
System.out.println(c1.getSimpleName());
打印了User
类的简单名字,也就是不包括包名的类名,所以输出还是User
。 -
在打印了一行分隔符之后,代码通过
c1.getDeclaredFields()
获取了User
类中声明的所有字段,包括private
、protected
、默认(包私有)和public
字段。在这个例子中,User
类有三个private
字段:name
、id
和age
。这些字段被打印出来,包括它们的访问修饰符、类型、类名和字段名。
getFields()和getDeclaredFields()
在Java的反射API中,getFields()
和getDeclaredFields()
方法都用于获取类的字段,但它们在获取字段的范围和访问权限上有明显的区别:
-
getFields()
:- 这个方法返回的是当前类及其所有父类(包括
Object
类)中声明的public
字段。 - 它不包括当前类中声明的
private
、protected
和默认(包私有)访问级别的字段。 - 如果当前类或其父类中没有
public
字段,那么这个方法将返回一个长度为0的数组。
- 这个方法返回的是当前类及其所有父类(包括
-
getDeclaredFields()
:- 这个方法返回的是当前类中声明的所有字段,无论它们的访问权限是什么(
public
、private
、protected
或默认)。 - 它不包括父类中声明的任何字段,只查看当前类的字段。
- 即使当前类中没有字段,这个方法也会返回一个数组,只不过数组的长度是0。
- 这个方法返回的是当前类中声明的所有字段,无论它们的访问权限是什么(
通常,如果你只对当前类的字段感兴趣,而不关心父类的字段,那么你应该使用getDeclaredFields()
。同时,如果你需要访问非public
字段,你也需要使用getDeclaredFields()
,因为getFields()
不会返回这些字段。
另外,值得注意的是,如果你使用getDeclaredFields()
获取了非public
字段,并且想要访问或修改这些字段的值,你需要先调用Field.setAccessible(true)
来允许访问这些字段,否则会抛出IllegalAccessException
异常。
getMethods()
和getDeclaredMethods()
现在,让我们来讨论getMethods()
和getDeclaredMethods()
的区别:
-
getMethods()
:- 这个方法返回当前类及其所有父类(包括
Object
类)中声明的所有public
方法。 - 它不包括当前类中声明的
private
、protected
和默认(包私有)访问级别的方法。 - 返回的数组包含了从当前类开始,沿着继承链向上直到
Object
类的所有public
方法。
- 这个方法返回当前类及其所有父类(包括
-
getDeclaredMethods()
:- 这个方法返回当前类中声明的所有方法,无论它们的访问权限是什么(
public
、private
、protected
或默认)。 - 它不包括父类中声明的任何方法,只查看当前类的方法。
- 即使当前类中没有方法,这个方法也会返回一个数组,只不过数组的长度是0。
- 这个方法返回当前类中声明的所有方法,无论它们的访问权限是什么(
与字段的情况类似,如果你只对当前类的方法感兴趣,而不关心父类的方法,那么你应该使用getDeclaredMethods()
。同时,如果你需要访问非public
方法,你也需要使用getDeclaredMethods()
,因为getMethods()
不会返回这些方法。
同样值得注意的是,如果你使用getDeclaredMethods()
获取了非public
方法,并且想要调用这些方法,你需要先调用Method.setAccessible(true)
来允许访问这些方法,否则会抛出IllegalAccessException
异常。