java之反射和内省

 

                                                              --------------        Android培训          java培训     期待与您交流!--------------------------

 

 

Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。

 

 反射的原理,是通过提供java类路径,将java文件读取到JVM中,通过反射提供的实例化机制来操作对象。反射可以访问类下的所有方法 属性

反射机制的优点与缺点    
        为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念,   
    静态编译:在编译时确定类型,绑定对象,即通过。   
    动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多   
    态的应用,有以降低类之间的藕合性。   
    一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中   
    它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编   
    译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如   
    这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能   
    的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功   
    能。   
       它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它
    满足我们的要求。这类操作总是慢于只直接执行相同的操作。

 

 

 

 

四、利用反射机制能获得什么信息    
         一句话,类中有什么信息,它就可以获得什么信息,不过前提是得知道类的名字,要不就没有后文了   
    首先得根据传入的类的全名来创建Class对象。   
    Class c=Class.forName("className");注明:className必须为全名,也就是得包含包名,比如,cn.netjava.pojo.UserInfo;   
    Object obj=c.newInstance();//创建对象的实例   
    OK,有了对象就什么都好办了,想要什么信息就有什么信息了。  
    获得构造函数的方法   
    Constructor getConstructor(Class[] params)//根据指定参数获得public构造器

    Constructor[] getConstructors()//获得public的所有构造器

    Constructor getDeclaredConstructor(Class[] params)//根据指定参数获得public和非public的构造器

    Constructor[] getDeclaredConstructors()//获得public的所有构造器  
    获得类方法的方法   
    Method getMethod(String name, Class[] params),根据方法名,参数类型获得方法

    Method[] getMethods()//获得所有的public方法

    Method getDeclaredMethod(String name, Class[] params)//根据方法名和参数类型,获得public和非public的方法

    Method[] getDeclaredMethods()//获得所以的public和非public方法  
    获得类中属性的方法   
    Field getField(String name)//根据变量名得到相应的public变量

    Field[] getFields()//获得类中所以public的方法

    Field getDeclaredField(String name)//根据方法名获得public和非public变量

    Field[] getDeclaredFields()//获得类中所有的public和非public方法  

 

下面通过一个例子解释下反射

 

01.package chb.test.reflect;  
02.  
03.public class Student {  
04.    private int age;  
05.    private String name;  
06.    public int getAge() {  
07.        return age;  
08.    }  
09.    public void setAge(int age) {  
10.        this.age = age;  
11.    }  
12.    public String getName() {  
13.        return name;  
14.    }  
15.    public void setName(String name) {  
16.        this.name = name;  
17.    }  
18.      
19.    public static void hi(int age,String name){  
20.        System.out.println("大家好,我叫"+name+",今年"+age+"岁");  
21.    }  
22.}


 

JAVA反射的常规使用步骤

    反射调用一般分为3个步骤:

  • 得到要调用类的class
  • 得到要调用的类中的方法(Method)
  • 方法调用(invoke)

 

[Csharp]  view plaincopy
01.Class cls = Class.forName("chb.test.reflect.Student");  
02.Method m = cls.getDeclaredMethod("hi",new Class[]{int.class,String.class});  
03.m.invoke(cls.newInstance(),20,"chb");


方法调用中的参数类型

        在方法调用中,参数类型必须正确,这里需要注意的是不能使用包装类替换基本类型,比如不能使用Integer.class代替int.class。

       如我要调用Student的setAge方法,下面的调用是正确的:

01.Class cls = Class.forName("chb.test.reflect.Student");  
02.Method setMethod = cls.getDeclaredMethod("setAge",int.class);  
03.setMethod.invoke(cls.newInstance(), 15);


 

三、static方法的反射调用

 

       static方法调用时,不必得到对象示例,如下:

 

1.Class cls = Class.forName("chb.test.reflect.Student");  
2.Method staticMethod = cls.getDeclaredMethod("hi",int.class,String.class);  
3.staticMethod.invoke(cls,20,"chb");//这里不需要newInstance  
4.//staticMethod.invoke(cls.newInstance(),20,"chb");<pre></pre>  


四、private的成员变量赋值

    如果直接通过反射给类的private成员变量赋值,是不允许的,这时我们可以通过setAccessible方法解决。代码示例:

 
  1. 1.Class cls = Class.forName("chb.test.reflect.Student");  
    2.Object student = cls.newInstance();//得到一个实例  
    3.Field field = cls.getDeclaredField("age");  
    4.field.set(student, 10);  
    5.System.out.println(field.get(student));<pre></pre>  
    

 

     运行如上代码,系统会报出如下异常:

[
  1. java.lang.IllegalAccessException: Class chb.test.reflect.TestClass can not access a member of class chb.test.reflect.Student with modifiers "private"  
  2.     at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)  
  3.     at java.lang.reflect.Field.doSecurityCheck(Unknown Source)  
  4.     at java.lang.reflect.Field.getFieldAccessor(Unknown Source)  
  5.     at java.lang.reflect.Field.set(Unknown Source)  
  6.     at chb.test.reflect.TestClass.testReflect(TestClass.java:20)<pre></pre>  

    解决方法:

 
  1. 1.Class cls = Class.forName("chb.test.reflect.Student");  
    2.Object student = cls.newInstance();  
    3.Field field = cls.getDeclaredField("age");  
    4.field.setAccessible(true);//设置允许访问  
    5.field.set(student, 10);  
    6.System.out.println(field.get(student));<pre></pre>  
    

    其实,在某些场合下(类中有get,set方法),可以先反射调用set方法,再反射调用get方法达到如上效果,代码示例:

 
 

1.Class cls = Class.forName("chb.test.reflect.Student");  
2.Object student = cls.newInstance();  
3.  
4.Method setMethod = cls.getDeclaredMethod("setAge",Integer.class);  
5.setMethod.invoke(student, 15);//调用set方法  
6.              
7.Method getMethod = cls.getDeclaredMethod("getAge");  
8.System.out.println(getMethod.invoke(student));//再调用get方法<pre></pre


内省(Introspector)是Java 语言对Bean类属性、事件的一种缺省处理方法。例如类 A 中有属性 name, 那我们可以通过 getName,setName 来得到其值或者设置新的值。通过getName/setName 来访问 name 属性,这就是默认的规则。

Java 中提供了一套 API 用来访问某个属性的 getter/setter  方法,通过这些 API 可以使你不需要了解这个规则(但你最好还是要搞清楚),这些 API 存放于包 java.beans 中。

2).直接通过属性的描述器java.beans.PropertyDescriptor类,来访问属性的getter/setter       

public class Point {   
    private Integer x;   
    private Integer y;   
      
    public Point(Integer x, Integer y) {   
        super();   
        this.x = x;   
        this.y = y;   
    }   
      
   public Integer getX() {   
       return x;   
   }      
   public void setX(Integer x) {   
       this.x = x;   
   }   
     
   public Integer getY() {   
       return y;   
   }    
   public void setY(Integer y) {   
       this.y = y;   
   }   
  }   
 
   import java.beans.PropertyDescriptor;   
   import java.lang.reflect.Method;   
     
   public class Reflect {   
     
   public static void main(String[] args) throws Exception {   
       Point point = new Point(2, 5);   
       String proName = "x";   
     
       getProperty(point, proName);   
       setProperty(point, proName);   
   }   
     
   private static void setProperty(Point point, String proName) throws Exception {   
       PropertyDescriptor proDescriptor = new PropertyDescriptor(proName, Point.class);   
       Method methodSetX = proDescriptor.getWriteMethod();   
       methodSetX.invoke(point, 8);   
       System.out.println(point.getX());// 8   
   }   
     
   private static void getProperty(Point point, String proName) throws Exception {   
       PropertyDescriptor proDescriptor = new PropertyDescriptor(proName, Point.class);   
       Method methodGetX = proDescriptor.getReadMethod();   
       Object objx = methodGetX.invoke(point);   
       System.out.println(objx);// 2   
   }   
  }    


     

 

3).通过类 Introspector 来获取某个对象的 BeanInfo信息,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的
getter/setter 方法,然后我们就可以通过反射机制来调用这些方法。

private static void getProperty(Point point, String proName) throws Exception {   
        BeanInfo beanInfo = Introspector.getBeanInfo(point.getClass());   
        PropertyDescriptor[] proDescriptors = beanInfo.getPropertyDescriptors();   
        for(PropertyDescriptor prop: proDescriptors){   
          if(prop.getName().equals(proName)){   
            Method methodGetx = prop.getReadMethod();   
            System.out.println(methodGetx.invoke(point));//8   
            break;   
          }   
       }   
   } 

 

 

4).我们又通常把javabean的实例对象称之为值对象 (Value                      Object),因为这些bean中通常只有一些信息字段和存储方法,没有功能性方法。一个JavaBean类可以不当JavaBean用,而当成普通类 用。JavaBean实际就是一种规范,当一个类满足这个规范,这个类就能被其它特定的类调用。一个类被当作javaBean使用时,JavaBean的 属性是根据方法名推断出来的,它根本看不到java类内部的成员变量(javabean的成员变量通常都是私有private的)。

5).除了反射用到的类需要引入外,内省需要引入的类如下所示,它们都属于java.beans包中的类,自己写程序的时候也不能忘了引入相应的包或者类。

                      import java.beans.BeanInfo;
                      import java.beans.IntrospectionException;
                      import java.beans.Introspector;
                      import  java.beans.PropertyDescriptor;

                      6).下面讲解一些开源的工具类Beanutils,需要额外下载的,commons-beanutils.jar,要使用它还必须导入commons-logging.jar包,不然会出异常;
      

public static void main(String[] args) throws Exception {   
        Point point = new Point(2, 5);   
        String proName = "x";   
        BeanUtils.setProperty(point, proName, "8");   
        System.out.println(point.getX());// 8   
        System.out.println(BeanUtils.getProperty(point, proName));// 8   
        System.out.println(BeanUtils.getProperty(point, proName).getClass().getName());// java.lang.String   
      
        BeanUtils.setProperty(point, proName, 8);   
       System.out.println(BeanUtils.getProperty(point, proName).getClass().getName());// java.lang.String   
   }   
   //我们看到虽然属性x的类型是Integer,但是我们设置的时候无论是Integer还是String,BeanUtils的内部都是当成String来处理的。

BeanUtils支持javabean属性的级联操作;

 public static void main(String[] args) throws Exception {   
     Point point = new Point(2, 5);//在point中加一个属性 private Date birth = new Date();并产生setter/getter方法   
     String proName = "birth";   
     Date date= new Date();   
     date.setTime(10000);   
     BeanUtils.setProperty(point, proName, date);   
     System.out.println(BeanUtils.getProperty(point, proName));   
        
     BeanUtils.setProperty(point, "birth.time", 10000);   
     System.out.println(BeanUtils.getProperty(point, "birth.time"));//10000   
 }   
 //之所以可以 BeanUtils.setProperty(point, "birth.time", 10000);这样写,那是因为Date类中有getTime()和setTime()方法,即Date类中相当于有time这个属性。 


解了相应的原理,那些现成的工具用起来就会更舒服,如Beanutils与 PropertyUtils工具。这两个工具设置属性的时候一个主要区别是PropertyUtils.getPropety方法获得的属性值的类型为该 属性本来的类型,而BeanUtils.getProperty则是将该属性的值转换成字符串后才返回。                    

总结

Web 开发框架 Struts 中的 FormBean就是通过内省机制来将表单中的数据映射到类的属性上,因此要求 FormBean 的每个属性要有 getter/setter方法。但也并不总是这样,什么意思呢?就是说对一个 Bean 类来讲,我可以没有属性,但是只要有 getter/setter 方法中的其中一个,那么 Java   的内省机制就会认为存在一个属性,比如类中有方法 setMobile ,那么就认为存在一个 mobile 的属性。

将 Java 的反射以及内省应用到程序设计中去可以大大的提供程序的智能化和可扩展性。有很多项目都是采取这两种技术来实现其核心功能,例如我们前面提到的 Struts还有用于处理 XML 文件的 Digester项目,其实应该说几乎所有的项目都或多或少的采用这两种技术。在实际应用过程中二者要相互结合方能发挥真正的智能化以及高度可扩展性。


              

 

 

 

 

 

                                                              --------------        Android培训          java培训     期待与您交流!--------------------------


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值