欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

第18章 类加载机制与反射

程序员文章站 2023-12-21 13:11:46
...

1.Java类加载器的原理和机制

(1)JVM和类

当调用java命令运行某个Java程序时,该命令将会启动一个Java虚拟机进程。

(2)类的加载

类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象。

(3)类的连接

当类加载之后,系统为之生成一个对应的Class对象,接着进入连接阶段,负责把类的二进制数据合并到JRE中。

(4)类的初始化

虚拟机负责对类进行初始化,主要是对类变量进行初始化。指定初始值有两种方式:

  1. 声明类变量时指定初始值;
  2. 使用静态初始化块为类变量指定初始值。

JVM初始化一个类的步骤:

  1. 假如这个类还没有被加载和连接,则程序先加载并连接该类
  2. 假如该类的直接父类还没有初始化,则先初始化其直接父类
  3. 假如类中有初始化语句,则系统依次执行这些初始化语句。

对于一个final型的类变量,如果该类变量的值在编译时就可以确定下来,那么这个类变量相当于“宏变量”。

当使用ClassLoader类的loadClass()方法来加载某个类时,该方法只是加载该类,并不会执行该类的初始化,使用Class的forName()静态方法才会导致强制初始化该类。

 

2.类加载器

JVM启动时,初始类加载器由三个类加载器组成:

Bootstrap ClassLoader:根类加载器

Extension ClassLoader:扩展类加载器

System ClassLoader:系统类加载器

JVM类加载器机制主要有三种:

全盘负责、父类委托、缓存机制

JVM的根类加载器并不是Java实现的,而且由于程序通常无须访问根类加载器,因此访问扩展类加载器的父类加载器时返回null。

自定义类加载器:ClassLoader类和URLClassLoader类

除根类加载器之外的所有类加载器都是ClassLoader子类的实例。ClassLoader提供两个关键方法:

loadClass(String name , Boolean resolve):根据指定名称来加载类,获取指定类对应的Class对象

findClass(String name):根据指定名称来查找类

 

3.通过反射查看类信息

程序需要在运行时发现对象和类的真实信息,有两种做法:

1)假设在编译和运行时都完全知道类型的具体信息,在这种情况下,可以先使用instanceof运算符进行判断,再2利用强制类型转换将其转换成其运行时类型的变量即可。

2)编译时根本无法预知该对象和类可能属于哪些类,程序只靠运行时信息来发现该对象和类的真实信息,必须使用反射。

第二种方法的步骤如下:

(1)获得Class对象

每个类被加载之后,系统就会生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。一般采用两种方法:

    1)使用Class类的forName(String clazzName)静态方法,传入字符串参数(是某个类的全限定类名,即添加完整包名)

    2)调用某个类的class属性来获取该类对应的Class对象,例如Person.class

(2)从Class中获取信息

通过Class对象可以得到大量的Method、Constructor、Field等对象。

方法参数反射利用java.lang.reflect包下的Executable抽象类获取指定类的构造器或方法的属性信息。Executable基类提供了isVarArgs()方法用于判断该方法或构造器是否包含数量可变的形参,通过getModifiers()方法来获取该方法或构造器的修饰符。

 

4.使用反射生成并操作对象

Class对象可以获得类里的方法(Method)、构造器(Constructor)、成员变量(Field)。这三个类都位于java.lang.reflect包下,并实现了java.lang.reflect.Member接口。

(1)创建对象

通过反射来生成对象需要先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。具体来说利用指定构造器来创建Java对象,需要如下三个步骤:

  1. 获取该类的Class对象(两种方法)
  2. 利用Class对象的getConstructor()方法来获取指定构造器
  3. 调用Constructor的newInstance()方法来创建Java对象。

如:

Class<?> jframeClazz = Class.forName(“javax.swing.JFrame”);
Constructor ctor = jframeClazz.getConstructor(String.class);
Object boj = ctor.newInstance(“测试窗口”);
System.out.println(obj);

(2)调用方法

通过Class对象的getMethod()方法获取指定方法,返回Method对象。

Method里包含一个invoke()方法,方法签名如下:

Object invoke(Object obj , Object …args):后面的args是执行该方法时传入该方法的实参。

(3)访问成员变量值

通过Class对象的getField()方法获取该类指定成员变量。使用getXxx(Object obj)获取obj对象的成员变量值(如果成员变量的类型是引用类型,则取消get后面的Xxx)和setXxx(Object obj , Xxx val)将obj对象的成员变量设置成val值。包括private修饰的成员变量。

(4)操作数组

使用Array提供的常用方法动态地创建数组,操作数组元素等。

 

5.使用反射生成JDK动态代理

使用Proxy和InvocationHandler创建动态代理

Proxy提供了用于创建动态代理类和代理对象地静态方法,它也是所有动态代理类的父类。

 

6.使用反射来获取泛型信息

当成员变量的类型是有泛型类型的类型,如Map<String , Integer>类型,则不能准确的得到成员变量的泛型参数,可以使用如Type gType = f.getGenericType()获取该成员变量的泛型类型。然后将Type对象强制类型转换为ParameterizedType对象,ParameterizedType代表被参数化的类型,也就是增加了泛型限制的类型。

 

 

上一篇:

下一篇: