现在的位置: 首页 > 综合 > 正文

java反射机制

2013年02月16日 ⁄ 综合 ⁄ 共 10577字 ⁄ 字号 评论关闭
文章目录

1.java反射机制的定义

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

Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。

下面介绍java的反射机制的简单应用。

2.Class类及Class类对象的实例化方式

先看下Java API中对于Class类的描述:

public final class Class<T>

extends Object

implements Serializable, GenericDeclaration, Type, AnnotatedElement

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

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

Class类对象的实例化方式:

 1)类名.class

       Class<?> c1=Person.class;

2)Class类的实例化对象.getClass()(注意Class类的Class类对象和常规的实例对象)

      Person p=new Person();
      Class<?> c2=p.getClass();

3)Class的forName静态方法

      Class<?> c2=Class.forName("reflection.Person");

3.通过反射获取一个类的结构内容

先建立基本的类和接口:

package reflection;

class Father{
 public String getSkill(){
  return "old skill !";
 }
}

interface Info{
 public String getInfo();
 public String say(String name,String content);
 public void sayHello();
}

public class Son extends Father  implements Info{

 /************静态变量************/
 public static String AUTHOR="LCZ";
 
 /************成员变量************/

 public String publicInfo="publicInfo";
 protected String protectedInfo="protectedInfo";
 String defaultInfo="defaultInfo";
 private String name;
 private int age;
 
 /************构造方法************/
 public Son(){
 }
 public Son(String name){
  setName(name);
 }
 public Son(String name,int age){
  setName(name);
  setAge(age);
 }
 
 /************静态方法************/
 public static String staticString(){
  return "static test !";
 }
 
 /************一般方法************/
 public int getAge() {
  return age;
 }
 public void setAge(int age) {
  this.age = age;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getName() {
  return name;
 }

 @Override
 public String say(String name, String content) {
  return name+" has said "+content+".";
 }
 @Override
 public void sayHello() {
  System.out.println("hello!!!");
 }
 @Override
 public String getInfo() {
  return "Info: name-"+name+",age-"+age;
 }
 
 /************特殊方法************/
 public final void f1(){
  f2();
 }
 private void f2(){
 }

}

1)获取父类

  Class<?> c=Class.forName("reflection.Son");
  Class<?> father=c.getSuperclass();
  System.out.println(c);
  System.out.println("father: "+father);
  System.out.println(father.getSuperclass());
  System.out.println(father.getSuperclass().getSuperclass());

输出:

class reflection.Son
father: class reflection.Father
class java.lang.Object
null

说明:

(1)如果没有父类,那getSuperclass方法会返回null。

(2)这里看到Class类的toString方法输出会以class打头,为什么,有必要吗?Class类的实例当然都是class啦?!再看下Java API中的描述“Class 类的实例表示正在运行的 Java 应用程序中的类和接口”,也就是说Class类的实例对象其实,可能是class也可能是interface,所以,要有个前缀标识。

可以看看interface的输出:

  Class<?> c1=Class.forName("reflection.Info");
  Class<?> father1=c1.getSuperclass();
  System.out.println(c1);
  System.out.println("father: "+father1);

输出:

interface reflection.Info
father: null

2)获取接口

  Class<?> c=Class.forName("reflection.Son");
  Class<?> inter[]=c.getInterfaces();
  for(int i=0;i<inter.length;i++){
   System.out.println(inter[i]);
  }

输出:

interface reflection.Info

说明:

获取接口的方法是返回一个数组,而获取父类的方法只会返回一个对象。因为在java中,一个类可以实现多个接口,只能继承一个类。

3)获取构造方法

System.out.println("\n*********************************************");
  System.out.println("constructor:");
  Class<?> c=Class.forName("reflection.Son");
  Constructor<?> con[]=c.getConstructors();
  for(int i=0;i<con.length;i++){
   System.out.println(con[i]);
  }
  
  System.out.println("\n*********************************************");
  System.out.println("constructor.getName():");
  for(int i=0;i<con.length;i++){
   System.out.println(con[i].getName());
  }
  
  System.out.println("\n*********************************************");
  System.out.println("组装输出:");
  for(int i=0;i<con.length;i++){
   int mod=con[i].getModifiers();//包括一个方法的所有修饰符(public static final?)
   String modifier=Modifier.toString(mod);
   String name=con[i].getName();//方法名
   Type type[]=con[i].getParameterTypes();//参数列表
   System.out.print(modifier);
   System.out.print(" "+name+"(");
   for(int j=0;j<type.length;j++){
    System.out.print(type[j]+" arg-"+j);
    if(j<type.length-1) System.out.print(",");
   }
   System.out.println(")");
  }

输出:

*********************************************
constructor:
public reflection.Son(java.lang.String)
public reflection.Son()
public reflection.Son(java.lang.String,int)

*********************************************
constructor.getName():
reflection.Son
reflection.Son
reflection.Son

*********************************************
组装输出:
public reflection.Son(class java.lang.Class arg-0)
public reflection.Son()
public reflection.Son(class java.lang.Class arg-0,class java.lang.Class arg-1)

说明:

(1)通过Constructor的toString方法或getName方法获取的输出都是固定的,如果想获得更丰富的输出,可以自己组装,甚至解析获得的各个部分然后再组装。

(2)Constructor的getModifiers方法返回的是一个int型的值,它是各个修饰符相加的和,但是要显示具体的信息,要用Modifier的静态toString方法。

4)获取普通方法

  System.out.println("直接输出:");
  Class<?> c=Class.forName("reflection.Son");
  Method m[]=c.getMethods();
  
  for(int i=0;i<m.length;i++){
   //System.out.println(m[i].getModifiers());
   System.out.println(m[i]);
  }
  System.out.println("______________________________________________________________\n");
  System.out.println("组装输出:");
  for(int i=0;i<m.length;i++){
   int mod=m[i].getModifiers();
   String modifier=Modifier.toString(mod);//访问修饰符
   //System.out.println("modifier: "+modifier+"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
   Type returnType=m[i].getReturnType();//返回值
   String name=m[i].getName();
   Type paramType[]=m[i].getParameterTypes();//参数列表
   Class<?> exception[]=m[i].getExceptionTypes();//异常列表
   
   System.out.print(modifier+" "+returnType+" "+name+"(");
   for(int j=0;j<paramType.length;j++){
    System.out.print(paramType[j]+" arg-"+j);
    if(j<paramType.length-1) System.out.print(",");
   }
   System.out.print(")");
   if(exception.length>0){
    System.out.print(" throws ");
    for(int k=0;k<exception.length;k++){
     System.out.print(exception[k].getName());
     if(k<exception.length-1) System.out.print(",");
    }
   }
   System.out.println();
  }
输出:

直接输出:
public void reflection.Son.setAge(int)
public static java.lang.String reflection.Son.staticString()
public int reflection.Son.getAge()
public java.lang.String reflection.Son.say(java.lang.String,java.lang.String)
public void reflection.Son.sayHello()
public final void reflection.Son.f1()
public java.lang.String reflection.Son.getSkill()
public java.lang.String reflection.Son.getName()
public void reflection.Son.setName(java.lang.String)
public java.lang.String reflection.Son.getInfo()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
______________________________________________________________

组装输出:
public void setAge(int arg-0)
public static class java.lang.String staticString()
public int getAge()
public class java.lang.String say(class java.lang.String arg-0,class java.lang.String arg-1)
public void sayHello()
public final void f1()
public volatile class java.lang.String getSkill()
public class java.lang.String getName()
public void setName(class java.lang.String arg-0)
public class java.lang.String getInfo()
public final void wait() throws java.lang.InterruptedException
public final void wait(long arg-0,int arg-1) throws java.lang.InterruptedException
public final native void wait(long arg-0) throws java.lang.InterruptedException
public native int hashCode()
public final native class java.lang.Class getClass()
public boolean equals(class java.lang.Object arg-0)
public class java.lang.String toString()
public final native void notify()
public final native void notifyAll()

5)获取成员变量

  Class<?> c=Class.forName("reflection.Son");
  Field field[]=c.getFields();
  for(int i=0;i<field.length;i++){
   System.out.println("Field "+i+":");
   System.out.println("    Field: "+field[i]);
   System.out.println("    name:  "+field[i].getName());
   System.out.println("    value: "+field[i].getType());
  }

输出:

Field 0:
    Field: public static java.lang.String reflection.Son.AUTHOR
    name:  AUTHOR
    value: class java.lang.String
Field 1:
    Field: public java.lang.String reflection.Son.publicInfo
    name:  publicInfo
    value: class java.lang.String

说明:

getField方法返回的都是公共字段(public),如API所说的“这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段”。

其实,getConstructors,getMethods也都是只获取public的,要想获取所有的,API提供了相应的如getDeclaredConstructors,getDeclaredMethods,getDeclaredField,getDeclaredFields等。

4.通过反射使用一个类

 1)反射调用一个类的构造函数,实例化对象

(1)调用无参构造函数

  Class<?> c1=Class.forName("reflection.Son");
  Constructor<?> con1=c1.getConstructor();
  Object o1=con1.newInstance();
  System.out.println(o1);
  System.out.println(((Son)o1).getInfo());

输出:

reflection.Son@61de33
Info: name-null,age-0

(2)调用有参构造函数

  Class<?> c2=Class.forName("reflection.Son");
  Constructor<?> con2=c2.getConstructor(String.class,int.class);
  Object o2=con2.newInstance("ISS",100);
  System.out.println(o2);
  System.out.println(((Son)o2).getInfo());

输出:

reflection.Son@14318bb
Info: name-ISS,age-100

补充说明:

对于getConstructor中的参数,必须跟真正存在的构造函数的参数一致,而且不会自动封装成包装类,如int型,必须写成int.class。

对于newInstance中的参数,它和一般函数的调用一样,可以在基本类型和封装类中自动转换,就如Java API中描述的:

使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。个别参数会自动解包,以匹配基本形参,必要时,基本参数和引用参数都要进行方法调用转换。

如上述的函数调用写成  Object o2=con2.newInstance("ISS",new Integer(100));也可以。

2)反射调用一个类的方法

  //1.无参无返回值
  Class<?> c=Class.forName("reflection.Son");
  Method m1=c.getMethod("sayHello");
  m1.invoke(c.newInstance());

  //2.有参无返回值
  Method m2=c.getMethod("sayHelloTo", String.class);
  m2.invoke(c.newInstance(), "ISS");
  
  //3.有参有返回值
  Method m3=c.getMethod("say", String.class,String.class);
  String o=(String)m3.invoke(c.newInstance(), "ISS","welcome");
  System.out.println(o);

输出:

hello!!!
Hello ISS!
ISS has said welcome.

3)反射调用一个类的属性

  Class<?> c=Class.forName("reflection.Son");
  Object obj=c.newInstance();
  //public变量
  Field f1=c.getField("AUTHOR");
  Field f2=c.getField("publicInfo");
  System.out.println(f1.get(obj));
  System.out.println(f2.get(obj));
  //protected变量
  Field f3=c.getDeclaredField("protectedInfo");
  System.out.println(f3.get(obj));
  //default变量
  Field f4=c.getDeclaredField("defaultInfo");
  System.out.println(f4.get(obj));
  //private变量
  Field f5=c.getDeclaredField("name");
  Field f6=c.getDeclaredField("age");
  f5.setAccessible(true);
  f6.setAccessible(true);
  f5.set(obj, "God");
  f6.set(obj, 10000);
  System.out.println(f5.get(obj));
  System.out.println(f6.get(obj));

输出:

LCZ
publicInfo
protectedInfo
defaultInfo
God
10000

说明:

调用属性的方法中getField只能调用public属性,要调用其它可见域的属性需要使用getDeclaredField方法。

private属性是不能直接访问的,需要先设置其可访问权限。而且一般情况下这种操纵是不推荐的,因为这样影响数据的安全,正确的方法是使用getter和setter。

 

抱歉!评论已关闭.