java射射机制及应用(二)

上一篇文章简单说明了java反射机制及其一些基本操作,这篇文章就简单写一下它的应用。

反射机制可以应用的地方还包括函数跳转表,脚本执行等,函数跳转表常用在多路判断上,以达到线性时间判断的效果。跳转表常用在流解析等应用上,比如说解析xml,解析脚本等,我们可以做一个长度为256的跳转表,每读入一个字节直接跳到指定处理的代码段,从而不用在判断上浪费CPU。用反射机制实现的跳转表,可以使用Method数组实现。当然,跳转表这儿,我推荐的方法是接口数组实现跳转表。例如你定义一个interface包含一个函数,然后定义一个接口数组,每个接口不同的实现即可。

本文的例子是脚本执行,为了让下面的字符串执行起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
第一行是命令,第二行是参数。说明我就写在代码里了,就不多说了,直接看代码。
1
2
3
4
5
6
7
8
9
/**
  * Cmd.java
**/
package com.hoverlees.reflect;
  
public final class Cmd{
     public String command;
     public String[] params;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/**
*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;
     }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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”)取得.
这些就不多说了,大家悟悟就知道了. 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值