第一章 反射入门
Java Reflection (JAVA反射) Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,
或者说“自审”,并能直接操作程序的内部属性。例如,使用它能获得 Java 类中各成员的名称并显示出来。
看看原来有这样一个类
public class TestC...{
private int id;
public void setId(int g)...{
id=g*200;
}
private String getId()...{
return "No."+id;
}
public static void main(String[] args) ...{
//。。。
}
public void abc(String[] args) throws Exception ...{
String id = "===["+getId()+"]=="+args;
System.out.println(id);
}
}
当你新建一个测试类,并且执行下列代码时,你会发现它把类的方法都打印出来了:
public static void main(String[] args) ...{
Class c = Class.forName("com.samland.test.TestC");
System.out.println(c.getName());
Method[] ms=c.getMethods();
for (int i=0;i<ms.length;i++)...{
Method d = ms[i];
System.out.print("public ");
System.out.print(d.getReturnType().getSimpleName());
System.out.print(" ");
System.out.print(d.getName());
System.out.print("(");
Class[] ccc = d.getParameterTypes();
for (int j=0;i<ccc.length;i++)...{
if (j>0) System.out.print(", ");
System.out.print(ccc[j].getSimpleName());
System.out.print(" arg"+j);
}
System.out.print(")");
Class[] eee = d.getExceptionTypes();
for (int j=0;j<eee.length;j++)...{
if (j==0) System.out.print(" throws ");
if (j>0) System.out.print(", ");
System.out.print(eee[j].getSimpleName());
}
System.out.println(";");
}
}
}
结果输出:
com.samland.test.TestC
public void setId(int arg0);
public void main(String[] arg0);
public void abc(String[] arg0) throws Exception;
public int hashCode();
public Class getClass();
public void wait() throws InterruptedException;
public void wait(long arg0, int arg1) throws InterruptedException;
public void wait(long arg0) throws InterruptedException;
public boolean equals(Object arg0);
public void notify();
public void notifyAll();
public String toString();
第二章 方法执行
根据方法的名称来执行方法
我们可以用 reflection 来做一些其它的事情,比如执行一个指定了名称的方法。
调用返回的 java.lang.reflect.Method 实例定义一种 invoke 方法,
您可以用来在正在定义的类的一个实例上调用方法。这种 invoke 方法使用两个参数,
为调用提供类实例和参数值数组。
下面的示例演示了这一操作:
public static void main(String[] args) {
Class c = Class.forName("com.samland.test.TestC");
Class partypes[] = new Class[1];
partypes[0] = int.class;
Method method1= c.getMethod("setId", partypes);
Object testinst = c.newInstance();
Object ob1 = method1.invoke(testinst,new Object[]{232});
partypes[0] = String[].class;
Method method2= c.getMethod("abc", partypes);
Object ob2 = method2.invoke(testinst,new Object[]{new String[]{"AVAA","VA"}});
}
}
你会看到如下输入
===[No.46400]==[Ljava.lang.String;@1bab50a
这个其实是由com.samland.test.TestC的abc方法所输出。
一般的例子,在执行method的invoke方法时,都只会教你新建一个已经存在的实例,如
TestC obj = new TestC();
method1.invoke(obj,new Object[]{232});
可是,如果当我们的TestC并不在classpath的时候,这样就无法执行了,因此我这里通过使用
newInstance()来创建实例。
下面一章将会说道如何编译一个不在classpath的java源文件,并且执行它。
第三章 编译源文件
当你在程序的运行过程中,想通过动态编译一些非预先定义好的源代码,并且执行它其中的方法,
可以参考一下代码:
假设TestC.java是你刚刚通过某种手段生成的一个文本文件,里面包含了一个类的正确定义。
这个文件保存在某个临时路径下,我们可以编译它,并且输出到classpath,然后通过反射机制
执行它其中方法(详见本文第一、二章)。
public class Test3 {
public static void deploy(String fileName, String classpath) throws Exception{
String[] cpargs = new String[] {"-d", classpath,fileName};
int status = Main.compile(cpargs);
if(status!=0){
System.out.println("没有成功编译源文件!");
}else{
System.out.println(status);
}
//注意,不要使用Main.main()方法哦
//虽然main()和compile方法同样可以编译,但是main方法会执行一个系统退出命令
//他会终止你的程序运行!
//Main.main(new String[] {"-d",classpath,fileName});
}
public static void main(String[] args) {
String filename = "D:/temp/TestC.java";
//假设你的项目输出路径就是位于这里:
String classpath = "E:/project/WebContent/WEB-INF/classes/";
deploy(filename,filename);
Class c = Class.forName("com.samland.test.TestC");
Class partypes[] = new Class[1];
partypes[0] = String[].class;
Method method2= c.getMethod("abc", partypes);
Object testinst = c.newInstance();
Object ob2 = method2.invoke(testinst,new Object[]{new String[]{"AVAA","VA"}});
}
}
你会看到输出
===[No.0]==[Ljava.lang.String;@186d4c1
第四章 加载不在classpath的类
一般的例子都会教你使用
Class c = Class.forName("com.samland.test.TestC");
来装载存在于classpath中的类,假设这个二进制名称com.samland.test.TestC
并不在你项目的classpath中,那么有没有办法执行它呢?
答案是:有!
只要你查阅jdk的帮助,你就会发现原来可以装载符合条件的二进制流。也就意味着你可
把你做好的*.class加密保存,需要用到的时候再解密然后加载到JVM中执行。
public class Test4 {
public static void deploy(String fileName, String classpath) throws Exception{
//详见第三章
}
public static void main(String[] args) throws Exception {
String filename = "D:/temp/TestC.java";
//E:路径并非项目的classpath!
String classpath = "E:/";
deploy(filename,classpath);
String aClass = "E:/com/samland/test/TestC.class";
MyClassLoader mc
= new MyClassLoader();Class c = mc.defineClass(aClass);
System.out.println(c.getName());
Class partypes[] = new Class[1];
partypes[0] = String[].class;
Object testinst = c.newInstance();
Method method2= c.getMethod("abc", partypes);
Object ob2 = method2.invoke(testinst,new Object[]{new String[]{"AVAA","VA"}});
}
}
同样你会看到输出(注意,代码中并没有出现“com.samland.test.TestC”)
com.samland.test.TestC
===[No.0]==[Ljava.lang.String;@186d4c1
因为上述代码使用了自定义的类加载(类装入)方法,代码如下:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream; public class MyClassLoader extends ClassLoader {
//defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain)
//使用可选的 ProtectionDomain 将一个字节数组转换为 Class 类的实例。
public Class defineClass(String filename) throws IOException{
byte[] b = MyClassLoader.FileToBuffer(filename);
//一般的,name应该是类的二进制名称,这里为了不特指,所以注入了参数值null
return defineClass(null,b,0,b.length);
}
public static byte[] FileToBuffer(String filename) throws IOException{
ByteArrayOutputStream sb = new ByteArrayOutputStream();
InputStream fis = new FileInputStream(filename);
byte buffer[] = new byte[0xFF];
int b;
while((b = fis.read(buffer)) != -1){
sb.write(buffer,0,b);
}
fis.close();
byte[] ret = sb.toByteArray();
sb.close();
return ret;
}
}
全文完。