反射
问:Classloader
Java中类的加载是由ClassLoader和它的子类来实现的,它负责在运行时查找和装入类文件中的类。
ClassLoader是一个重要的Java运行时系统组件,它主要包括:引导类加载器(BootStrap ClassLoader)、扩展类加载器(Extension ClassLoader)、应用类加载器(Application ClassLoader)和用户自定义类加载器(java.lang.ClassLoader的子类)。
- Bootstrap ClassLoader:它负责加载放在
\jre\lib/目录中的,或者-Xbootclasspath参数所指定路径中的Java核心库(如rt.jar),是用原生代码来实现的; - Extension ClassLoader:它负责加载
\jre\lib\ext目录中,或系统变量java.ext.dirs所指定路径中的所有类库; - Application ClassLoader:它负责加载Java应用的CLASSPATH所指定的类库。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它是应用最广泛的类加载器。开发者可以直接使用这个类加载器,如果应用程序中没有自定义自己的类加载器,一般情况下这个就是程序中默认的类加载器;
- Custom ClassLoader:应用程序根据自身需要自定义的ClassLoader,是java.lang.ClassLoader的子类,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader。
例3:能不能自己写个类,也叫java.lang.String?
可以,但在应用的时候,需要用自己的类加载器去加载,否则,系统的类加载器永远只是去加载rt.jar包中的那个java.lang.String。
由于在tomcat的web应用程序中,都是由webapp自己的类加载器先自己加载WEB-INF/classess目录中的类,然后才委托上级的类加载器加载。
如果我们在tomcat的web应用程序中写一个java.lang.String,这时候Servlet程序加载的就是我们自己写的java.lang.String,
但是这么干就会出很多潜在的问题,原来所有用了java.lang.String类的都将出现问题。
问:JVM的类加载机制 (还不怎么懂,暂时保留)
Java类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中准备、验证、解析3个部分统称为连接(Linking)。
图 1
加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班地开始,而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运行时绑定(也称为动态绑定或晚期绑定)
问:动态语言
问:Class对象
Java虚拟机有一个运行时数据区,这个数据区又被分为方法区,堆区和栈区等,其中方法区的主要作用是存储被JVM加载的类信息、常量、静态变量等。当JVM加载某个类的时候,需要类加载器定位相应的class文件,然后将其读入到JVM,紧接着JVM提取class中的类型信息,将这些信息存储到方法区中。
在Java的世界里,一切皆对象。从某种意义上说,在Java中有两种对象:实例对象和Class对象。实例对象就是我们平常定义的一个类的实例,例如定义一个类Person,然后使用new Person()定义Person类的一个实例对象;而Class对象是没办法用new关键字得到的,每当加载一个新的类的时候,JVM都会在Java堆中创建一个对应于该类的Class对象,该对象就代表该类,通过该Class对象我们就可以访问该类的基本信息。
获取Class对象一般有三种方式
(1) 通过类对象的getClass()方法获取
public class test {
public static void main(String[] args) {
Dog dog = new Dog();
Class clazz = dog.getClass();
}
}
(2) 通过类的.class属性获取
public class test {
public static void main(String[] args) {
Class clazz = Dog.class;
}
}
通过这种方式时,只会加载Dog类,并不会触发Dog类构造器的初始化。
(3) 使用Class.forName(String className)动态加载类,className需要是类的全限定名(最常用)
public class ClassTest {
public static void main(String[] args) {
try {
Class clazz = Class.forName("packageName.Dog");
} catch (ClassNotFoundException e) {}
}
}
问:反射机制
所谓的反射是Java语言在运行时拥有一项自观的能力,反射使您的程序代码能够得到装载到JVM中的类的内部信息,允许您执行程序时才得到所需类的内部信息,而不是在编写代码的时候就必须要知道所需类的内部信息。这样,在程序运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够访问它的任意一个属性和方法;这种动态获取信息以及动态调用对象的方法的功能称为Java语言的反射机制。
问:反射机制的基本使用
Java反射机制的实现要借助于4个类:Class,Constructor,Field,Method;其中Class代表的是类的Class对象,Constructor是代表类构造方法的对象,Field是代表类属性的对象,Method是代表类方法的对象,通过这四个对象我们可以粗略的看到一个类的各个组成部分。其中最核心的就是Class对象,它是实现反射的基础。
反射机制的作用
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理
Spring、Struts、Hibernate等框架都是通过类的反射进行开发的。
应用反射的基本步骤:
(1) 获得你想操作的类的Class对象;
(2) 调用Class对象中的方法获取你想要的信息集合,如调用getDeclaredFields()方法得到类的所有属性;
(3) 处理第2步中得到的信息,然后进行你想做的实际操作。
Class对象的常用方法
(1) 获得类的构造方法
- Constructor getConstructor(Class[] parameterTypes)
- Constructor[] getConstructors()
- Constructor getDeclaredConstructor(Class[] parameterTypes)
- Constructor[] getDeclaredConstructors()
(2) 获得类的字段信息
- Field getField(String name)
- Field[] getFields()
- Field getDeclaredField(String name)
- Field[] getDeclaredFields()
(3) 获得类的方法信息
- Method getMethod(String name, Class[] parameterTypes)
- Method[] getMethods()
- Method getDeclaredMethod(String name, Class[] parameterTypes)
- Method[] getDeclaredMethods()
注意:
(1) getFields()与getDeclaredFields()区别
- getFields() 只能返回类中所有的公有属性,包括从父类继承的公有属性;
- getDeclaredFields() 返回类中声明的所有属性,包括public/private/protect/default属性,不包括从父类继承的属性
(2) getMethods()与getDeclaredMethods()区别
- getMethods() 只能返回类中所有的公有方法,包括从父类继承的公有方法;
- getDeclaredFields() 返回类中声明的所有方法,包括public/private/protect/default方法,不包括从父类继承的方法
(3) getConstructors()与getDeclaredConstructors()区别
- getConstructors() 只能返回类中所有的公有构造方法
- getDeclaredConstructors() 返回类中声明的所有构造方法,包括public/private/protect/default构造方法
具体如何使用反射,请看两篇文章