Java 升级之路(二)类加载机制与反射


Java 升级之路(二)类加载机制与反射JVM类的加载类的初始化通过反射查看类信息使用反射生成对象使用反射调用方法使用反射访问成员变量

JVM

当调用java命令运行某个程序时,该命令将会启动一个Java虚拟机(Java Virtual Machine -> JVM)进程。同一个JVM的所有线程、变量都处于同一个进程里,他们都使用该JVM进程的内存区。

当Java程序运行结束时,JVM进程结束,该进程在内存中的状态将会丢失。

类的加载

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或类初始化。

类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。

通常有如下几种加载方式:

  1. 从本地文件系统加载class文件
  2. JAR包加载class文件
  3. 通过网络加载class文件
  4. 把一个Java源文件动态编译,并执行加载

类的初始化

在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对类变量进行初始化。

由于初始化步骤原因,JVM最先初始化的总是java.lang.Object

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

通过反射查看类信息

每个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。一旦获得了某个类所对应的Class对象之后,程序就可以调用Class对象的方法来获得该对象和该类的信息了。

Class类提供了大量的实例方法来获取该Class对象所对应类的详细信息

下面4个方法用于获取Class对应类所包含的构造器:

Connstructor<T> getConstructor(Class<?>... parameterTypes):返回此Class对象对应类的、带指定形参列表的public构造器

Connstructor<?>[] getConstructors():返回此Class对象对应类的所有构造器

Connstructor<T> getDeclaredConstructor(Class<?>... parameterTypes):返回此Class对象对应类的、带指定形参列表的public构造器,与构造器的访问权限无关

Connstructor<?>[] getDeclaredConstructors():返回此Class对象对应类的所有构造器,与构造器的访问权限无关

 

下面4个方法用于获取Class对应类所包含的方法:

Method getMethod(String name, Class<?>...parameterTypes):返回此Class对象对应类的、带指定形参列表的public方法

Method[] getMethods():返回此Class对象所表示的类的所有public方法

Method getDeclaredMethod(String name, Class<?>...parameterTypes):返回此Class对象对应类的、带指定形参列表的方法,与方法的访问权限无关

Method[] getDeclaredMethods():返回此Class对象所表示的类全部方法,与方法的访问权限无关

 

下面4个方法用于访问Class对应类所包含的成员变量:

Field getField(String name):返回此Class对象对应类的、指定名称的public成员变量

Field[] getFields():返回此Class对象对应类的所有public成员变量

Field getDeclaredField(String name):返回此Class对象对应类的、指定名称的成员变量,与方法的访问权限无关

Field[] getDeclaredFields():返回此Class对象对应类的全部成员变量,与方法的访问权限无关

 

使用反射生成对象

上面提到Class对象可以获得该类里的构造器、方法和成员变量(由Field对象表示),这三个类都位于java.lang.reflect包下,并实现了java.lang.reflect.Member接口。程序可以通过Method对象来执行对应的方法,通过Constructor对象来调用对应的构造器创建实例,通过Field对象直接访问并修改对象的成员变量值。

调用Class对象的newInstance()方法即可创建一个Java对象,如下,会输出当前日期

使用反射调用方法

当获得某个类对应的Class对象后,就可以通过该Class对象的getMethods()getMethod()方法来获取全部方法或指定方法。

每个Method对象对应一个方法,获得Method对象后,程序就可通过该Method来调用它对应的方法。在Method里包含一个invoke()方法

Object invoke(Object obj, Object... args):该方法中的obj是执行该方法的主调,后面的args是执行该方法时传入该方法的实参

当通过Methodinvoke()方法来调用对应的方法时,Java会要求程序必须有调用该方法的权限。如果程序需要调用某个private方法,可以先调用setAccessible()方法:

setAccessible(boolean flag):将Method对象的accessible设置为指定的布尔值。值为true,指示该Mtrhod在使用时应该取消Java语言的访问权限检测;值为false,则需要权限检查

使用反射访问成员变量

上面还提到过getFields()getField()来访问成员变量。Field提供了下面2组方法来读取或设置成员变量值:

getXxx(Object obj):获取obj对象的该成员变量值。此处的Xxx对应8种基本类型,如果该成员变量的类型是引用类型,则取消get后面的Xxx。例如someField.getInt(new Aclass());

setXxx(Object obj, Xxx val):将obj对象的该成员变量设置为val值。此处的Xxx对应8种基本类型,如果该成员变量的类型是引用类型,则取消get后面的Xxx。例如someField.setInt(new Aclass(),1);

一个Demo: