理解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项目,其实应该说几乎所有的项目都或多或少的采用这两种技术。在实际应用过程中二者要相互结合方能发挥真正的智能化以及高度可扩展性。
理解Java的反射与内省及其区别的更多相关文章
- 深入理解Java的反射机制
https://blog.csdn.net/u012585964/article/details/52011138 http://www.importnew.com/20339.html 一,java ...
- 七分钟理解 Java 的反射 API
像java一样,一种具有反射功能的语言.允许开发人员在运行时检查类型.方法.字段.注解等,并在程序运行时决定是否使用. 为此,Java的反射API提供类,类,字段,构造函数,方法,注释和其他. 使用它 ...
- 简单理解Java的反射
反射(reflect): JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功 ...
- 利用Java的反射与代理机制实现AOP
在上一篇文章中,我们讲述了利用Java的反射机制中实现Spring中的IOC,在本文中,我们将更进一步,讲述用Java的反射和动态代理机制来实现Spring的AOP. 一.AOP概述 AOP(Aspe ...
- 深入理解java:1.1.1. 反射机制
反射 到底什么是反射(Reflection)呢? 反射有时候也被称为内省(Introspection),事实上,反射,就是一种内省的方式, Java不允许在运行时改变程序结构或类型变量的结构,但它允许 ...
- 深入理解Java反射
要想理解反射的原理,首先要了解什么是类型信息.Java让我们在运行时识别对象和类的信息,主要有2种方式:一种是传统的RTTI,它假定我们在编译时已经知道了所有的类型信息:另一种是反射机制,它允许我们在 ...
- 深入理解Java反射+动态代理
答: 反射机制的定义: 是在运行状态中,对于任意的一个类,都能够知道这个类的所有属性和方法,对任意一个对象都能够通过反射机制调用一个类的任意方法,这种动态获取类信息及动态调用类对象方法的功能称为j ...
- Java中反射机制和Class.forName、实例对象.class(属性)、实例对象getClass()的区别
一.Java的反射机制 每个Java程序执行前都必须经过编译.加载.连接.和初始化这几个阶段,后三个阶段如下图: 其中
- Java 反射和内省实现spring的IOC和DI
1.构造两个JavaBean package com.spring.model; public class People { private Car car; public Car getCar() ...
随机推荐
- 【总结】牛客职播第九期:您的美团点评offer已送到门口,快来与我们一起影响世界!
一.介绍 美团点评2018校园春招流程介绍&面试答疑 讲师:燕鹏.Taylor 二.收获 面试时如果太紧张或者内向,容易吃亏,所以建议把面试当做展示自己的一次舞台. 遇见一道编程题目,如果无法 ...
- adv7180驱动
http://download.csdn.net/download/u013308744/9945184 http://www.ebaina.com/bbs/thread-10121-1-1.html ...
- svn -- 数据备份,版本回退,版本冲突,多仓库配置
数据备份 差异存储法: 版本回退 版本冲突 原理图: 解决办法: 三种方案: 1)合理分配项目开发模块 wangcai:文章,邮件,会员 xiaoqiang:静态化,缓存,前台 2)合理分配项目开发时 ...
- Keystone中间件WSGI环境变量总结
OpenStack keystonemiddleware接收前一个WSGI过滤器传来的WSGI环境信息,进行验证工作后传递给下一个中间件,本文探讨keystone中间件究竟有哪些WSGI环境变量. 说 ...
- 每天一个linux命令:traceroute命令
通过traceroute我们可以知道信息从你的计算机到互联网另一端的主机是走的什么路径.当然每次数据包由某一同样的出发点(source)到达某一同样的目的地(destination)走的路径可能会不一 ...
- int[,] 和 int[][] 有什么区别
int[,] 是二维数组,它就是传统意义上 n x m 的表,和 C++ 里的 int[][] 是一个意思. int[][] 是交错数组,与 C++ 里的 int[][] 不同.它其实是一个 int[ ...
- Nginx配置优化解读
全局配置 Nginx的配置文件是nginx的安装目录的conf/nginx .conf,nginx.conf配置文件中,几个全局高级配置在模块部分之上. user www www; worker_p ...
- linux中chown命令
chown将指定文件的拥有者改为指定的用户或组,用户可以是用户名或者用户ID:组可以是组名或者组ID:文件是以空格分开的要改变权限的文件列表,支持通配符.系统管理员经常使用chown命令,在将文件拷贝 ...
- linux中crontab实战篇
1.先安装crontab,之前的文章有介绍 2.查看 crontab -l 3.编辑 crontab -e 0 7 * * * /application/php/bin/php www.xialan. ...
- ping telnet ssh netstat
1.pingping命令工作在OSI参考模型的第三层-网络层.ping命令会发送一个数据包到目的主机,然后等待从目的主机接收回复数据包,当目的主机接收到这个数据包时,为源主机发送回复数据包,这个测试命 ...