11-反射

反射

问: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。

例1
例2

例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语言的运行时绑定(也称为动态绑定或晚期绑定)

例1

问:动态语言

问: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

应用反射的基本步骤:

(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构造方法

例1

具体如何使用反射,请看两篇文章

分享到 评论