理解Java的反射与内省及其区别

java的内省机制和反射机制什么区别

内省操作只针对JavaBean,只有符合JavaBean规则的类的成员才可以采用内省API进行操作。。。。而反射则不同,一个类的所有成员都可以进行反射操作。

内省和反射的操作也有很大不同,内省是先得到属性描述器PropertyDecriptor后再进行各种操作,反射则是先得到类的字节码Class后再进行各种操作的。

反射(reflection)(实现可扩展性智能化

相对而言,反射比内省更容易理解一点。用一句比较白的话来概括,反射就是让你可以通过名称来得到对象(类,属性,方法)的技术。例如我们可以通过类名来生成一个类的实例;知道了方法名,就可以调用这个方法;知道了属性名就可以访问这个属性的值,还是写两个例子让大家更直观的了解反射的使用方法:

//通过类名来构造一个类的实例
ClassClasscls_str=Class.forName("java.lang.String");
//上面这句很眼熟,因为使用过JDBC访问数据库的人都用过J
Objectstr=cls_str.newInstance();
//相当于Stringstr=newString();  

//通过方法名来调用一个方法
StringmethodName="length";
Methodm=cls_str.getMethod(methodName,null);
System.out.println("lengthis"+m.invoke(str,null));
//相当于System.out.println(str.length());  

上面的两个例子是比较常用方法。看到上面的例子就有人要发问了:为什么要这么麻烦呢?本来一条语句就完成的事情干吗要整这么复杂?没错,在上面的例子中确实没有必要这么麻烦。不过你想像这样一个应用程序,它支持动态的功能扩展,也就是说程序不重新启动但是可以自动加载新的功能,这个功能使用一个具体类来表示。首先我们必须为这些功能定义一个接口类,然后我们要求所有扩展的功能类必须实现我指定的接口,这个规定了应用程序和可扩展功能之间的接口规则,但是怎么动态加载呢?我们必须让应用程序知道要扩展的功能类的类名,比如是test.Func1,当我们把这个类名(字符串)告诉应用程序后,它就可以使用我们第一个例子的方法来加载并启用新的功能。这就是类的反射,请问你有别的选择吗?

应用:Java中利用反射实现类的动态加载

//首先定义一个接口来隔离类:
public interface Operator
{
//    public java.util.List act(java.util.List params);
    public java.util.List act(String content,String content2,java.util.List params);
} 

根据设计模式的原理,我们可以为不同的功能编写不同的类,每个类都继承Operator接口,客户端只需要针对Operator接口编程就可以避免很多麻烦。比如这个类:

import java.util.*;

public class Success implements Operator{

public static void main(String[] args) {
    List list = new ArrayList();
    list.add("Success3");
    Operator op = new Success();
    System.out.println("act===" + op.act("Success1", "Success2", list));
}

//实现接口的方法
//       public java.util.List act(java.util.List params)
public java.util.List act(String content, String content2,java.util.List params) {
    List result = new ArrayList();
    result.add(content);
    result.add(content2);
    result.add(params);
    return result;
}

} 

同样,也可以写另一个类:

import java.util.*;
public class Load implements Operator{

public static void main(String[] args) {
    List list = new ArrayList();
    list.add("Load3");
    Operator op = new Load();
    System.out.println("act===" + op.act("Load1", "Load2", list));
}

//       public java.util.List act(java.util.List params)
public java.util.List act(String content, String content2,java.util.List params)
{
    List result = new ArrayList();
    result.add(content);
    result.add(content2);
    result.add(params);
    return result;
}

}

我们还可以写其他很多类,但是有个问题,接口是无法实例化的,我们必须手动控制具体实例化哪个类,这很不爽,如果能够向应用程序传递一个参数,让自己去选择实例化一个类,执行它的act方法,那我们的工作就轻松多了

很幸运,Java提供这样的反射机制,可以实现我们的无理要求。编写一个配置文件emp.properties:

#成功响应 
1000=Success

#向客户发送普通文本消息
2000=Load

#客户向服务器发送普通文本消息 
3000=Store

文件中的键名是客户将发给我的消息头,客户发送1000给我,那么,我就执行Success类的act方法,类似的如果发送2000给我,那就执行Load类的act方法,这样一来系统就完全符合开闭原则了,如果要添加新的功能,完全不需要修改已有代码,只需要在配置文件中添加对应规则,然后编写新的类,实现act方法就ok,即使我弃这个项目而去,它将来也可以很好的扩展。这样的系统具备了非常良好的扩展性和可插入性。

下面这个例子体现了动态加载的功能,程序在执行过程中才知道应该实例化哪个类

import java.lang.reflect.*;
import java.util.Properties;
import java.io.FileInputStream;
import java.util.List;
//这个程序是针对Operator编程的,所以无需做任何修改,直接提供Load和Store类,

就可以支持2000、3000做参数的调用
//有了这样的反射机制,可以把接口的作用发挥到极至,设计模式也更能体现出威力,

public class TestReflect
{
//加载配置文件,查询消息头对应的类名
private String loadProtocal(String header)
{
    String result=null;
    try
    {
    Properties prop=new Properties();
    //           FileInputStream fis=new FileInputStream("emp.properties");
    //           id = prop.getProperty(idString);
    //           prop.load(fis);
    //           fis.close();
    prop.load(getTCL().getResourceAsStream("emp.properties"));
    result=prop.getProperty(header);
    }catch(Exception e){
        System.out.println(e);
    }
return result;
}

private static ClassLoader getTCL() throws IllegalAccessException,
InvocationTargetException {
    Method method = null;
    try {
    method = Thread.class.getMethod("getContextClassLoader", null);
    } catch (NoSuchMethodException e) {
    return null;
    }
return (ClassLoader)method.invoke(Thread.currentThread(), null);
}

//针对消息作出响应,利用反射导入对应的类
public String response(String header,String content,String content2,List list)
{
    String result=null;
    String s=null;
    try
    {
    /*
    * 导入属性文件emp.properties,查询header所对应的类的名字
    * 通过反射机制动态加载匹配的类,所有的类都被Operator接口隔离
    * 可以通过修改属性文件、添加新的类(继承MsgOperator接口)来扩展协议
    */
    s="org.bromon.reflect."+this.loadProtocal(header).trim();
    //加载类
    System.out.println("s==="+s);//打印 s===org.bromon.reflect.Success
    Class c=Class.forName(s);
    //java.lang.reflect.Methods 是用来描述某个类中单个方法的一个类
    //           Method m[] = c.getDeclaredMethods();//
    //           for (int i = 0; i < m.length; i++)//
    //               System.out.println(m[i].toString());
    //  打印   public java.util.List org.bromon.reflect.Success.act(java.util.List)
    //创建类的事例
    Operator mo=(Operator)c.newInstance();
    System.out.println("mo==="+mo);
    //构造参数列表
    Class params[]=new Class[3];
    //           params[0]=Class.forName("java.util.List");
    params[0]=Class.forName("java.lang.String");
    params[1]=Class.forName("java.lang.String");
    params[2]=Class.forName("java.util.List");
    System.out.println("params[0]==="+params[0]);
    //           //查询act方法
    Method m=c.getMethod("act",params);
    System.out.println("method=="+m.toString());
    Object[] args=new Object[3];
    args[0]=content;
    args[1]=content2;
    args[2]=list;
    //           //调用方法并且获得返回
    Object returnObject=m.invoke(mo,args);//这个地方出问题了,抛异常~~~~
    //           System.out.println("returnObject==="+returnObject);
    List result2 = (List)returnObject;
    result = (String)result2.get(0);
    System.out.println("result2=="+result2);
    //
    }catch(Exception e) {
    System.out.println("Handler-response:"+e);

    //Handler-response:java.lang.IllegalArgumentException: argument type mismatch
    //IllegalArgumentException - 如果该方法是实例方法,且指定对象参数不是声明基础方法的类或接口(或其中的子类或实现程序)的实例;
    //如果实参和形参的数量不相同;如果基本参数的解包转换失败;或者如果在解包后,无法通过方法调用转换将参数值转换为相应的形参类型。
    }
return result;
}

public static void main(String args[])
{
    TestReflect tr=new TestReflect();
    List list = new java.util.ArrayList();
    list.add("测试List");
    tr.response("2000","Load1","Load2",list);//1000是Success,2000是Load
    tr.response("1000","Success1","Success2",list);//1000是Success,2000是Load

}
}    

测试一下,run一下TestReflect类,打印内容有,great!!

result2==[Load1, Load2, [测试List]]
result2==[Success1, Success2, [测试List]]

这个程序是针对Operator编程的,所以无需做任何修改,直接提供Load和Store类,就可以支持2000、3000做参数的调用。

有了这样的反射机制,可以把接口的作用发挥到极至,设计模式也更能体现出威力

内省(Introspector)

内省操作只针对JavaBean,只有符合JavaBean规则的类的成员才可以采用内省API进行操作

内省是Java语言对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新的值。通过getName/setName来访问name属性,这就是默认的规则。Java中提供了一套API用来访问某个属性的getter/setter方法,通过这些API可以使你不需要了解这个规则,这些API存放于包java.beans中。

一般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以调用这些方法。下面我们来看一个例子,这个例子把某个对象的所有属性名称和值都打印出来:

/*
*Createdon2004-6-29
*/  

package demo;  

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

public class IntrospectorDemo{  

String name;  

public static void main(String[]args)throwsException{  
    IntrospectorDemo demo=new IntrospectorDemo();
    demo.setName("WinterLau");  

    //如果不想把父类的属性也列出来的话,
    //那getBeanInfo的第二个参数填写父类的信息
    BeanInfo bi=Introspector.getBeanInfo(demo.getClass(),Object.class);  //通过类        Introspector来获取某个对象的BeanInfo信息
    PropertyDescriptor[] props=bi.getPropertyDescriptors();  //通过BeanInfo来获取    属性的描述器
    for(inti=0;i<props.length;i++){
    //通过这个属性描述器就可以获取某个属性对应的getter/setter方法
    //然后我们就可以通过反射机制来调用这些方法
     System.out.println(props[i].getName()+"="+props[i].getReadMethod().invoke(demo,null));
     }
}
public String getName(){
    return name;
} 

public void setName(Stringname){
  this.name=name;
}
}     

应用:通过内省机制来将表单中的数据映射到类的属性上

Web开发框架Struts中的FormBean就是通过内省机制来将表单中的数据映射到类的属性上,因此要求FormBean的每个属性要有getter/setter方法。但也并不总是这样,什么意思呢?就是说对一个Bean类来讲,我可以没有属性,但是只要有getter/setter方法中的其中一个,那么Java的内省机制就会认为存在一个属性,比如类中有方法setMobile,那么就认为存在一个mobile的属性,这样可以方便我们把Bean类通过一个接口来定义而不用去关心具体实现,不用去关心Bean中数据的存储。比如我们可以把所有的getter/setter方法放到接口里定义,但是真正数据的存取则是在具体类中去实现,这样可提高系统的扩展性。

总结

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

时间: 2024-12-27 09:07:46

理解Java的反射与内省及其区别的相关文章

深入理解java的反射机制

今天将从以下4方面来系统的学习一下java的反射机制: java反射是什么 java反射(Reflection)底层实现原理 java反射的简单演示 java反射的应用场景 1,java反射是什么 首先大家应该先了解两个概念,编译期和运行期,编译期就是编译器帮你把源代码翻译成机器能识别的代码,比如编译器把java代码编译成jvm识别的字节码文件,而运行期指的是将可执行文件交给操作系统去执行,JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够

理解java中反射,区别Class.forName(),Class.forName().instance() ,new

先了解一下反射(这玩意着实让我理解了很久啊)博文参考(http://blog.csdn.net/cookieweb/article/details/7056277) 先了解一些基本的概念:运行时,编译时,编译型,解释型,类加载器,动态加载类  什么是编译?将原程序翻译成计算机语言,就是二进制代码,在java中是将.java文件也就是源程序翻译成.class的字节码  什么是编译时?将原程序翻译成计算机语言的过程中,将.java翻译为.class文件的过程  什么是运行时?就是在启动这个程序的时候

Java反射与内省

反射 用一句比较白的话来概括,反射就是让你可以通过名称来得到对象(类,属性,方法)的技术. 例如我们可以通过类名来生成一个类的实例:知道了方法名,就可以调用这个方法:知道了属性名就可以访问这个属性的值 内省 内省是Java语言对Bean类属性.事件的一种缺省处理方法. JavaBean 属于反射,特殊与一般反射.可以说是一种较为特殊的反射方法,更为快捷的获相应的东西. 例如类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新的值.通过getName/setNam

java的反射

2017年 3月11号 周六 晴 前言:理解java的反射能更好的理解spring的运行机制. 因为spring的两大核心技术是Ioc(Inversion of Control,控制反转,有时也称为依赖注入,即DI,Dependcy Injection)和AOP(Aspect Oriented Programming,面向切面编程,即纵向的编程).Ioc实现原理是java反射机制,Aop实现原理是java的动态代理. java的反射机制概念: java的反射机制是运行状态中,对于任意一个类,都能

深入理解java动态代理的实现机制

今天将从以下5方面来系统的学习一下java动态代理的实现机制: 什么是代理 什么是静态代理 什么是动态代理 动态代理的实现机制 动态代理的使用场景 1,什么是代理 相信大家都有购买过火车票或者机票的经历,有的人在携程买,有的在飞猪,也有的在微信上买等等,这里的携程飞猪微信也好都是受铁路部的委托代理售卖火车票,这里的携程飞猪就是代理类,铁路部就是委托类,这就是代理 2,什么是静态代理 所谓的静态代理就是在代码运行之前,代理类就已经存在,通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生

java的反射机制入门

理解Java的反射机制对学习Java框架有很大帮助,比如Spring框架的核心就是使用Java反射实现的,同时对做一些Java底层操作也会有帮助,比如字节码操作.自定义注解等. 什么是反射 Java反射说的是在运行状态中,对于任何一个类,我们都能够直到这个类有哪些方法和属性.对于任何一个对象,我们都能够对它的方法和属性进行调用.这种动态获取对象信息和调用对象方法的功能就是Java的反射机制. 反射的三种方式 反射其实就是获取类的字节码文件,也就是.class文件,那么我们就可以通过Class这个

深入理解java:1.1.1.反射机制

反射 到底什么是反射(Reflection)呢? 反射有时候也被称为内省(Introspection),事实上,反射,就是一种内省的方式, Java不允许在运行时改变程序结构或类型变量的结构,但它允许在运行时去探知.加载.调用在编译期完全未知的class,可以在运行时加载该class,生成实例对象(instance object),调用method,或对field赋值. 这种类似于"看透"了class的特性被称为反射(Reflection),我们可以将反射直接理解为:可以看到自己在水中

Java关于反射的加深理解

一.反射的基础 java程序中各个java类属于同一类事物,描述这类事物的java类名就是Class 比如说,很多人,用java来表示就用Person类,很多类,就用Class,Person类的实例对象比如张三.李四代表着一个个具体的人,而Class类就代表着各个类在内存中的字节码 一个类被类加载器加载进内存,会占用一片存储空间,这个空间的内容就是类的字节码,不同的类的字节码不同,所以他们在内存中的内容是不同,这些空间分别用一个一个对象来表示,这些对象具有相同的类型,这个类型就是Class 面试

通过案例一步学习理解java反射机制

java 反射机制 以下只是个人在学习反射过程中的笔记,如有错误请指出纠正. 1. 通过例子理解反射 获取类的类类型的三种方法 package Reflect; public class ClassReflect { public static void main(String[] args) { ClassReflect cr = new ClassReflect(); //方法一: Class c = cr.getClass(); //方法二: Class c1 =ClassReflect.