【搞定Java基础】 13 Java 类型信息(Class对象)与反射机制

https://blog.csdn.net/pcwl1206/article/details/86556091

目录

1、深入理解 Class 对象

1.1、RRTI 的概念以及 Class 对象作用

1.2、Class 对象的加载及其获取方式

1.2.5  关于类型转换的问题

1.2.6  instanceof 关键字与 isInstance 方法

2、理解反射技术:反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。

2.1、Constructor 类及其用法

2.2、Field 类及其用法

2.3、Method类及其用法

2.4、反射包中的 Array 类


字面常量就是一个确定的值本身
有名常量,即是把常量 赋给这个名字
int a= 1
1是字面量
然后这个字面量有个名字a,就是有名常量

1、深入理解 Class 对象

1.1、RRTI 的概念以及 Class 对象作用

RTTI(Run-Time Type Identification)运行时类型识别,其作用是在运行时识别一个对象的类型和类的信息,这里分两种:传统的”RRTI”,它假定我们在编译期已知道了所有类型(在没有反射机制创建和使用类对象时,一般都是编译期已确定其类型,如 new 对象时该类必须已定义好),另外一种是反射机制,它允许我们在运行时发现和使用类型的信息。在 Java 中用来表示运行时类型信息的对应类就是 Class 类,Class 类也是一个实实在在的类,存在于 JDK 的 java.lang 包中

1、Class 类也是类的一种,与 Class 关键字是不一样的。

2、手动编写的类被编译后会产生一个 Class 对象,其表示的是创建的类的类型信息,而且这个 Class 对象保存在同名.class 的文件中(字节码文件),比如:创建一个 Shapes 类,编译 Shapes 类后就会创建其包含 Shapes 类相关类型信息的 Class 对象,并保存在 Shapes.class 字节码文件中。

3、每个通过关键字 Class 标识的类,在内存中有且只有一个与之对应的 Class 对象来描述其类型信息,无论创建多少个实例对象,其依据的都是用一个 Class 对象。

4、Class 类只存在私有构造函数,因此对应 Class 对象只能有 JVM 创建和加载。

5、Class 类的对象作用是:运行时提供或获得某个对象的类型信息,这点对于反射技术很重要(关于反射稍后分析)。

1.2、Class 对象的加载及其获取方式

1、获取 Class 对象引用的方式 3 种,通过继承自 Object 类的 getClass 方法,Class 类的静态方法 forName 以及字面常量的方式”.class”。[字面常量就是一个确定的值本身]

2、其中实例类的 getClass 方法和 Class 类的静态方法 forName 都将会触发类的初始化阶段,而字面常量获取 Class 对象的方式则不会触发初始化。[我们获取字面常量的 Class 引用时,触发的应该是加载阶段,因为在这个阶段 Class 对象已创建完成,获取其引用并不困难,而无需触发类的最后阶段初始化; 编译期静态常量也不会触发初始化阶段]

3、初始化是类加载的最后一个阶段,也就是说完成这个阶段后,类也就加载到内存中(Class 对象在加载阶段已被创建),此时可以

对类进行各种必要的操作了(如 new 对象,调用静态成员等),注意在这个阶段,才真正开始执行类中定义的 Java 程序代码或者字节码。

关于类加载的初始化阶段,在虚拟机规范严格规定了有且只有 5 种场景必须对类进行初始化:

1、使用 new 关键字实例化对象时、读取或者设置一个类的静态字段(不包含编译期常量)以及调用静态方法的时候,必须触发类加载的初始化过程(类加载过程最终阶段)。

2、使用反射包(java.lang.reflect)的方法对类进行反射调用时,如果类还没有被初始化,则需先进行初始化,这点对反射很重要。

3、当初始化一个类的时候,如果其父类还没进行初始化则需先触发其父类的初始化。

4、当 Java 虚拟机启动时,用户需要指定一个要执行的主类(包含 main 方法的类),虚拟机会先初始化这个主类。

5、当使用 JDK 1.7 的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后解析结果为:REF_getStatic、REF_putStatic、REF_invokeStatic 的方法句柄,并且这个方法句柄对应类没有初始化时,必须触发其初始化(这点看不懂就算了,这是 JDK1.7 的新增的动态语言支持,其关键特征是它的类型检查的主体过程是在运行期而不是编译期进行的,这是一个比较大点的话题,这里暂且打住)。

1.2.5  关于类型转换的问题

之所可以强制转换,这得归功于 RRTI,要知道在 Java 中,所有类型转换都是在运行时进行正确性检查的利用 RRTI 进行判断类型是否正确从而确保强制转换的完成,如果类型转换失败,将会抛出类型转换异常。

1.2.6  instanceof 关键字与 isInstance 方法

事实上 instanceOf 与 isInstance 方法产生的结果是相同的。对于 instanceOf 是关键字只被用于对象引用变量,检查左边对象是不是右边类或接口的实例化。如果被测对象是 null 值,则测试结果总是 false。一般形式:

而 isInstance 方法则是 Class 类的 Native 方法,其中 obj 是被测试的对象或者变量,如果 obj 是调用这个方法的 class或接口的实例,则返回 true。如果被检测的对象是 null 或者基本类型,那么返回值是 false。一般形式如下

public void cast2(Object obj){
    // instanceof关键字
    if(obj instanceof Animal){
        Animal animal= (Animal) obj;
    }

    // isInstance方法
    if(Animal.class.isInstance(obj)){
        Animal animal= (Animal) obj;
    }
}

2、理解反射技术反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。

一直以来反射技术都是 Java 中的闪亮点,这也是目前大部分框架(如 Spring / Mybatis 等)得以实现的支柱。在 Java中,Class 类与 java.lang.reflect 类库一起对反射技术进行了全力的支持。在反射包中,我们常用的类主要有 Constructor 类表示的是 Class 对象所表示的类的构造方法,利用它可以在运行时动态创建对象、Field 表示 Class 对象所表示的类的成员变量,通过它可以在运行时动态修改成员变量的属性值(包含 private)、Method 表示 Class 对象所表示的类的成员方法,通过它可以动态调用对象的方法(包含 private)。下面将对这几个重要类进行分别说明。

2.1、Constructor 类及其用法

利用好 Class 类和 Constructor 类,我们可以在运行时动态创建任意对象,从而突破必须在编译期知道确切类型的障碍。

2.2、Field 类及其用法

Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。

如果我们不期望获取其父类的字段,则需使用 Class 类的 getDeclaredField / getDeclaredFields 方法来获取字段即可,倘若需要连带获取到父类的字段,那么请使用 Class 类的 getField / getFields,但是也只能获取到 public 修饰的的字段,无法获取父类的私有字段。

2.3、Method类及其用法

Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息,所反映的方法可能是类方法或实例方法(包括抽象方法)

// 获取所有的public方法,包含继承自父类的方法
Method[] methods = clazz.getMethods();
for(Method m1 : methods){
    System.out.println("m1:" + m1);
}
// 获取当前类的所有方法包含private,该方法无法获取继承自父类的method
Method[] methods2 = clazz.getDeclaredMethods();
for(Method m2 : methods2){
    System.out.println("m2:" + m2);
}
// 对私有无参方法的操作
        Method method1 = clazz.getDeclaredMethod("drawCircle");
// 修改私有方法的访问标识
        method1.setAccessible(true);
        method1.invoke(circle);

在上述代码中调用方法,使用了Method类的 invoke(Object obj,Object... args) 第一个参数代表调用的对象,第二个参数传递的调用方法的参数。这样就完成了类方法的动态调用。

2.4、反射包中的 Array 类

在 Java 的 java.lang.reflect 包中存在着一个可以动态操作数组的类,Array,它提供了动态创建和访问 Java 数组的方法。Array 允许在执行 get 或 set 操作进行取值和赋值。在 Class 类中与数组关联的方法是:

猜你喜欢

转载自blog.csdn.net/ZHAOJING1234567/article/details/89318464