🐻Class类
2021-3-8
| 2023-8-3
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
Property
 
除了int等基本类型外,Java的其他类型全部都是class(包括interface),例如:
  • String
  • Object
  • Runnable
  • Exception
  • ...
 
class(包括interface)的本质是数据类型(Type),无继承关系的数据类型无法赋值:
class是由JVM在执行过程中动态加载的。JVM在第一次读取到一种class类型时,将其加载进内存。
每加载一种class,JVM就为其创建一个Class类型的实例,并关联起来。注:这里的Class类型是一个名叫Classclass
 
String类为例,JVM加载String类时,首先读取String.class文件到内存,然后为String类创建一个Class实例并关联起来:
这个Class实例是JVM内部创建的,如果查看JDK源码,可以发现Class类的构造方法是private,只有JVM能创建Class实例,我们自己的Java程序是无法创建Class实例的。
 
所以,JVM持有的每个Class实例都指向一个数据类型(classinterface):
notion image
 
一个Class实例包含了该class的所有完整信息:
notion image
 
由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,就可以通过这个Class实例获取到该实例对应的class的所有信息。
这种通过Class实例获取class信息的方法称为反射(Reflection)
 
如何获取一个classClass实例?有三个方法:
  1. 直接通过一个class的静态变量class获取
    1. 如果有一个实例变量,可以通过该实例变量提供的getClass()方法获取
      1. 如果知道一个class的完整类名,可以通过静态方法Class.forName()获取
        因为Class实例在JVM中是唯一的,所以上述方法获取的Class实例是同一个实例,可以用==比较两个Class实例:
         
        Class实例比较和instanceof的差别:
        instanceof不但匹配指定类型,还匹配指定类型的子类。而用==判断class实例可以精确地判断数据类型,但不能作子类型比较。
        通常情况下应该用instanceof判断数据类型,因为面向抽象编程的时候不关心具体的子类型。只有在需要精确判断一个类型是不是某个class的时候,才使用==判断class实例。
         
        因为反射的目的是为了获得某个实例的信息,因此当拿到某个Object实例时,可以通过反射获取该Objectclass信息:
         
        注意到数组(例如String[])也是一种类,而且不同于String.class,它的类名是[Ljava.lang.String;。此外,JVM为每一种基本类型如int也创建了Class实例,通过int.class访问。
         
        如果获取到了一个Class实例,就可以通过该Class实例来创建对应类型的实例:
        上述代码相当于new String()。通过Class.newInstance()可以创建类实例,它的局限是:只能调用public的无参数构造方法。带参数的构造方法,或者非public的构造方法都无法通过Class.newInstance()被调用。
         
         

        动态加载

        JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载:
        当执行Main.java时,由于用到了Main,因此,JVM首先会把Main.class加载到内存。然而,并不会加载Person.class,除非程序执行到create()方法,JVM发现需要加载Person类时,才会首次加载Person.class。如果没有执行create()方法,那么Person.class根本就不会被加载。
        这就是JVM动态加载class的特性。
         
        动态加载class的特性对于Java程序非常重要。利用JVM动态加载class的特性,才能在运行期根据条件加载不同的实现类。例如,Commons Logging总是优先使用Log4j,只有当Log4j不存在时,才使用JDK的logging。利用JVM动态加载特性,大致的实现代码如下:
        这就是为什么只需要把Log4j的jar包放到classpath中,Commons Logging就会自动使用Log4j的原因。
      2. Java
      3. 异常处理访问字段
        目录