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);
}
在上面的测试中,我们分别通过对象和类名获取了 String
的 Class
对象,最终断言两者是同一个实例。
虽然结果相同,但它们在语义和使用场景上存在差异,接下来我们详细分析。
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)
我们在日常开发中经常使用基本类型,比如 int
、double
等。那能不能通过 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