您的位置:首页 > 编程语言 > Java开发

Java 反射机制介绍

2010-08-14 22:17 323 查看
Java 反射机制

1.介绍

Java 的反射机制是使其具有动态特性的非常关键的一种机制,通过java反射机制,可以动态分析一个类的属性及行为,也是在JavaBean 中广泛应用的一种特性。

2.Class类介绍

Java的反射机制是通过对Class类的操作进行的,在程序运行期间,运行时系统始终为所有的对象维护一个被称为运行时的类型标识,这个信息保存着每个对象所属的类足迹。

3. 获取Class类

获取Class类有三种方法,下面分别介绍:
(1) 调用一个类对象的getClass方法,将返回这个对象所属类的Class类型。
(2) 调用Class类的forName方法,它的参数是一个字符串,当字符串代表的不是一个类或接口的时候,调用forName方法会抛出一个checkedexception。所以无论何时使用这个方法都应该提供一个异常处理。
(3) 如果T代表任意Java类型(T即可以是类也可以是基本的数据类型)。
如:Class class1 = int.class;(基本数据类型)
Class class2 = Date.class;(类)
Class class3 = int[].class;(类)

Class的newInstance()方法,作用是返回这个类的一人实例,由于这个方法没有提供参数,所以构造类对象时是调用的相应类的默认构造方法。在这时一定要注意异常处理,测试代码如下:
package reflect;

public class SampleReflect {
public String info;

/**
* @return the info
*/
public String getInfo() {
return info;
}

/**
* @param info the info to set
*/
public void setInfo(String info) {
this.info = info;
}

/**print Info
*
*/
public void printInfo() {
System.out.println("Information : " + this.info);
}

public static void main(String[] arg) {
Class cla1 = null;
String classType = "reflect.SampleReflect";
try {
cla1 = Class.forName(classType);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("没有找到类 : " + classType);
}
System.out.println("第二种方法得到Class : " + cla1.getName());
//调用方法printInfo
SampleReflect sr1 = null;
try {
sr1 = (SampleReflect) cla1.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("不能实例化对象。/n请确认你要实例化的类不是一个抽象类或接口。");
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("发生对类非法访问的异常");
}
//调用SampleReflect类的printInfo方法
sr1.setInfo("我是用Class的forName方法加载的");
sr1.printInfo();
//打印sr1.getClass的getName及SampleReflect.class的getName方法返回值
System.out.println("第一种方法得到Class : " + sr1.getClass().getName() + "/n"
+ "第三种方法得到Class : " + SampleReflect.class.getName());
}
}

4. 利用反射分析类的能力

在java.lang.reflect包中有三个类Field、method、Constructor分别用于描述类的域、方法及构造器,且他们都有一个getName()方法用来返回相应项目的名称。Field类有一个getType方法,用来返回描述域所属类型的Class对象。这三个类还有一个getModifiers的方法,返回一个整数用于描述public、private及protected等描述。可以调用Modifier类相应静态方法来判断是什么修饰符。
Class类的getFileds、getMethods及getConstructors同getDeclareFields、getDeclareMethods及getDeclareConstructors的区别:前者返的都是用public修饰的项目,而后都返的全部项目,但想想也明白,不包括超类的项目。
下面的代码对main函数的第一个参数代表的类进行分析,并输出到控制台所有的域、构造器及方法。代码AnalyseClass如下:
package reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.TypeVariable;

public class AnalyseClass {

/**
* @param cla
*/
public AnalyseClass(Class cla) {
this.cla = cla;
this.printConstructors();
this.printMethods();
this.printFields();
}

//待分析的类
Class cla = null;

/**打印类声明的所有构造器
* @return 声明的构造器的个数
*/
public int printConstructors() {
System.out.println("/n****constructors of Class " + this.cla.getName() + "****");
Constructor[] cons = cla.getDeclaredConstructors();
int counter = 0;
for(Constructor c : cons) {
++counter;
String name = c.getName();
String modifiers = Modifier.toString(c.getModifiers());
if(modifiers.length() > 0) {
System.out.print(modifiers + " " + name + "(");
}
Class[] types = c.getParameterTypes();
for(int j = 0;j < types.length; ++j) {
if(j > 0) System.out.println(",");
System.out.print(types[j].getName());
}
System.out.println(");");
}
return counter;
}

/**打印类声明的所有方法
* @return 声明的方法的个数
*/
public int printMethods() {
System.out.println("/n****methods of Class " + this.cla.getName() + "****");
Method[] methods = cla.getDeclaredMethods();
int counter = 0;
for(Method m : methods) {
++counter;
String name = m.getName();
String modifiers = Modifier.toString(m.getModifiers());
if(modifiers.length() > 0) {
System.out.print(modifiers + " " + name + "(");
}
Class[] types = m.getParameterTypes();
for( int j = 0; j < types.length; ++j) {
if(j > 0 ) System.out.print(",");
System.out.print(types[j].getName());
}
System.out.println(");");
}
return counter;
}

/**打印类声明的域
* @return 声明的域的个数
*/
public int printFields() {
System.out.println("/n****fields of Class " + this.cla.getName() + "****");
Field[] fields = cla.getDeclaredFields();
int counter = 0;
for(Field f : fields) {
String name = f.getName();
String modifiers = Modifier.toString(f.getModifiers());
if(modifiers.length() > 0) {
System.out.println(modifiers + " " + name + ";");
}
++counter;
}
return counter;
}

public static void main(String[] args) {
if(args.length > 0) {
Class cla = null;
try {
cla = Class.forName(args[0]);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("没有找到类 : " + args[0]);
return;
}
new AnalyseClass(cla);
}else {
System.out.println("请附带类");
}
}
}
对于上述的分析类的程序,打印构造器、域及方法时可以直接调用相应的toString()方法。

5.执行方法

对于类SampleReflect加入下面测试代码,用Method的invoke方法执行方法。
如果invoke的是一个静态方法,第一个隐式参数可以不写,但若不是静态方法时,第一个隐式参数就必须填了,为要执行的对象的object引用。
try {
Method m1 =cla1.getMethod("setInfo",String.class);
if(m1 != null) {
m1.invoke(sr1, "通过Method的invoke方法进行的调用");
}
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

6. 在运行时使用反射分析对象

在编写程序时,如果知道要查看的域名和类型,查看指定的域是一件很容易的事情。而利用反射机制可以查看在编译时还不清楚的对象域。
主要使用了Field类的get(Object)方法,当Field为私有域时,会抛出IllegalAccessException。反射机制默认行为受限于java的访问控制。然而,如果一个java程序没有受到安全管理器的控制,就可以履盖访问控制。为了达到这个目的,需要调用Field、 Method、Constructor对象的setAccessible方法。
//访问控制
Field f = null;
try {
f = cla1.getField("info");
f = cla1.getDeclaredField("info");
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("非法访问");
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("没有这个域");
}
if (f != null) {
try {
System.out.println("域info的值为 : " + f.get(sr1));
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("不能访问");
}
}
由于域info的修饰符是public,故上面的代码可以成功运行。但如果把info变为private时,就会产生异常:NoSuchFieldException 。因为getField只能得到被声明为public的项目,故得不到info这个域。所以这里要用getDeclaredField方法来得到info域。

7.使用反射实现运算表达式的计算

代码如下:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;

public class DyComp {
String expr = null;
// private static com.sun.tools.javac.Main javac = new com.sun.tools.javac.Main();

public static double execute(String expr) {
DyComp dc = new DyComp(expr);
double result = -99999;
try {
result = dc.result();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}

//私有构造
private DyComp(String expr) {
this.expr = expr;
}

public double result() throws IOException {
String javafileContent = "import java.io.*;/r/n" +
"public class ExpreDyComp {" + "/r/n" +
"public static double cal() {/r/n" +
"/tdouble var = " + this.expr + ";/r/n" +
"return var;/r/n" +
"}/r/n}";
System.out.println("待生成的文件内容" + "/n" + javafileContent);
File tmpFile = new File("ExpreDyComp.java");
tmpFile.createNewFile();
PrintWriter printWriter = new PrintWriter(new FileWriter(tmpFile));
printWriter.write(javafileContent);
printWriter.flush();
printWriter.close();
//对文件进行编译
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int compRes = compiler.run(null, null, null,"-d","bin","ExpreDyComp.java");
if(compRes != 0) {
System.out.println("编译出错");
}

Class exprClass = null;
try {
exprClass = Class.forName("ExpreDyComp");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.err.println("找不到相应类");
}
Method m = null;
try {
if(exprClass != null) {
m = exprClass.getMethod("cal");
}
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(m != null) {
try {
Double d = (Double)m.invoke(null);
//System.out.println("值为" + d.doubleValue());
return d.doubleValue();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return -9999;
}

private double readResultToFile(File inFile) throws IOException {
BufferedReader bf = new BufferedReader(new FileReader(inFile));
String content = bf.readLine();
double res = 0;
if(content != null) {
res = Double.parseDouble(content);
} else {
System.out.println("文件" + inFile.getAbsolutePath() + "无数据");
}
bf.close();
return res;
}

public static void main(String args[]) {
System.out.println("动态编译");
System.out.println("输入你要计算的表达式");
InputStreamReader inputReader = new InputStreamReader(System.in);
BufferedReader bfReader = new BufferedReader(inputReader);
String expre = null;
try {
expre = bfReader.readLine();
} catch (IOException e1) {
// TODO Auto-generated catch block
System.out.println("从键盘输入出现问题");
e1.printStackTrace();
}
//定义结果数据
double result = -99999;
result = execute(expre);
//打印到console
System.out.println(expre + "计算结果如下" + "/n" + result);
}
}

附:继承设计的技巧

(1) 将公共操作和域放在超类。
(2).不要使用受保护的域。
(3) 使用继承实现is-a关系。
(4) 除非所有继承的方法都有意义,否则不要使用继承。
(5) 在履盖方法不要改变预期的行为。
(6) 使用类型信息而非类型信息。
(7) 不要过多的使用反射。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: