java JDK8 学习笔记——第17章 反射与类加载器

第十七章 反射与类加载器

17.1 运用反射

反射:.class文档反映了类基本信息,从Class等API取得类信息的方式称为反射。

17.1.1 Class与.class文档

1、java.lang.Class的实例代表Java应用程序运行时加载的.class文档,类、接口、Enum等编译过后,都会生成.class文档。Class类没有公开构造函数,实例时候JVM自动产生,每个.class文档加载时,JVM会自动生成对应的Class对象。

2、取得Class对象的方式: 
(1)通过Object的getClass方法 
(2)通过.class常量取得每个对象对应的Class对象 
(3)如果是基本类型,可以使用对应的打包类加上.TYPE取得Class对象

3、默认首次加载.class文档时会执行静态区块。

4、类信息是在编译时期存储在.class文档,这是java支持执行运行时类型识别的方式。编译时期若使用到相关类,编译程序会检查对应的.class文档中记载的信息,以确定是否可完成编译。执行时期使用某类是,会先检查是否有对应的Class对象,如果没有,会加载对应的.class文档并生成对应的Class实例。

5、默认使用getClass()或.class取得的Class实例会是同一个对象。

17.1.2 使用Class.forName()

1、Class.forName()方法实现动态加载类,可用字符串指定类名称来获得类相关信息。

2、Class.forName()另一个版本可以让指定类名称、加载类时是否执行静态区块与类加载器。

static Class forName(String name,boolean initialize,ClassLoader loader)

17.1.3 从Class获得信息

Class对象代表加载的.class文档,取得Class对象后,就可以取得.class文档中加载的信息,像是包、构造函数、方法成员、数据成员等类型。

17.1.4 从Class建立对象

1、事先不知道类名称时,可以利用Class.forName()动态加载.class文档,取得Class对象之后,利用其newInstance()方法建立类实例。

Class clz = Class.forName(args[0]); Object obj = clz.newInstance();

2、若类定义有多个构造函数,也可以指定使用哪个构造函数生成对象,这必须在调用Class的getConstructor()方法时指定参数类型,取得代表构造函数的Constructor对象,利用Constructor的newInstance()指定创建时的参数值来建立对象。

3、生成数组:数组的Class实例是由JVM生成,也可以通过.class或getClass()取得Class实例。生成动态生成长度为10的java.util.ArrayList数组:

Class clz = java.util.ArrayList.class; Object obj = Array.newInstance(clz,10);

4、从第一个索引取得被手机对象实际的Class实例,此时就可以用它配合Array.newInstance()建立数组实例。

17.1.5 操作对象方法与成员

1、可以使用invoke()方法来动态调用指定的方法。

2、调用受把保护或私有方法,可以使用Class的getDeclaredMethod()取得方法,并在调用Method的setAccessible()时指定为true。

17.1.6 动态代理

1、在反射API中有个Proxy类,可动态建立接口的操作对象。

2、使用代理机制有两种代理方式:静态代理和动态代理。

3、静态代理 
在静态代理实现中,代理对象与被代理对象必须实现同一接口,在代理对象中可以实现日志服务,必要时调用被代理对象,,可以这样使用代理对象:

Hello proxy = new HelloProxy(new HelloSpeaker()); proxy.hello("Justin");

创建代理对象HelloProxy时必须指定被代理对象HelloSpeaker,代理对象代理HelloSpeaker执行hello方法。 
静态代理必须为个别接口操作出个别代理类,在应用程序行为复杂时,多个接口必须定义多个代理对象,操作与维护代理对象会有不少的负担。

4、动态代理 
使用动态代理机制,可使用一个处理者代理多个接口的操作对象,处理者类必须操作java.lang.reflect.InvocationHandler接口。 
使用Proxy.newPrxyInstance()方法建立代理对象,调用时必须指定类加载器,告知要代理的接口,以及接口上定义方法被调用时的处理者,Proxy.newPrxyInstance()方法底层会使用原生方式生成代理对象的Class实例,并利用它来生成代理对象。

17.2 了解类加载器

类加载器实际的职责就载入.class文档,JDK本身有默认的类加载器。

17.2.1 类加载器层级架构

1、Bootstrap Loader:产生Exrtended Loader和System Loader 
Exrtended Loader:父加载器为Bootstrap Loader 
System Loader:父加载器为Exrtended Loader

2、Bootstrap Loader通常由C撰写而成。若是Oracle的JDK,Bootstrap Loader会搜索系统参数sun.boot.class.path中指定位置的类,默认是JRE目录的classes中的.class文档,或lib目录中.jar文档里的类。

3、Exrtended Loader由Java撰写而成,会搜索系统参数java.ext.dirs中指定位置的类,默认是JRE目录lib\ext\classes中的.class文档。

4、System Loader由Java撰写而成,会搜索系统参数java.class.path中指定位置的类,也就是CLASSPATH路径,默认是当前工作路径下的.class文档。

5、加载类是会以Bootstrap Loader->Exrtended Loader->System Loader的顺序寻找类,如果所有类加载器都找不到指定类,就会抛出java.lang.NoClassDefFoundError.

6、对null调用getParent()方法会抛出NullPointedException异常。

7、ClassLoader可以使用loadClass()方法加载类,使用localClass方法加载类时,默认不会执行静态区块,真正使用类建立实例时才会执行静态区块。

17.2.2 建立ClassLoader实例

1、使用URLClassLoader来产生新的类加载器,需要java.net.URL作为其参数来指定类加载的搜索路径。使用URLClassLoader的loadClass()方法加载指定类时,会先委托父加载器代为搜索。

2、由同一类加载器载入的.class文档,只会有一个Class实例。如果同一.class文档由两个不同的类加载器载入,则会有两份不同的Class实例。

3、path可以输入不在System Loader以上层级类加载器搜索路径的其他路径。

上一篇:面试题:m个长度为n的ordered array,求top k 个 数字


下一篇:JS(TS)中数组常见的方法(未完待续)