12.1 Java 类加载机制
前面我们给大家介绍过 JVM 的功能(提供运行时环境、垃圾回收机制和提供中立的体
系结构)。在提供运行时环境中有个子功能是 ClassLoader(类加载器),它主要用于将主
类(即包含了 main 方法的类)加载到 JVM 的 code segment(代码区)。然后运行环境找到
main 方法(程序入口)开始执行程序。在整个程序运行的过程中,会有更多的 class 被动态
Load 到内存中。类加载机制如图 12-1 所示:
图 12-1 类加载机制
需要注意的是:类并非一次性就全部加载完毕,而是在需要的时候(运行期间)动态
加载到内存。利用 java -verbose:class Test 可以观察类的具体加载过程。
实例如下:
class A{
static {
System.out.println("A is loaded~");
}
}
public class Test {
static {
System.out.println("主类被加载~");
}
public static void main(String[] args)
{
System.out.println("进入主方法");
try{
Thread.sleep(10000);
}catch(Exception e){
}
new A();
}
}
12.2 Java 反射机制
什么是反射
Java 程序在运行期间可以动态加载、解析和使用一些在编译阶段并不确定的类型数据
这一机制被称为反射(Reflection)。反射库(reflection library)提供了一个非常丰富且精心
设计的工具类,以便编写能够动态操纵 Java 代码的程序。使用反射,在设计和运行中添加
新类时,能够快速的应用开发工具动态的查询新添加类的能力。
反射方式
反射机制提供的功能
·加载运行时才能确定的数据类型
·解析类的结构、分析类的能力、获取其内部信息
·操作类(进行实例化访问非静态成员,直接利用类名访问静态成员)或其实例(访问
属性、调用方法、创建新对象)
12.2.1 Class 类
在程序运行期间,Java 运行时系统始终为所有的对象维护一个被称为运行时的类型标
识,被称为 Class(注意与 class 的区别)。通过 Class 可以完整的得到一个类中的完整结构,
包括此类中的方法定义,属性定义等。Class 是反射的源头或入口,通过查看 JDK 帮助手
册其常见方法如图:
12.2.2 如何获取 Class 类对象
12.2.2.1 针对引用数据类型
·通过 ClassLoader 的 loadClass 方法
Class c1 = ClassLoader.getSystemClassLoader().loadClass("com.itjob.Person");
·调用静态方法 Class.forName()
Class.forName("com.itjob.Person");
Class.forName("oracle.jdbc.dirver.OracleDriver");
·调用 Object 类中定义的 getClass()方法
Person p = new Person();
Class c1 = p.getClass();
Class c2 = "Hello".getClass();
·使用.class 表达式
Class c1 = String.class;
Class c2 = com.itjob.Person.class;
Class c3 = oracle.jdbc.dirver.OracleDriver.class;
12.2.2.2 针对基本数据类型及 void
·使用.class 表达式
Class c1 = int.class;
Class c2 = double.class;
Class c3 = void.class;
·调用相应封装类的 Type 属性
Class c1 = Integer.TYPE;
Class c2 = Double.TYPE;
Class c3 = Void.TYPE;
示例如下:
try {
//1. 引用数据类型
//1.1 利用 ClassLoader 类的 loaderClass("类全名称");手动加载,会有异常
System.out.println(ClassLoader.getSystemClassLoader().loadClass("com.itjob.Pers
on" ).getName());
//1.2 利用 Class.forName(类全名称);手动加载,会有异常
System.out.println(Class.forName("com.itjob.Person").getName());
//1.3 调用 Object 类中定义的 getClass()方法
System.out.println(new Person().getClass().getName());
//1.4 使用.class 表达式
System.out.println(Person.class.getName());
//2. 基本数据类型和 void
//2.1 使用.class 表达式
System.out.println(int.class.getName());
System.out.println(double.class.getName());
System.out.println(void.class.getName());
//2.2 通过各自的包装类.TYPE
System.out.println(Integer.TYPE);
System.out.println(Double.TYPE);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
12.2.3 获取实例对象
不光可以取得对象所在类的信息,也可以直接通过 Class 类的 newInstance 方法进行对
象实例化操作。newInstance 方法原型如下:
public T newInstance() throws InstantiationException,IllegalAccessException
调用过程:
p.getClass().newInstance();
或:
Person.class.newInstance();
或:
Class.forName("com.itjob.Person").newInstance()
创建一个 Person 类的实例。newInstance 方法调用默认的构造器初始化新创建的对象,
如果这个类没有默认的构造器,就会抛出一个异常。
要想调用有参构造方法,则必须使用 Constructor 类的 newInstance 方法。
12.2.4 获取类的结构
Class 类的实例用于表示运行时的 Java 数据类型,包括类、接口、数组、枚举、注解、
基本数据类型。在类加载时,Java 虚拟机会自动创建相应 Class 对象。通过反射得到一个类
的完整结构要使用到 java.lang.reflect 包,此包下的常见类如下:
java.lang.reflect.Field
java.lang.reflect.Method
java.lang.reflect.Constructor<T>
java.lang.reflect.Modifier
java.lang.reflect.Array
12.2.4.1 获取类实现的所有接口
要想取得一个类所实现的全部接口,则必须使用 Class 类中的 getInterfaces()方法。此
方法定义如下:public Class[] getInterfaces()。因为一个类可以同时实现多个接口,因此此
方法返回一个 Class 类的对象数组,之后就可以直接利用 Class 类中的 getName()方法输出
即可。
示例如下:
interface A{
}
interface B{
}
class Person implements A, B{
}
public class Test {
public static void main(String[] args) {
Class[] c = Person.class.getInterfaces();
for(Class cc : c){
System.out.println(cc.getName());
}
}
}
12.2.4.2 获取类所继承的父类
一个类可以实现多个接口,但是只能继承一个父类,如果没有明确的指明继承那个类,
则肯定继承的是 Object 类。所以要想取得一个类的父类,可以直接使用 Class 类中的
getSuperclass()方法。此方法定义如下:public Class<? super T> getSuperclass()。此方法返回
的是 Class 实例,和之前得到接口一样,可以通过 getName()方法取得名称。
示例如下:
class A{
}
class Person extends A{
}
public class Test {
public static void main(String[] args) {
System.out.println(Person.class.getSuperclass().getName());
}
}
12.2.4.3 获取类中的全部构造方法
可以直接使用 Class 类中的 getConstructors()方法或 getDeclaredConstructors()方法取得
本类中的全部构造方法。这两个方法的返回类型都是 Constructor 的数组。
Constructor 类定义在 java.lang.reflect 包中,常用方法如下: