reflect

来源:互联网 发布:服装软件哪个好 编辑:程序博客网 时间:2024/05/18 12:30

一、       Introduction

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

反射机制作用的对象是类。需要了解java中类的加载。在java中,当程序需要使用某个类时,如果该类还没有加载到内存中,则系统会通过加载、连接和初始化来实现对这个类的初始化。加载是指将class文件读入到内存,并为之创建一个class对象。连接,包括3个内容:验证,类是否有正确的内部结构,并和其他类协调一致;准备,负责为类的静态成员分配内存,并设置默认初始化值;解析,将类的二进制数据中的符号引用替换为直接引用;初始化,java虚拟机执行类定义中的代码。类的初始化时机包括以下几个方面:1,创建类的实例时;2,类的静态变量在加载时;3,类的静态方法在加载时;4,使用反射方式强制创建某个类或接口对应的java.lang.Class对象;5,初始化某个类的子类;6,使用java.exe命令运行某个主类。

类的加载由类加载器负责完成。Java中类加载器的组成。

BootstrapClassLoader

根类加载器

也被称为引导类加载器,负责java核心类的加载。比如System,String等。在jdk中jre的lib目录下rt.jar文件中。

ExtensionClassLoader

扩展类加载器

负责jre的扩展目录中jre包的加载。

在jdk中jre的lib目录下ext目录中。

SystemClassLoader

系统类加载器

负载在jvm启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

二、       反射的应用

(一)            获取类的字节码文件对象

要通过反射解剖一个类,必须获取到这个类的字节码文件对象。获取类字节码文件对象的方法有3种。

1.       通过Object类中的getClass方法

2.       通过类名.class获取到字节码文件(任意数据类型都具有一个隐藏的class静态属性)

3.       通过Class类中的forName方法。注意forName中的字符串参数为类的全路径名。

例子:

创建一个测试用的Person类

public class Person {

   public Stringname;

   private intage;

   private Stringaddress;

   //构造方法

      public Person() {

         System.out.println("空参数构造方法");

      }

     

      public Person(Stringname) {

         this.name =name;

         System.out.println("带有String的构造方法");

      }

      //私有的构造方法

      privatePerson(String name, intage){

         this.name =name;

         this.age =age;

         System.out.println("带有Stringint的构造方法");

      }

     

      public Person(Stringname, intage, String address){

         this.name =name;

         this.age =age;

         this.address =address;

         System.out.println("带有String, int,String的构造方法");

      }

     

      //成员方法

      //没有返回值没有参数的方法

      public void method1(){

         System.out.println("没有返回值没有参数的方法");

      }

      //没有返回值,有参数的方法

      public void method2(String name){

         System.out.println("没有返回值,有参数的方法 name= "+name);

      }

      //有返回值,没有参数

      public int method3(){

         System.out.println("有返回值,没有参数的方法");

         return 123;

      }

      //有返回值,有参数的方法

      public String method4(Stringname){

         System.out.println("有返回值,有参数的方法");

         return"哈哈" +name;

      }

      //私有方法

      private void method5(){

         System.out.println("私有方法");

      }

      @Override

      public String toString() {

         return"Person [name=" + name + ", age="+ age+ ", address=" + address+"]";

      } 

}

 

public class GetClass {

   public staticvoidmain(String[] args)throwsClassNotFoundException{

      Person p=new Person();

      Class c1=p.getClass();

      System.out.println("通过object类的getClass方法");

      Class c2=Person.class;

      System.out.println("通过类名.class的方法");

      Class c3=Class.forName("com.hh.reflect.Person");

      System.out.println("通过Class类的forName方法");

   }

}

相比前两种方法,第三种方法不需要使用具体的类,只需要提供相关的字符串就可以。适合按照配置文件加载,具有更强的扩展性。

(二)            通过反射获取构造方法并使用

使用Class类获得构造方法类的对象

java.lang
类 Class<T>

java.lang.Object

  java.lang.Class<T>

类型参数: T - 由此 Class 对象建模的类的类型。例如,String.class的类型是 Class<String>。如果将被建模的类未知,则使用 Class<?>。

所有已实现的接口: Serializable, AnnotatedElement,GenericDeclaration,Type

public final class Class<T>

extends Object

implements Serializable,GenericDeclaration,Type, AnnotatedElement

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

Class 没有公共构造方法。Class对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。

以下示例使用 Class 对象来显示对象的类名:

     voidprintClassName(Object obj) {

        System.out.println("The class of " + obj +

                            " is " +obj.getClass().getName());

     }

还可以使用一个类字面值(JLS Section 15.8.2)来获取指定类型(或 void)的 Class 对象。例如:

    System.out.println("The name of class Foo is:"+Foo.class.getName());

从以下版本开始: JDK1.0

另请参见: ClassLoader.defineClass(byte[],int, int),序列化表格

 

java.lang.reflect
类 Constructor<T>

java.lang.Object

  java.lang.reflect.AccessibleObject

      java.lang.reflect.Constructor<T>

类型参数: T - 在其中声明构造方法的类。

所有已实现的接口: AnnotatedElement, GenericDeclaration,Member

public final class Constructor<T>

extends AccessibleObject

implements GenericDeclaration, Member

Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。

Constructor 允许在将实参与带有底层构造方法的形参的 newInstance() 匹配时进行扩展转换,但是如果发生收缩转换,则抛出 IllegalArgumentException。

另请参见: Member, Class, Class.getConstructors(),Class.getConstructor(Class[]),Class.getDeclaredConstructors()

Method

 Constructor<T>

getConstructor(Class<?>... parameterTypes)
          返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。

 Constructor<?>[]

getConstructors()
          返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。

 Constructor<T>

getDeclaredConstructor(Class<?>... parameterTypes)
          返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。

 Constructor<?>[]

getDeclaredConstructors()
          返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。

例子:

   public voidtest1() throwsException{

      //获取类的字节码文件

      Class c=Class.forName("com.hh.reflect.Person");

      //获得所有的公共的构造方法

      Constructor cons1[]=c.getConstructors();

      for(Constructorcon:cons1){

         System.out.println(con);

      }

      //获得所有的构造方法,不考虑访问修饰符

      Constructor cons2[]=c.getDeclaredConstructors();

      for(Constructorcon:cons2){

         System.out.println(con);

      }

      //获得某一个构造方法,方法中的可变参数就是构造方法的参数

      Constructor con1=c.getConstructor(null);

      System.out.println(con1);

      Constructor con2=c.getConstructor(String.class);

      System.out.println(con2);

      Constructor con3=c.getConstructor(String.class,int.class,String.class);

      System.out.println(con3);

      //不考虑访问修饰符获得构造方法

      Constructor con4=c.getDeclaredConstructor(String.class,int.class);

      System.out.println(con4);

   }

(三)            通过反射的方式,获取构造方法来创建对象

使用Constructor的newInstance方法

 T

newInstance(Object... initargs)
          使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。

获取公共的构造方法创建对象的例子:

   public voidtest2() throwsException{

      Class c=Class.forName("com.hh.reflect.Person");

      Constructor con1=c.getConstructor(null);

      Object o1=con1.newInstance(null);

      System.out.println(o1);

      Constructor con2=c.getConstructor(String.class,int.class,String.class);

      Object o2=con2.newInstance("hh",11,"kk");

      System.out.println(o2);

   }

获得私有的构造方法创建对象的例子:

通过设置类构造方法的Accessible标志为true,取消java语言访问的检查,从而获得访问私有成员的权限。

java.lang.reflect
类 AccessibleObject

java.lang.Object

  java.lang.reflect.AccessibleObject

所有已实现的接口: AnnotatedElement

直接已知子类: Constructor, Field,Method

public class AccessibleObject

extends Object

implements AnnotatedElement

AccessibleObject 类是 Field、Method 和Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。

在反射对象中设置 accessible 标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization 或其他持久性机制)以某种通常禁止使用的方式来操作对象。

从以下版本开始: 1.2

另请参见: Field, Method, Constructor, ReflectPermission

 void

setAccessible(boolean flag)
          将此对象的 accessible 标志设置为指示的布尔值。

例子:

   public voidtest3() throws Exception{

      Class c=Class.forName("com.hh.reflect.Person");

      Constructor con1=c.getDeclaredConstructor(String.class,int.class);

      con1.setAccessible(true);

      Object o1=con1.newInstance("kk",11);

      System.out.println(o1);

   }

(四)            通过反射获得成员变量并使用

成员变量类Field

java.lang.reflect
类 Field

java.lang.Object

  java.lang.reflect.AccessibleObject

      java.lang.reflect.Field

所有已实现的接口: AnnotatedElement, Member

public final class Field

extends AccessibleObject

implements Member

Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。

Array 允许在执行 get 或 set 访问操作期间进行扩展转换,但如果将发生收缩转换,则抛出一个IllegalArgumentException。

另请参见: Member, Class, Class.getFields(),Class.getField(String),Class.getDeclaredFields(),Class.getDeclaredField(String)

通过Class类中的方法

 Field

getField(String name)
          返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。

 Field[]

getFields()
          返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。

 Field

getDeclaredField(String name)
          返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。

 Field[]

getDeclaredFields()
          返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。

例子:

   public voidtest4() throwsException{

      Class c=Class.forName("com.hh.reflect.Person");

      //获得所有公共的成员变量数组

      Field fields[]=c.getFields();

      for(Fieldf:fields){

         System.out.println(f);

      }

      //获得所有的成员变量的数组

      Field fields2[]=c.getDeclaredFields();

      for(Fieldf:fields2){

         System.out.println(f);

      }

      //获得单个公共的成员变量

      Field f1=c.getField("name");

      System.out.println(f1);

      //获得单个私有的成员变量

      Field f2=c.getDeclaredField("age");

      System.out.println(f2);

   }

(五)            通过反射,创建对象,获得指定的成员变量并进行赋值和取值操作

使用Field类的方法

 Object

get(Object obj)
          返回指定对象上此 Field 表示的字段的值。

 void

set(Object obj,Object value)
          将指定对象变量上此 Field 对象表示的字段设置为指定的新值。

例子:

   public voidtest5() throws Exception{

      Class c=Class.forName("com.hh.reflect.Person");

      Constructor con1=c.getDeclaredConstructor(String.class,int.class);

      con1.setAccessible(true);

      Object o=con1.newInstance("会话",13);

      Field nameF=c.getField("name");

      Field ageF=c.getDeclaredField("age");

      Field addressF=c.getDeclaredField("address");

      //取消java语言访问检查

      ageF.setAccessible(true);

      addressF.setAccessible(true);

      System.out.println("name"+nameF.get(o));

      System.out.println("age"+ageF.get(o));

      System.out.println("address"+addressF.get(o));

      //通过fieldset方法给指定的成员变量赋值

      nameF.set(o,"看看");

      ageF.set(o,13);

      addressF.set(o,"");

      System.out.println("name"+nameF.get(o));

      System.out.println("age"+ageF.get(o));

      System.out.println("address"+addressF.get(o));

   }

(六)            通过反射获取成员方法并使用

成员方法类Method

java.lang.reflect
类 Method

java.lang.Object

  java.lang.reflect.AccessibleObject

      java.lang.reflect.Method

所有已实现的接口: AnnotatedElement, GenericDeclaration,Member

public final class Method

extends AccessibleObject

implements GenericDeclaration, Member

Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。

Method 允许在匹配要调用的实参与底层方法的形参时进行扩展转换;但如果要进行收缩转换,则会抛出IllegalArgumentException。

另请参见: Member, Class, Class.getMethods(),Class.getMethod(String,Class[]),Class.getDeclaredMethods(),Class.getDeclaredMethod(String,Class[])

使用Class类的方法

 Method

getDeclaredMethod(String name,Class<?>... parameterTypes)
          返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。

 Method[]

getDeclaredMethods()
          返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

 Method

getMethod(String name,Class<?>... parameterTypes)
          返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。

 Method[]

getMethods()
          返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。

例子:

   public voidtest6() throws Exception{

      Class c=Class.forName("com.hh.reflect.Person");

      //获得所有的公共的成员方法

      Method ms1[]=c.getMethods();

      for(Methodm:ms1){

         System.out.println(m);

      }

      //获得所有的成员方法

      Method ms2[]=c.getDeclaredMethods();

      for(Methodm:ms2){

         System.out.println(m);

      }

      //获得指定的成员方法

      Method m1=c.getMethod("method1",null);

      System.out.println(m1);

      Method m2=c.getMethod("method2", String.class);

      System.out.println(m2);

      //获得指定的私有的成员方法

      Method m3=c.getDeclaredMethod("method5",null);

      System.out.println(m3);

   }

(七)            通过反射创建对象,调用指定的方法

使用Method类的invoke方法

 Object

invoke(Object obj,Object... args)
          对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。

例子:

   public voidtest7() throws Exception{

      Class c=Class.forName("com.hh.reflect.Person");

      Constructor con=c.getConstructor(String.class,int.class,String.class);

      Object o=con.newInstance("看看",14,"kl");

      Method m1=c.getMethod("method4", String.class);

      System.out.println(m1);

      Object o2=m1.invoke(o,"kk");

      System.out.println(o2);

      //调用私有的方法,首先取消java语言访问检查

      Method m2=c.getDeclaredMethod("method5",null);

      m2.setAccessible(true);

      Object o3=m2.invoke(o,null);

      System.out.println(o3);

   }

三、       使用反射进行泛型的擦除

原理是通过反射获得要擦除泛型的方法,然后,使用invoke方法执行这个方法,传入泛型外的参数。

例子:

   public voidtest8() throws Exception{

      ArrayList<Integer> list=new ArrayList();

      list.add(132);

      list.add(newInteger(123));

      //list.add("快快快");

      System.out.println(list);

      //获取ArrayList的字节码文件

      Class c=Class.forName("java.util.ArrayList");

      Method m=c.getMethod("add", Object.class);

      m.invoke(list,"嘿嘿嘿");

      System.out.println(list);

   }

四、       通过配置文件使用反射技术访问类的成员

通过配置文件的方法,提高了程序的扩展性。

例子:

创建配置文件properties.txt

className=com.hh.reflect.Person

methodName=method5

通过配置文件,使用反射访问类

   public voidtest9() throwsException{

      Properties p=new Properties();

      p.load(newFileInputStream("properties.txt"));

      String className=p.getProperty("className");

      System.out.println(className);

      Class c=Class.forName(className);

      Constructor con=c.getConstructor(String.class,int.class,String.class);

      Object o=con.newInstance("会话",13,"");

      System.out.println(o);

      String methodName=p.getProperty("methodName");

      System.out.println(methodName);

      Method m=c.getDeclaredMethod(methodName,null);

      m.setAccessible(true);

      m.invoke(o,null);

   }

 

 

 

 

 

 

原创粉丝点击