聊聊Java中的反射(一)

本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃

 

  反射reflection主要为了动态操作Java代码,它的主要功能体现在Java提供的reflection library中,在《Java核心技术》中总结反射作用:1、在运行中对类的结构进行动态分析;2、在运行中查看对象;3、实现通用的数组操作;4、利用Method对象。下面分几个章节,结合书中的代码,来仔细看看这四个作用是如何实现的(第一部分是理解反射机制的关键)。

一、类的动态分析

  在Java代码运行时,会为每个对象维护一个类型标识,通过Java中提供的Class类可以访问这些信息。我们知道,在Java中Object类是所有类的父类,Object类中提供了一个getClass()方法,可以返回一个Class类的实例。当你用任意一个类对象来调用这个getClass方法时,返回的Class实例对象就可以标识出类对象的属性。例如:

//根据对象获取类名
Date d = new Date(); Class dateClass = d.getClass(); String dateName = dateClass.getName();
//根据类名获取Class对象,并根据这个Class对象创建类名下的实例对象
try{
  Class cl = Class.forName(dateName);
  Object xx = cl.newInstance();
} cathc(ClassNotFoundException e){
  e.printStackTrace();
}

  Date是java.util常用的日期类,这里定义了一个Date对象d,通过d.getClass()方法获取Class类的实例,该实例包含了Date类的属性,例如类名称、类构造函数、类方法、类变量常量等等信息,通过Class类的getName()方法就可以获取类名,上面String打印出来我们可以看到结果是java.util.Date,是的,返回的类名中还包括了包名。还可以调用Class中的静态方法forName()获得类名对应的Class对象,并根据这个Class对象为类名对应的类创建实例,注意这里会抛出一个异常。

  上面听起来有点绕,打个比方,这个Class类有点像盖房子的设计图,设计图中包含了房子的所有细节信息,我们可以根据房子获取到设计图,从而获得房子的尺寸、房型等等信息,也可以根据设计图再盖一栋一样的房子。

  除了通过getClass和getName方法获取Class对象外,还可以通过类名.class的方法生成Class对象。例如

Class c1 = Date.class;
Class c2 = int.class;
Class c3 = Double[].class;

  注意,在上面的代码中,Class对象实现代表一个类型,不一定是一个类,例如int。我们可以通过==运算实现两个Class对象的比较操作,例如date.getClass()==Date.class。

  知道上面这些内容,我们基本就可以利用反射来分析类的能力了,一个类主要就是三个部分:域(Field,包括常量、变量等)、方法(Method)、构造函数(Constructor构造器),要分析一个类主要就是查看类的三部分构成。我们用reflect库中的三个类:Filed、Method、Constructor来描述类的能力,书中提供了一个打印类全部信息的方法,我会和大家一起仔细阅读这份代码:ReflectionTest.java

public class ReflectionTest{
   public static void main(String[] args){
      // 通过main函数参数列表或者用户输入来读取类名,保存为name
      String name;
      if (args.length > 0) name = args[0];
      else{
         Scanner in = new Scanner(System.in);
         System.out.println("Enter class name (e.g. java.util.Date): ");
         name = in.next();
      }
      try{
         // 打印出类名及其父类名(包括包名)
         Class cl = Class.forName(name);//通过forName()方法获取Class对象
         Class supercl = cl.getSuperclass();//通过getSuperclass()获取父类Class对象
         String modifiers = Modifier.toString(cl.getModifiers());//反射库中的Modifier类,可以获取类或者Field、Method、Constructor的
                                       //public、private、static、final等修饰符描述
if (modifiers.length() > 0) System.out.print(modifiers + " "); System.out.print("class " + name); if (supercl != null && supercl != Object.class) System.out.print(" extends " + supercl.getName()); System.out.print("\n{\n"); printConstructors(cl);//打印类的构造器 System.out.println(); printMethods(cl);//打印类中的方法 System.out.println(); printFields(cl);//打印类中的常量、变量等域信息 System.out.println("}"); }catch (ClassNotFoundException e){//如果没有找到你所输入的类,则返回一个异常 e.printStackTrace(); } System.exit(0); } /** * 打印类的构造器 * @param 参数为Class对象 */ public static void printConstructors(Class cl){ Constructor[] constructors = cl.getDeclaredConstructors();//定义了一个Constructor[]数组保存类的构造器 for (Constructor c : constructors){ String name = c.getName();//获取构造器名称 System.out.print(" "); String modifiers = Modifier.toString(c.getModifiers());//获取构造器的修饰符 if (modifiers.length() > 0) System.out.print(modifiers + " "); System.out.print(name + "("); Class[] paramTypes = c.getParameterTypes();//获取参数的列表 for (int j = 0; j < paramTypes.length; j++){ if (j > 0) System.out.print(", "); System.out.print(paramTypes[j].getName()); } System.out.println(");"); } } /** * 打印类中的所有方法,对比构造器,多了一个返回值类型 * @param 参数为Class对象 */ public static void printMethods(Class cl){ Method[] methods = cl.getDeclaredMethods();//定义一个Method[]数组保存类中的方法 for (Method m : methods){ Class retType = m.getReturnType();//获取Method的返回值类型 String name = m.getName();//获取Method名 System.out.print(" "); String modifiers = Modifier.toString(m.getModifiers());//获取Method的修饰符 if (modifiers.length() > 0) System.out.print(modifiers + " "); System.out.print(retType.getName() + " " + name + "("); // print parameter types Class[] paramTypes = m.getParameterTypes();//获取Method参数列表 for (int j = 0; j < paramTypes.length; j++){ if (j > 0) System.out.print(", "); System.out.print(paramTypes[j].getName()); } System.out.println(");"); } } /** * 打印类中的所有域 * @param 参数为Class对象 */ public static void printFields(Class cl){ Field[] fields = cl.getDeclaredFields();//定义一个Field[]数组保存类的所有域 for (Field f : fields){ Class type = f.getType();//获取域的类型 String name = f.getName(); System.out.print(" "); String modifiers = Modifier.toString(f.getModifiers());//获取域的修饰符 if (modifiers.length() > 0) System.out.print(modifiers + " "); System.out.println(type.getName() + " " + name + ";"); } } }

   我们可以输入任意的类名,如java.util.Date或者你自己定义的某个类,获取该类的能力分析,注意名称不要输入错误,否则将打印一个异常信息。

/************

//二、利用反射分析对象

//  在编写代码时要想查看对象的域名和类型是一件很容易的事情,但是利用反射机制我们可以在编译时查看还不清楚的对象域。查看对象域的关键方法是Field类中的get方法,

 *************/

posted @ 2017-08-19 22:26  负赑屃  阅读(401)  评论(0编辑  收藏  举报