ch18 类加载机制与反射java
类的加载,链接和初始化数据库
系统可能在第一次使用某个类时加载该类,也可能采用预加载机制来加载某个类数组
JVM和类缓存
一个Java程序就是一个Java虚拟机进程网络
两个JVM之间数据独立,因此一个类的静态属性并不会跨虚拟机进程共享框架
类的加载spa
加载->链接->初始化代理
类加载是指将类的class文件读入内存,并为之建立一个java.lang.Class对象,当程序中使用任何类时,系统都将为之创建一个java.lang.Class对象orm
系统中的全部类实际上也是实例,它们都是java.lang.Class的实例对象
类的加载由类加载器完成,类加载器一般由JVM提供,JVM提供的这些类加载器一般被称为系统类加载器。除此以外,开发者能够经过继承ClassLoader基类来建立本身的类加载器
类的来源:
本地文件系统class
JAR包
网络
动态编译Java文件
类的链接
验证->准备->解析
类的初始化
在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对静态Field进行初始化
Java初始化静态Field:声明时指定初始值或使用静态初始化块
JVM初始化一个类的步骤:
如未加载和链接,则加载并链接该类
如其直接父类未初始化,则先初始化其直接父类
若有初始化语句,则依次执行这些初始化语句
当执行步骤2时,系统对直接父类的初始化步骤也遵循此步骤1~3,依次迭代直到java.lang.Object类。当程序主动
使用任何一个类时,系统会保证该类以及全部父类(包括直接父类和间接父类)都被初始化
类初始化的时机
当Java程序首次经过下面6种方式使用某个类或者接口时,系统就会初始化该类或接口:
建立类的实例 (new,反射,反序列化)
调用某个类的静态方法
访问(读写)某个类或接口的静态Field
使用反射方式强制建立某个类或接口对应的java.lang.Class对象,如Class.forName(“Person”)
初始化某个类的子类
直接使用java.exe运行某个主类
final型静态Field在编译时就能定下来,故不会触发类初始化行为
当使用ClassLoader类的loadClass()方法来加载某个类时,该方法只是加载该类,并不会执行该类的初始化。
使用Class的forName()静态方法才会致使强制初始化该类
类加载器
负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象
类加载器简介
在JVM中,一个类用其全限定类名和其类加载器做为其惟一标识
JVM启动时,会造成由3个类加载器组成的初始类加载器层次结构
Bootstrap ClassLoader:根类加载器(引导类加载器,负责加载Java的核心类)
Extension ClassLoader:扩展类加载器
System ClassLoader:系统类加载器(在JVM启动时加载来自java命令的-classpath选项,java.class.path
系统属性或者CLASSPATH环境变量所指定的JAR包和类路径。程序均可以经过ClassLoader的静态方法getSystemClassLoader()获取系统类加载器)
类加载机制
类型:全盘负责,父类委托,缓存机制
层级关系:用户类加载器->系统类加载器->扩展类加载器->根类加载器
getParent()方法
根类加载器不是由Java实现的
系统类加载器是AppClassLoader的实例,扩展类加载器是ExtClassLoader的实例,这两个类都是URLClassLoader的实例
类加载class的8个步骤
建立并使用自定义的类加载器
JVM中除了根类加载器以外的全部类加载器都是ClassLoader子类的实例
经过扩展ClassLoader子类,重写包含的方法能够实现自定义类加载器
两个关键方法:
loadClass(String name,boolean resolve)
findClass(String name)
推荐重写findClass()方法,而不是loadClass()方法
loadClass()执行步骤:
用findLoadedClass(String)来检查是否已经加载类,若是已经加载则返回;
在父类加载器上调用loadClass()方法。若是父类加载器为null,则使用根类加载器来加载;
调用findClass(String)方法查找类。
核心方法Class defineClass(String name,byte[] b,int off,int len),该方法负责将指定类的字节码文件(class文件)读入字节数组byte[] b内,并把它转换为Class对象
URLClassLoader类
两个构造器建立ClassLoader对象
经过loadClass()方法能够加载指定类
应用:从文件系统加载MySQL驱动,并使用该驱动来获取数据库链接、
经过反射查看类信息
获取Class对象
三种方式:
Class.forName(String),传入包含完整包名的全限定类名称
调用某个类的class属性来获取该类对应的Class对象
调用某个对象的getClass()方法
从Class中获取信息
构造器
Field
方法
Annotation
使用反射生成并操做对象
建立对象
使用Class对象的newInstance()方法来建立该Class对象对应类的实例,要求该Class对象的对应类有默认构造器
使用Class对象获取指定的Constructor对象,再调用Constuctor对象的newInstance()方法来建立该Class对象对应的实例。经过这种方式能够选择使用指定的构造器来建立实例
在不少JavaEE框架中都须要根据配置文件信息来建立Java对象,从配置文件读取的只是某个类的字符串类名,程序须要根据该字符串来建立对应的实例,就必须使用反射
调用方法
geMethods()方法或者getMethod()方法获取所有方法或者指定方法——分别返回Method对象数组或者Method对象
得到Method对象后,就可经过该Method的invoke(Object obj,Object...args)调用相应的方法,obj是执行该方法的主调,后面的args是执行该方法时传入该方法的实参
权限问题:setAccessible(boolean flag),实现经过反射来调用private方法,private构造器和访问private属性
访问属性值
经过Class对象的getFields()或getField()方法能够获取该类所包括的所有Field或指定Field
操做数组
java.lang.reflect包下还提供了一个Array类,Array对象能够表明全部的数组。程序能够经过Array来动态建立数组,操做数组元素等
Object arr=Array.newInstance(String.class,10)
使用反射生成JDK动态代理
使用Proxy和InvocationHandler建立动态代理
Proxy提供了用于建立动态代理类和代理对象的静态方法,它也是全部动态代理类的父类。若是在程序中为一个或多个接口动态的生成实现类。就可使用Proxy来建立动态代理类;若是须要为一个或多个接口动态地建立实例,也可使用Proxy来建立动态代理实例。
系统生成的每一个代理对象都有一个与之关联的InvocationHandler对象,定义一个InvocationHandler实现类须要重写invoke()方法——调用代理对象的全部方法时都会被替换成调用该invoke()方法,Object invoke(Object proxy,Method method,Object[] args)
动态代理和AOP
JDK动态代理只能为接口建立动态代理
采用动态代理能够很是灵活的实现解耦,一般都是为指定的目标对象生成动态代理
这种动态代理在AOP(Aspect Orient Programming)中被称为AOP代理,AOP代理包含目标对象的所有方法,能够替代目标对象。可是两者存在差别:AOP代理里的方法能够在执行目标方法先后插入一些通用处理
反射和泛型
泛型和Class类
使用Class<T>泛型能够避免强制类型转换
对象工厂
使用反射来获取泛型信息
经过指定类对应的Class对象,能够得到类中的全部Field及其类型:
Class<?> a=f.getType()//普通类型
获取泛型类型:Type gType=f.getGenericType()
ParameterizedType对象
getRawType():返回没有泛型信息的原始类型
getActualTypeArguments():返回泛型参数的类型