java射射机制及应用(二)

来源:互联网 发布:ubuntu挂载硬盘到home 编辑:程序博客网 时间:2024/04/30 19:39
上一篇文章简单说明了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{
    publicString command;
    publicString[] 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里。
    privateHashMap<String,Object> global;
    //while用的Stack
    privateStack<WhileBlock> whileBlock;
    publicCommandExecutor(){
        global=newHashMap<String,Object>();
        whileBlock=newStack<WhileBlock>();
    }
    /**
     * 执行原子指令的函数,优秀的脚本引擎,应该采用跳转表的方式(内部使用Hash表或查找树实现),节约效率,PHP就是这样实现的
     * 但在Java里,函数跳转表可以通过反射机制去实现,为了不把两个例子混到一起,这儿就用if去判断了.
     *
     * @param ip 程序位置,脚本引擎的话,应该由解释器提供.
     * @param command 命令
     * @param args 参数列表
     * @return 新的程序当前位置(跳转时使用),不跳转时一般返回当前ip.
     */
    publicint executeCommand(intip,String command,String[] args){
        Class c;
        intn;
        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 newError("int command must have two parameters.");
            global.put(args[0], Integer.parseInt(args[1]));
        }
        /**
         * executeCommand("String","name","Hoverlees")
         * =>
         * String name="hoverlees";
         */
        elseif(command.equals("String")){
            global.put(args[0], args[1]);
        }
        //下面的例子是变量赋值
        /**
         * executeCommand("set","name","value")
         * =>
         * name=value;
         */
        elseif(command.equals("set")){
            global.put(args[0], global.get(args[1]));
        }
        //下面实现get,取得对象的公有属性
        /**
         * executeCommand("get","r","obj","pname")
         * =>
         * r=obj.pname;
         */
        elseif(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
         */
        elseif(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);
         */
        elseif(command.equals("new")){
            n=(args.length-2)/2;//取得参数个数
            paramTypes=newClass[n];
            params=newObject[n];
            for(inti=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);
         */
        elseif(command.equals("invoke")){
            n=(args.length-3)/2;//取得参数个数
            paramTypes=newClass[n];
            params=newObject[n];
            for(inti=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;
         */
        elseif(command.equals("while")){
            WhileBlock wb=newWhileBlock();
            wb.ip=ip;
            wb.vname=args[0];
            wb.state=global.get(wb.vname).equals(true);
            whileBlock.push(wb);
        }
        elseif(command.equals("endwhile")){
            WhileBlock wb=whileBlock.peek();
            if(global.get(wb.vname).equals(true)){
                returnwb.ip; //如果条件还满足,那么回到while处继续执行
            }
            elsewhileBlock.pop();
        }
  
        //我从来不送人送到西,再后面需要你自己根据需要去加喽:)
        returnip;
    }
    publicvoid debug(String name){
        System.out.println(global.get(name));
    }
    privateClass getClassByName(String name){
        Class c;
        //因为int等基本数据类型的class不能直接通过Class.forName取得(当然它们属于java.lang.Number),所以这儿可以通过判断返回。
        if(name.equals("int.class"))return int.class;
        elseif(name.equals("int[].class"))return int[].class;
        //...可以在这儿添加其它基本类型
        else{
            try{
                c=Class.forName(name);
            }catch (ClassNotFoundException e) {
                System.out.println(name);
                e.printStackTrace();
                c=null;
            }
        }
        returnc;
    }
    privatefinal class WhileBlock{
        publicint ip;
        publicString vname;
        publicboolean 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 {
    intn;
    publicTestClass(){
        n=5;
    }
    publicTestClass(int num){
        n=num;
    }
    publicint getN(){
        returnn;
    }
    publicboolean next(){
        n--;
        returnn!=-1;
    }
    publicvoid doit(){
        System.out.println("N:"+n);
    }
    publicstatic void main(String[] args) throws IOException{
        System.out.println("=========Output by Java code=========");
        TestClass t1=newTestClass();
        while(t1.next()){
            t1.doit();
        }
  
        CommandExecutor executor=newCommandExecutor();
        ArrayList<Cmd> cmds=newArrayList<Cmd>();
        inti=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=newBufferedReader(newInputStreamReader(newByteArrayInputStream(script.getBytes())));
        String cmd;
        String params;
        while((cmd=br.readLine()) !=null){
            params=br.readLine();
            Cmd c=newCmd();
            c.command=cmd;
            c.params=params.split(",");
            cmds.add(i++,c);
        }
  
        for(intj=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”)取得.
这些就不多说了,大家悟悟就知道了.