上一篇文章简单说明了java反射机制及其一些基本操作,这篇文章就简单写一下它的应用。
本文地址: http://www.hoverlees.com/blog/?p=539
反射机制可以应用的地方还包括函数跳转表,脚本执行等,函数跳转表常用在多路判断上,以达到线性时间判断的效果。跳转表常用在流解析等应用上,比如说解析xml,解析脚本等,我们可以做一个长度为256的跳转表,每读入一个字节直接跳到指定处理的代码段,从而不用在判断上浪费CPU。用反射机制实现的跳转表,可以使用Method数组实现。当然,跳转表这儿,我推荐的方法是接口数组实现跳转表。例如你定义一个interface包含一个函数,然后定义一个接口数组,每个接口不同的实现即可。
本文的例子是脚本执行,为了让下面的字符串执行起来。
sget stdout,java.lang.System,out String info,=========Output by commands========= invoke r,stdout,println,java.lang.String,info new t1,com.hoverlees.reflect.TestClass invoke can_next,t1,next while can_next invoke r,t1,doit invoke can_next,t1,next endwhile
第一行是命令,第二行是参数。说明我就写在代码里了,就不多说了,直接看代码。
/** * Cmd.java **/ package com.hoverlees.reflect; public final class Cmd{ public String command; public String[] params; }
/** *CommandExecutor.java **/ package com.hoverlees.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Stack; /** * Java反射机制使用例子,执行字符串命令流 * @author hoverlees http://www.hoverlees.com * CommandExecutor 命令执行器 */ public class CommandExecutor { //引擎的全局变量保存到一个HashMap里。 private HashMap<String,Object> global; //while用的Stack private Stack<WhileBlock> whileBlock; public CommandExecutor(){ global=new HashMap<String,Object>(); whileBlock=new Stack<WhileBlock>(); } /** * 执行原子指令的函数,优秀的脚本引擎,应该采用跳转表的方式(内部使用Hash表或查找树实现),节约效率,PHP就是这样实现的 * 但在Java里,函数跳转表可以通过反射机制去实现,为了不把两个例子混到一起,这儿就用if去判断了. * * @param ip 程序位置,脚本引擎的话,应该由解释器提供. * @param command 命令 * @param args 参数列表 * @return 新的程序当前位置(跳转时使用),不跳转时一般返回当前ip. */ public int executeCommand(int ip,String command,String[] args){ Class c; int n; Class[] paramTypes; Object[] params; Object obj; Field f; /** * 判断命令是否需要执行 */ if(!whileBlock.isEmpty()){ if(!whileBlock.peek().state) return ip; //如果在while内但while的条件不为真,此不执行此命令 } //下面两个例子是对基本变量的声明 /** * executeCommand("int","a","8766") * => * int a=8766; */ if(command.equals("int")){ //这儿只是举个参数检查的例子,为了省事儿,下面的命令都不会进行参数检查。 if(args.length!=2) throw new Error("int command must have two parameters."); global.put(args[0], Integer.parseInt(args[1])); } /** * executeCommand("String","name","Hoverlees") * => * String name="hoverlees"; */ else if(command.equals("String")){ global.put(args[0], args[1]); } //下面的例子是变量赋值 /** * executeCommand("set","name","value") * => * name=value; */ else if(command.equals("set")){ global.put(args[0], global.get(args[1])); } //下面实现get,取得对象的公有属性 /** * executeCommand("get","r","obj","pname") * => * r=obj.pname; */ else if(command.equals("get")){ obj=global.get(args[1]); try { f=obj.getClass().getDeclaredField(args[2]); global.put(args[0], f.get(obj)); } catch (Exception e) { e.printStackTrace(); } } //下面实现sget,取得类的公有static属性. /** * executeCommand("sget","r","java.lang.System","out") * => * r=System.out */ else if(command.equals("sget")){ c=getClassByName(args[1]); try { f=c.getDeclaredField(args[2]); global.put(args[0], f.get(null)); } catch (Exception e) { e.printStackTrace(); } } //下面的例子是动态创建类,分别举无参数构造和有参数构造的例子 /** * executeCommand("new","name","java.lang.String") * => * Object name=new String();//无参数构造 * * executeCommand("new","name","java.lang.Integer","int.class",varname) * => * Object name=new Integer(varname); */ else if(command.equals("new")){ n=(args.length-2)/2; //取得参数个数 paramTypes=new Class[n]; params=new Object[n]; for(int i=0;i<n;i++){ paramTypes[i]=getClassByName(args[2+i*2]); //初始化参数类型列表 params[i]=global.get(args[3+i*2]); //初始化参数值列表 } c=getClassByName(args[1]); try{ Constructor ct=c.getConstructor(paramTypes); //取得对应参数的构造函数 global.put(args[0], ct.newInstance(params)); }catch(Exception e){ e.printStackTrace(); global.put(args[0], null); } } //下面的例子是函数调用,其实它是跟调用构造函数差不多的 /** * executeCommand("invoke","r","obj","setType","int.class","vname") * => * r=obj.setType((int)vname); */ else if(command.equals("invoke")){ n=(args.length-3)/2; //取得参数个数 paramTypes=new Class[n]; params=new Object[n]; for(int i=0;i<n;i++){ paramTypes[i]=getClassByName(args[3+i*2]); //初始化参数类型列表 params[i]=global.get(args[4+i*2]); //初始化参数值列表 } c=global.get(args[1]).getClass(); //取得对象所属的类 try{ Method m=c.getDeclaredMethod(args[2], paramTypes); global.put(args[0],m.invoke(global.get(args[1]), params)); }catch(Exception e){ e.printStackTrace(); } } //下面简单实现while(可嵌套) /** * while(vname); * ... * endwhile; */ else if(command.equals("while")){ WhileBlock wb=new WhileBlock(); wb.ip=ip; wb.vname=args[0]; wb.state=global.get(wb.vname).equals(true); whileBlock.push(wb); } else if(command.equals("endwhile")){ WhileBlock wb=whileBlock.peek(); if(global.get(wb.vname).equals(true)){ return wb.ip; //如果条件还满足,那么回到while处继续执行 } else whileBlock.pop(); } //我从来不送人送到西,再后面需要你自己根据需要去加喽:) return ip; } public void debug(String name){ System.out.println(global.get(name)); } private Class getClassByName(String name){ Class c; //因为int等基本数据类型的class不能直接通过Class.forName取得(当然它们属于java.lang.Number),所以这儿可以通过判断返回。 if(name.equals("int.class")) return int.class; else if(name.equals("int[].class")) return int[].class; //...可以在这儿添加其它基本类型 else { try { c=Class.forName(name); } catch (ClassNotFoundException e) { System.out.println(name); e.printStackTrace(); c=null; } } return c; } private final class WhileBlock{ public int ip; public String vname; public boolean state; } }
package com.hoverlees.reflect; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; /** * 测试类,测试脚本执行效果。 **/ public class TestClass { int n; public TestClass(){ n=5; } public TestClass(int num){ n=num; } public int getN(){ return n; } public boolean next(){ n--; return n!=-1; } public void doit(){ System.out.println("N:"+n); } public static void main(String[] args) throws IOException{ System.out.println("=========Output by Java code========="); TestClass t1=new TestClass(); while(t1.next()){ t1.doit(); } CommandExecutor executor=new CommandExecutor(); ArrayList<Cmd> cmds=new ArrayList<Cmd>(); int i=0; //初始化命令流,这个流可以从文件初始化,也可以从你定义的字符串脚本初始化.你甚至可以写和脚本解释器,对于CommandExecutor来说不重要. //我这儿就随便初始化了. /** sget stdout,java.lang.System,out String info,=========Output by commands========= invoke r,stdout,println,java.lang.String,info new t1,com.hoverlees.reflect.TestClass invoke can_next,t1,next while can_next invoke r,t1,doit invoke can_next,t1,next endwhile */ String script= "sget\n" + "stdout,java.lang.System,out\n" + "String\n" + "info,=========Output by commands=========\n" + "invoke\n" + "r,stdout,println,java.lang.String,info\n" + "new\n" + "t1,com.hoverlees.reflect.TestClass\n" + "invoke\n" + "can_next,t1,next\n" + "while\n" + "can_next\n" + "invoke\n" + "r,t1,doit\n" + "invoke\n" + "can_next,t1,next\n" + "endwhile\n\n"; //System.out.println("Script:\n"+script); BufferedReader br=new BufferedReader(new InputStreamReader(new ByteArrayInputStream(script.getBytes()))); String cmd; String params; while((cmd=br.readLine()) != null){ params=br.readLine(); Cmd c=new Cmd(); c.command=cmd; c.params=params.split(","); cmds.add(i++,c); } for(int j=0;j<i;j++){ j=executor.executeCommand(j, cmds.get(j).command, cmds.get(j).params); } } }
让我轻轻地告诉你:
1.访问静态函数/静态变量时,object传递null
2.访问一个类的nested类时,可使用Class.forName(“package.dir.ClassName$NestedClassName”)取得.
这些就不多说了,大家悟悟就知道了.
好厉害的啊,感觉你什么都懂得啊,崇拜