JavaSE 反射技术

来源:互联网 发布:java监控系统日志抓取 编辑:程序博客网 时间:2024/05/16 06:27

反射技术

一、             反射应用场景&特点

在我们实际开发中会做一些软件,并且这个软件可以正常使用了,后期我们可能出现需要在原有软件上扩展一些功能。要扩展功能,我们都知道需要预留接口。后期需要对这个软件功能进行扩展的话,就需要写个类去实现接口,实现其中的方法。可是问题来了,软件已经正常使用,后期写的类,正常运行的软件无法预知,也无法使用新增加的功能,这时需要修改软件的源码,并且在源码中创建后期所写类的对象,并调用其中的方法。但这种做法不推荐,因为要改动源码,工程量大,并且不利于后期维护。

于是就想到能不能不要改变源码,而是基于接口,后期实现这个接口的程序,都把类名写到指定的配置文件中。这样原有的程序就可以去读取配置文件中的信息,并且根据读取到的信息动态创建类的对象,调用其中的功能呢?

要实现上述的这种功能,就需要使用Java中的反射技术来完成。

通过反射技术,获得配置文件中的类名,并且创建类的对象,调用其中的功能。反射技术的出现提高了程序的扩展性。

 

2、字节码文件对应的类

我们编写任意个Java源文件,在编译后都会生成一个class文件。Java语言是面向对象的语言,Java也对这些class文件进行了对象的描述。把所有的Java源文件编译后生成的class文件,使用Class这个类来描述。

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。

原来在Java中所有的类、接口、数组、基本类型等都是Class类的实例。也就是我们在写一个类的时候,在类中写的成员变量、成员方法、静态方法、构造方法等都被封装成了对象。

二、             获取字节码

1.      获取字节码文件对象——getClass方法

在Object类中描述了一个方法为getClass,任何对象都具备这个方法,这个方法的功能就是获取当前对象所属class文件对象的。

/*

 * 演示获取Class文件对象

 */

publicclass GetClassDemo {

 

      public static void main(String[] args) {

             Person p = newPerson("zhangsan",23);

             Class clazz = p.getClass();

             System.out.println(clazz);

      }

}

2.      获取字节码文件对象——class属性

获取class文件对象第二种方式,任何对象,任意类型,包括基本类型,都拥有静态属性class,同样使用class属性也可以获得class文件对象。

 

      public static void getClass_2() {

             //获取基本类型对应的class文件对象

             Class clazz = int.class;

             System.out.println(clazz);

             //获取自定义类对应的class文件对象

             Class clazz2 = Person.class;

             System.out.println(clazz2);

通过任意类型的静态成员属性class,可以获取到当前类型的class文件对象。这种好处是不用创建对象,直接获取,但是这种方式也要知道具体的类型才能获取。

3.      获取字节码文件对象——forname方法

Class既然是描述class文件对象的类,那么在其中就必定有获取class文件对象的功能。这个功能就是forName。

forName方法在使用的时候需要制定一个字符串对象,这个字符串对象就是class文件的全名称(报名+类名)。

      public static void getClass_3() throwsClassNotFoundException {

            

             String className ="cn.itcast.sh.domain.Person";

            

             Class clazz =Class.forName(className);

             System.out.println(clazz);

            

      }

这种方式的好处:不需要具体的对象,不需要具体类型,只要给定要获取class文件的名称即可。通常给定的class文件名都是通过配置读取而获取到的。

 

4.      动态创建字节码对象所表示的类的对象

在未学习反射技术之前,要使用对象,必须使用new关键字来创建对象,然后在通过对象来调用方法。既然反射技术可以拿到class文件对象,那么就可以创建出class文件所描述的这类事物对象。如何创建这个对象呢?

查阅Class类的描述,在其中有newInstance方法,可以动态创建Class所表的类的实例对象。

 

      /*

       * 要使用Class中的newInstance方法动态创建类的对象,需要先加载这个类的class文件

       */

      public static void getInstance() throwsClassNotFoundException, InstantiationException, IllegalAccessException {

             /*

              * 1、获取需要创建对象的class文件对象,

              * 其实在使用forName方法的时候,会加载指定的class文件

              */

             Class clazz =Class.forName("cn.itcast.sh.domain.Person");

             /*

              * 调用Class类中的newInstance方法获取Person对象。

              * 在调用newInstance方法的时候会调用Person的无参数的构造方法,

              * 如果Person.class文件没有无参数的构造方法,在调用newInstance方法时会发生InstantiationException异常

              * 如果Person.class文件有无参数的构造方法,但是权限不够大,那么会发生IllegalAccessException异常

              */

            

             Object obj = clazz.newInstance();

             System.out.println(obj);

            

             /* Person p = new Person();

              * 1,加载Person类,并将Person类封装成字节码文件对象。

              * 2,通过new创建Person对象。

              * 3,调用构造函数对对象初始化。

              */

     

三、             反射类中的成员

class文件加载进内存后,被封装成Class对象,即就是在Class对象中存放这个class文件的所有信息。通过Class对象,就可以获取class文件中的相关信息,比如class文件中的构造函数,成员变量,成员方法等。

1.      反射构造函数

查阅Class的API描述信息,在其方法的描述中有getConstructor方法,可以获取到对应的构造方法对象,在使用时如果要获取拥有参数的构造方法对象,需要指定构造方法的形式参数类型。

publicclass GetConstructor {

 

      public static void main(String[] args)throws Exception {

             /*

              * 如果要通过指定的构造函数初始化对象怎么办呢?

              * 思路:

              * 1,获取字节码文件对象。

              * 2,再获取给定的构造函数。

              * 3,通过构造函数初始化对象。

              */

             getConstructorDemo();

      }

      public static void getConstructorDemo()throws Exception {        

             String className ="cn.itcast.sh.domain.Person";

             Class clazz =Class.forName(className);

             //获取指定的构造器。获取Person类中两个参数string,int的构造函数。

             Constructor cons =clazz.getConstructor(String.class,int.class);

             //有了构造器对象后,通过构造器对象来初始化给类对象。

             Object obj = cons.newInstance("wangwu",23);

             //Person p = newPerson("lisi",21);

             System.out.println(obj);

      }

}

2.      反射字段&暴力访问

Class类中的getField方法可以获取到class文件对象中公共的成员变量。

publicclass GetField {

      public static void main(String[] args)throws Exception {

             getFieldDemo();

      }

 

      public static void getFieldDemo() throwsException {

            

             String className ="cn.itcast.sh.domain.Person";

             Classclazz = Class.forName(className);

             String fieldName = "age";

             //获取age字段对象。

//          Field field =clazz.getField(fieldName);//获取是公共的字段。

             Field field =clazz.getDeclaredField(fieldName);//可以获取私有的成员字段

            

//          getXXX:获取都是类中公共的成员。

//          getDeclaredXXX:获取本类中已有的成员。

//          System.out.println(field);

            

             //对其进行值的设置,必须先有对象。

             Object obj = clazz.newInstance();

            

             //通过查找父类AccessiableObject的方法。setAccessiable(true);

             field.setAccessible(true);//取消权限检查,暴力访问。一般不访问私有。

             field.set(obj,30);//IllegalAccessException:age字段是私有的。

            

             System.out.println(field.get(obj));

      }

 

}

 

总结:当需要获取class文件中的私有成员,需要使用getDeclaredXxxx,同时也需要使用setAccessible(true)设置取消访问权限,否则无法访问这些私有的成员。

3.      反射方法

同样在Class类中的方法也可以获得class文件对象中的成员方法,已经私有成员方法和静态成员方法,并且通过invoke方法调用方法,让其运行

publicclass GetMethod {

      public static void main(String[] args)throws Exception {

             getMethodDemo2();

      }

      //演示获取Person类中的静态成员方法

      public static void getMethodDemo2() throwsException {

 

             String className ="cn.itcast.domain.Person";

             Class clazz =Class.forName(className);

             String methodName ="staticShow";

 

             Method method =clazz.getMethod(methodName, null);

             method.invoke(null, null);

      }

      //获取Person类中的非静态成员方法

      public static void getMethodDemo() throwsException {

 

             String className ="cn.itcast.domain.Person";

             Class clazz =Class.forName(className);

 

             String methodName ="show";

 

             Method method =clazz.getMethod(methodName, String.class, int.class);

             Object obj = clazz.newInstance();

             method.invoke(obj,"wangcai", 20);

      }

}

0 0