1. 概述

在 Java 中,**java.lang.Class 类是所有反射操作的入口**。一旦我们拿到了 Class 对象,就可以调用其方法来获取各种反射相关的类对象。

本篇文章将重点讲解获取 Class 对象的两种常见方式之间的区别:

  • 调用对象的 getClass() 方法
  • 使用 .class 语法

2. 简要介绍两种方式

Object.getClass()Object 类的一个实例方法。如果我们有一个对象,就可以通过 object.getClass() 获取其运行时类型对应的 Class 对象。

而使用 ClassName.class 则是一种静态方式来获取类的 Class 对象。举个例子就能看得很清楚:

@Test
public void givenObjectAndType_whenGettingClassObject_thenTwoMethodsHaveTheSameResult() {
    String str = "I am an object of the String class";

    Class fromStrObject = str.getClass();
    Class clazz = String.class;

    assertSame(fromStrObject, clazz);
}

在上面的测试中,我们分别通过对象和类名获取了 StringClass 对象,最终断言两者是同一个实例。

虽然结果相同,但它们在语义和使用场景上存在差异,接下来我们详细分析。

3. 运行时类型 vs 静态类型

再回顾一下之前的例子:
✅ 调用 str.getClass() 得到的是 str 对象的运行时类型
✅ 而 String.class 获取的是编译时已知的静态类型

在这个例子中,两者的类型恰好一致。但如果涉及到继承关系,结果就不一样了。

我们来看两个类:

public class Animal {
    protected int numberOfEyes;
}

public class Monkey extends Animal {
    // monkey stuff
}

然后做如下测试:

@Test
public void givenClassInheritance_whenGettingRuntimeTypeAndStaticType_thenGetDifferentResult() {
    Animal animal = new Monkey();

    Class runtimeType = animal.getClass();
    Class staticType = Animal.class;

    assertSame(staticType, runtimeType);
}

❌ 运行这个测试会失败:

java.lang.AssertionError: ....
Expected :class com.baeldung.getclassobject.Animal
Actual   :class com.baeldung.getclassobject.Monkey

虽然变量 animal 的声明类型是 Animal,但其实际运行时指向的是 Monkey 实例,所以 getClass() 返回的是 Monkey.class,而 Animal.class 始终是 Animal 类型。

⚠️ 所以记住:

  • obj.getClass() 获取的是运行时类型
  • SomeClass.class 获取的是编译期静态类型

4. 处理基本数据类型(Primitive Types)

我们在日常开发中经常使用基本类型,比如 intdouble 等。那能不能通过 number.getClass() 来获取基本类型的 Class 对象呢?

int number = 7;
Class numberClass = number.getClass();

❌ 编译报错:

Error: java: int cannot be dereferenced

因为 int 是基本类型,不能调用方法,所以 getClass() 无法用于基本类型。

.class 语法可以吗?

✅ 当然可以:

@Test
public void givenPrimitiveType_whenGettingClassObject_thenOnlyStaticTypeWorks() {
    Class intType = int.class;
    assertNotNull(intType);
    assertEquals("int", intType.getName());
    assertTrue(intType.isPrimitive());
}

从 Java 9 开始,基本类型的 Class 对象也属于 java.base 模块。

⚠️ 所以:

  • obj.getClass() ❌ 不能用于基本类型
  • int.class ✅ 可以直接获取基本类型的 Class 对象

5. 在没有实例的情况下获取 Class 对象

有时候我们想获取某个类的 Class 对象,但又没有这个类的实例,比如:

  • 抽象类
  • 接口
  • 工具类(构造方法私有)
public abstract class SomeAbstractClass {
    // ...
}

interface SomeInterface {
   // some methods ...
}

public class SomeUtils {
    private SomeUtils() {
        throw new RuntimeException("This Util class is not allowed to be instantiated!");
    }
    // some public static methods...
}

这种情况下,我们显然不能调用 obj.getClass(),但可以用 .class 语法:

@Test
public void givenTypeCannotInstantiate_whenGetTypeStatically_thenGetTypesSuccefully() {
    Class interfaceType = SomeInterface.class;
    Class abstractClassType = SomeAbstractClass.class;
    Class utilClassType = SomeUtils.class;

    assertNotNull(interfaceType);
    assertTrue(interfaceType.isInterface());
    assertEquals("SomeInterface", interfaceType.getSimpleName());

    assertNotNull(abstractClassType);
    assertEquals("SomeAbstractClass", abstractClassType.getSimpleName());

    assertNotNull(utilClassType);
    assertEquals("SomeUtils", utilClassType.getSimpleName());
}

✅ 所以总结:

  • 没有实例时 ❌ 不能用 obj.getClass()
  • 没有实例时 ✅ 可以用 SomeClass.class

6. 总结

本文我们对比了获取 Class 对象的两种方式:

特性 obj.getClass() SomeClass.class
获取类型 运行时类型 ✅ 静态类型 ✅
基本类型 ❌ 不支持 ✅ 支持
抽象类、接口、工具类 ❌ 不支持 ✅ 支持

简单粗暴一句话总结:

如果你有对象,就用 getClass();如果没对象,或者想获取静态类型,就用 .class

源码地址:GitHub


原始标题:The Difference Between a.getClass() and A.class in Java

« 上一篇: 学习JPA和Hibernate