1. Class 是什么

废话不说,反手翻译下 Class 类官方注释

Instances of the class Class represent classes and interfaces in a running Java application. An enum is a kind of class and an annotation is a kind of interface. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions. The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects.

已经被应用程序加载的类( class )和接口( interface )才能获取到 Class 实例对象。枚举归属于类( class ) 而注解归属于接口( interface )。所有的数组也是归属于类( class )的,这个类的 Class 实例对象是由具有相同元素类型和维数组成的。Java 的基本类型(boolean, byte, char, short, int, long, float, and double) 以及关键字 void 也都有 Class 对象。 Class 没有公有的构造方法。它是在 Java 虚拟机加载类时通过类加载器( class loader )调用 defineClass 自动创建。 // 例子略

上边是笼统的说明,细分的话还会有如下几类

1.1 Top level classes

顶层类,是 Java 中对类的一种定义方式,在 .java 文件中处于最外层的类就称为顶层类,在其外部不存在将其包围起来的任何代码块。
.java 文件中允许多个顶层类并列存在,但是只能有一个与其文件名同名、声明为 public 的顶层类。这个与文件名同名的顶层类可以声明为包私有的,但在这个文件中的其他顶层类只能声明为包私有的。

  1. // Human.java
  2. package classtest;
  3. public class Human {
  4. }
  5. class Man {
  6. }

1.2 Nested classes (嵌套类)


可以查看 Oracle 官方文档对 Nested Classes 的解释

Java 语言允许在 class 中嵌套定义 class ,以这种方式定义的 class 都归于 Nested classes

其中嵌套类嵌套类,按照不同格式也有如下四种分类

  1. static nested classes 静态嵌套类
  2. inner classes 非静态嵌套类—也叫内部类
  3. local classes 局部嵌套类
  4. anonymous classes 匿名类嵌套类

在 Java 的注释( Class的getEnclosingClass 方法 )里,把 Nested classes 默认规定为 static nested classes

1.2.1 Static nested classes(静态嵌套类)

静态内部类不持有外部类的引用,所以可以直接 new,用法如下:

  1. package classtest;
  2. public class Main {
  3. public static void main(String[] args) {
  4. Human.Man man = new Human.Man();
  5. System.out.println(man.getClass().getName()); // 输出:classtest.Human$Man
  6. }
  7. }
  8. class Human {
  9. // 静态内部类
  10. static class Man {
  11. }
  12. }

1.2.2 Inner classes(内部类)

内部类持有外部类引用,所以可以调用外部类的方法和属性(本质上是通过引用去调用的)。同样也是因为必须持有外部引用,所以其自身是无法定义任何静态属性、静态方法以及静态类的。当然也无法定义接口,因为接口本质上也是静态的。例如如下:

  1. package classtest;
  2. public class Main {
  3. public static void main(String[] args) {
  4. Human.Man man = new Human().new Man();
  5. System.out.println(man.getClass().getName()); // 输出:classtest.Human$Man
  6. }
  7. }
  8. class Human {
  9. class Man {
  10. // 编译器错误:Inner classes cannot have static declarations
  11. static int errorAttribute;
  12. // 编译器错误:Inner classes cannot have static declarations
  13. static void errorFunction(){
  14. }
  15. // 编译器错误:Inner classes cannot have static declarations
  16. interface errorInterface{
  17. }
  18. }
  19. }

1.2.3 Local classes(局部类)

在块(被 { } 包裹的内容)中定义的类都叫做局部类,非 static 块中的局部类持有外部引用,所以也无法定义任何静态属性、静态方法以及静态类的。局部类。而 static 块中的局部类虽然没有持有外部引用,但是也无法定义任何的静态

局部类内无法修改包含它的块中的变量,因为这些变量在局部类使用时,会转变成构造参数传入到内部类中

  1. package classtest;
  2. public class Main {
  3. public static void main(String[] args) {
  4. // 无法实例化局部类
  5. Human human;
  6. }
  7. }
  8. class Human {
  9. static {
  10. // 静态块中定义的局部类,不持有外部类引用
  11. class Man {
  12. void test() {
  13. }
  14. }
  15. }
  16. {
  17. // 构造代码块中定义的局部类,持有外部类引用
  18. class Man {
  19. void test() {
  20. }
  21. }
  22. }
  23. void test() {
  24. // 方法块中定义局部类
  25. int age = 0;
  26. class Man {
  27. void echo() {
  28. System.out.println(name);
  29. System.out.println(age);
  30. }
  31. }
  32. }
  33. }

1.2.4 Anonymous classes(匿名类)

匿名类故名思议就是没有名字的内部类,匿名类和局部类的访问规则一样,可以理解为简化的局部类,不能继承类和接口

  1. class Human {
  2. void test() {
  3. // 匿名类
  4. Callback callback = new Callback() {
  5. };
  6. }
  7. }

2. 如何获取 Class 对象

此节加工自 Oracle 官方文档

2.1 Object.getClass()

如果对象的实例可用,那么获取其 Class 最简单的方法时调用 Object.getClass() 。当然,这只适用于所有继承自 Object 的引用类型。 下面是一些例子

  1. // 获取字符串类型
  2. Class c = "foo".getClass();
  3. // 获取枚举类型
  4. enum E {A,B}
  5. Class c = A.getClass();
  6. //获取数组类型
  7. byte[] bytes = new byte[1024];
  8. Class c = bytes.getClass(); // 值为 [B

2.2 The .class Syntax

如果类型时可用,但是没有实例,那么可以通过 类名称加上 .class 来获得 Class。这也是基本类型获取 Class 最简单的方法。下面是一些例子

  1. // 获取基本类型 boolean 的 Class
  2. Class c = boolean.class;
  3. // 获取多维数组的 Class
  4. Class c = int[][][].class; // 值为 [[[I

2.3 Class.forName()

如果类的完全限定名可用,则可以使用静态方法 Class.forName() 获得相应的 Class。此方法无法获取基本类型。数组类型的 Class 可以通过此方法获得。下面是一些例子

  1. // 基于给定的完全限定名称获取 Class
  2. Class c = Class.forName("com.duke.MyLocaleServiceProvider");
  3. // 获取 double [] 类型的 Class
  4. Class cDoubleArray = Class.forName("[D");
  5. // 获取 String [][] 类型的 Class
  6. Class cStringArray = Class.forName("[[Ljava.lang.String;");

2.4 TYPE Field for Primitive Type Wrappers

通过 .class 是获取基本类型 Class 的最方便和首选的方法。但是还有一种获取方式,就是使用原始类型和 void 的包装类,每个包装类都包含一个名为 TYPE 的字段,该字段等于被包装的基本类型的 Class。下面是一些例子

  1. // 等同于 double.class
  2. Class c = Double.TYPE;
  3. // 等同于 void.class
  4. Class c = Void.TYPE;

2.5 Methods that Return Classes

还有一些是得需要已经获取到了 Class 对象后,通过这个对象获取的,例如获取父类之类的,下面一一列举下

2.5.1 Class.getSuperclass()

获取指定 Class 的父类 ,例如: Class c = String.class.getSuperclass();

2.5.2 Class.getClasses()

返回该类(包含父类)内的公有( public )的内部类、内部接口、内部枚举。例子如下:

  1. Class<?>[] c = Character.class.getClasses();
  2. // 返回三个成员类: Character.Subset 、Character.UnicodeBlock 以及 Character.UnicodeScript

2.5.3 Class.getDeclaredClasses()

返回该类(包含父类)内所有显示声明的内部类、内部接口、内部枚举。例子如下:

  1. Class<?>[] c = Character.class.getDeclaredClasses();
  2. // 返回四个成员类,公有的三个和Class.getClasses()一致,多了个私有的Character.CharacterCache

2.5.4 Class.getDeclaringClass()

获取嵌套类的声明类,如果此类不是嵌套类,则返回为空,如果嵌套类是局部类( local classes )或匿名类( anonymous classes ),则也返回为空。例子如下:

  1. class Human {
  2. // 内部类
  3. class Man {
  4. }
  5. // 静态嵌套类
  6. static class Woman{
  7. }
  8. {
  9. // 局部类
  10. class Local{
  11. }
  12. // 匿名内部类
  13. Local anonymousClass = new Local(){
  14. };
  15. System.out.println(Woman.class.getDeclaringClass()); // 输出:class classtest.Human
  16. System.out.println(Man.class.getDeclaringClass()); // 输出:class classtest.Human
  17. System.out.println(Local.class.getDeclaringClass()); // 输出:null
  18. System.out.println(anonymousClass.getClass().getDeclaringClass()); // 输出:null
  19. System.out.println(Human.class.getDeclaringClass()); // 输出:null
  20. }
  21. }

java.lang.reflect.Field.getDeclaringClass()
java.lang.reflect.Method.getDeclaringClass()
java.lang.reflect.Constructor.getDeclaringClass()

以上三个方法比较直译了,获取字段,方法,构造函数所被声明的类

2.5.5 Class.getEnclosingClass()

获取对应类的直接外部类,如果没有直接外部类,则返回为空。例子如下:

  1. class Human {
  2. class Man {
  3. }
  4. static class Woman{
  5. }
  6. {
  7. class Local{
  8. }
  9. Local anonymousClass = new Local(){
  10. };
  11. System.out.println(Woman.class.getEnclosingClass()); // 输出:class classtest.Human
  12. System.out.println(Man.class.getEnclosingClass()); // 输出:class classtest.Human
  13. System.out.println(Local.class.getEnclosingClass()); // 输出:class classtest.Human
  14. System.out.println(anonymousClass.getClass().getEnclosingClass()); // 输出:class classtest.Human
  15. System.out.println(Human.class.getEnclosingClass()); // 输出:null
  16. }
  17. }

3. Class 常用方法

方法名 说明
forName()
1. 获取 Class 对象的一个引用,如果引用的类还没有加载就加载这个类
1. 为了产生 Class 引用,forName()立即就进行了初始化
getClass() 获取 Class 对象的一个引用,返回标识该对象的实际类型的 Class 引用
getName() 获取全限定的类名(包括包名),即类的完全名称
getSimpleName() 获取类名(不包括包名)
isInterface() 判断 Class 对象是否表示一个接口
getInterfaces() 返回 Class 对象锁引用的类所实现的所有接口
getSuperclass()
newInstance()
getFields()
getDeclaredFields()