ZetCode

Java Class 类

最后修改时间:2025 年 4 月 13 日

java.lang.Class 类表示 Java 应用程序中类和接口的运行时表示。 它是反射操作的入口点,允许程序员检索有关类的元数据,例如其字段、方法、构造函数和修饰符。 通过利用 Class 类,开发人员可以动态地分析和操作 Java 类型。

Java 中的每个对象都与一个描述其类型的 Class 对象相关联。 这些对象由 Java 虚拟机 (JVM) 在加载相应的类时自动创建。 由于 Class 类没有公共构造函数,因此无法直接实例化 Class 的实例,而是通过 getClassClass.forName(String className) 等方法获取。

理解 Class 类是高级 Java 功能(如反射、注解和动态类加载)的基础。 反射允许程序在运行时检查和修改类结构,使框架和库能够在没有显式类型定义的情况下执行动态操作。

Class 类方法

Class 类提供了各种方法来检查运行时类元数据。 一些关键方法包括

通过利用这些方法,Java 实现了动态类检查和操作,这在框架、依赖注入、序列化和测试环境中特别有用。

获取 Class 对象

在 Java 中,有几种方法可以获取 Class 对象。 最常见的方法是使用类字面量,在对象上调用 getClass,以及使用 Class.forName。 每种方法都有不同的用例和含义。

Main.java
class Sample {}

void main() {

    Class<Sample> cls1 = Sample.class;
    
    Sample obj = new Sample();
    Class<? extends Sample> cls2 = obj.getClass();
    
    try {
        Class<?> cls3 = Class.forName("Sample");
    } catch (ClassNotFoundException e) {
        System.out.println("Class not found: " + e.getMessage());
    }
    
    System.out.println("Class name via class literal: " + cls1.getName());
    System.out.println("Class name via getClass(): " + cls2.getName());
}

此示例演示了获取 Class 对象的三种方法。 类字面量是编译时安全的,getClass 适用于现有对象,而 Class.forName 允许按名称动态加载类。 所有这三种方法都为给定的类返回相同的 Class 对象。

检查类元数据

Class 类提供了检查类结构各个方面的方法。 这包括获取类名、包、修饰符、超类和实现的接口。 这些方法是基于反射的代码分析的基础。

Main.java
interface Greetable {
    void greet();
}

class Person implements Greetable {
    public void greet() {
        System.out.println("Hello!");
    }
}

void main() {

    Class<Person> personClass = Person.class;
    
    System.out.println("Class name: " + personClass.getName());
    System.out.println("Simple name: " + personClass.getSimpleName());
    System.out.println("Package: " + personClass.getPackageName());
    System.out.println("Superclass: " + personClass.getSuperclass());
    
    Class<?>[] interfaces = personClass.getInterfaces();
    System.out.println("Implemented interfaces:");
    for (Class<?> iface : interfaces) {
        System.out.println("  " + iface.getSimpleName());
    }
    
    System.out.println("Is interface? " + personClass.isInterface());
    System.out.println("Is array? " + personClass.isArray());
}

此示例显示如何检查基本类元数据。 我们检查 Person 类的名称、包、超类和实现的接口。 输出演示了反射如何在运行时揭示类的结构。

访问字段

Class 类提供了以反射方式访问类的字段的方法。 您可以获取公共字段、声明的字段(包括非公共字段),并检查字段元数据,如类型和修饰符。 字段值也可以以反射方式获取和设置。

Main.java
class Book {

    public String title;
    private String author;
    protected int pages;
}

void main() throws Exception {

    Class<Book> bookClass = Book.class;
    Book book = new Book();
    
    // Get all public fields (including inherited)
    Field[] publicFields = bookClass.getFields();
    System.out.println("Public fields:");
    for (Field field : publicFields) {
        System.out.println("  " + field.getName());
    }
    
    // Get all declared fields (regardless of modifier)
    Field[] allFields = bookClass.getDeclaredFields();
    System.out.println("\nAll declared fields:");
    for (Field field : allFields) {
        System.out.println("  " + field.getName() + 
                          " (" + field.getType().getSimpleName() + ")");
    }
    
    // Access private field
    Field authorField = bookClass.getDeclaredField("author");
    authorField.setAccessible(true); // Override access control
    authorField.set(book, "J.K. Rowling");
    System.out.println("\nAuthor set to: " + authorField.get(book));
}

此示例演示了通过反射进行字段访问。 我们展示了如何获取公共字段、所有声明的字段,以及如何通过覆盖访问控制来访问私有字段。 请注意,绕过访问控制应该谨慎进行,因为它会破坏封装。

调用方法

反射允许在运行时调用方法。 Class 类提供了获取方法元数据和调用方法的方法。 这对于需要动态调用方法的框架(如依赖注入或测试框架)非常有用。

Main.java
class Calculator {

    public int add(int a, int b) {
        return a + b;
    }
    
    private String repeat(String s, int times) {
        return s.repeat(times);
    }
}

void main() throws Exception {

    Class<Calculator> calcClass = Calculator.class;
    Calculator calc = new Calculator();
    
    // Get and invoke public method
    Method addMethod = calcClass.getMethod("add", int.class, int.class);
    int result = (int) addMethod.invoke(calc, 5, 3);
    System.out.println("5 + 3 = " + result);
    
    // Get and invoke private method
    Method repeatMethod = calcClass.getDeclaredMethod("repeat", 
                                                    String.class, int.class);
    repeatMethod.setAccessible(true);
    String repeated = (String) repeatMethod.invoke(calc, "Java ", 3);
    System.out.println("Repeated: " + repeated);
    
    // List all public methods (including inherited)
    System.out.println("\nPublic methods:");
    for (Method method : calcClass.getMethods()) {
        System.out.println("  " + method.getName());
    }
}

此示例显示了如何以反射方式发现和调用方法。 我们演示了调用公共和私有方法,列出所有公共方法,以及处理方法参数。 方法调用在运行时是类型安全的,但会失去编译时类型检查。

创建实例

Class 类允许通过反射动态实例化对象,即使在编译时不知道对象的类型也可以创建对象。 这在框架、依赖注入和需要灵活对象创建的场景中特别有用。 使用反射,可以动态访问和调用构造函数,从而可以精确控制对象实例化。

Main.java
static class Vehicle {

    private String type;

    public Vehicle() {
        this("Unknown");
    }

    public Vehicle(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }
}

public static void main(String[] args) throws Exception {

    // Using default constructor reflectively
    Class<Vehicle> vehicleClass = Vehicle.class;
    Vehicle v1 = vehicleClass.getDeclaredConstructor().newInstance();
    System.out.println("Default type: " + v1.getType());

    // Using parameterized constructor
    Constructor<Vehicle> constructor = vehicleClass.getConstructor(String.class);
    Vehicle v2 = constructor.newInstance("Truck");
    System.out.println("Specified type: " + v2.getType());

    // Creating instance from class name
    Class<?> dynamicClass = Class.forName("Main$Vehicle");
    Vehicle v3 = (Vehicle) dynamicClass.getConstructor(String.class).newInstance("Car");
    System.out.println("Dynamic type: " + v3.getType());
}

此示例演示了以反射方式创建实例的三种方法

基于反射的实例化广泛用于 Spring 等框架和依赖注入系统中,从而可以在没有显式类型依赖关系的情况下动态创建对象。 通过利用反射,应用程序可以根据运行时配置实例化类,从而提高模块化和可扩展设计中的灵活性。

使用数组

Class 类为数组类型提供特殊支持。 您可以以反射方式创建数组、获取组件类型并确定数组维度。 数组类在 Java 反射中遵循特殊的命名约定。

Main.java
void main() throws ClassNotFoundException {
    
    // Get array class
    Class<?> stringArrayClass = String[].class;
    System.out.println("Array class: " + stringArrayClass.getName());
    
    // Create array instance
    String[] names = (String[]) Array.newInstance(String.class, 3);
    Array.set(names, 0, "Alice");
    Array.set(names, 1, "Bob");
    Array.set(names, 2, "Charlie");
    
    System.out.println("\nArray contents:");
    for (int i = 0; i < Array.getLength(names); i++) {
        System.out.println("  " + Array.get(names, i));
    }
    
    // Multi-dimensional arrays
    Class<?> int2DArrayClass = Class.forName("[[I");
    int[][] matrix = (int[][]) Array.newInstance(int.class, 2, 3);
    matrix[1][2] = 42;
    System.out.println("\nMatrix[1][2] = " + matrix[1][2]);
    
    // Component type
    Class<?> componentType = stringArrayClass.getComponentType();
    System.out.println("\nComponent type: " + componentType);
}

此示例显示了数组的反射。 我们演示了获取数组类、以反射方式创建数组、访问元素、使用多维数组以及检查组件类型。 数组反射遵循 Java 内部的数组类型命名约定。

来源

Java Class 类文档

在本文中,我们介绍了 Java Class 类的基本方面以及实际示例。 理解 Class 对于高级 Java 功能(如反射、注解和动态类加载)至关重要。

作者

我叫 Jan Bodnar,是一位拥有多年经验的敬业程序员。 我于 2007 年开始撰写编程文章,至今已撰写超过 1,400 篇文章和 8 本电子书。 凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程