反射就是把Java类中的各种成分映射成相应的Java类对象。
把类的构造方法映射成Constructor的对象
一个类通过Class类加载到内存,由这个类的字节码就可以获得这个类的各种信息,比如String类,
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args){
//获得String类的所有的构造方法的列表
Constructor[] cons = String.class.getConstructors();
for(Constructor con:cons){
//循环打印出String类的所有的构造方法的列表
System.out.println(con);
}
//获得String类的所有的方法的列表
Method[] methods = String.class.getMethods();
for(Method method:methods){
//循环打印出String类的所有的方法的列表
System.out.println(method);
}
}
}
一个类会有多个构造方法的,如果要获得单个的构造方法就要根据构造方法的参数列表给反射传递相应的参数,
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test {
public static void main(String[] args) throws SecurityException, NoSuchMethodException,IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException{
//获得String类的参数是一个String的对象的构造方法,参数传递一个String对象
//抛出异常SecurityException, NoSuchMethodException
Constructor stringcon = String.class.getConstructor(String.class);
//反射一个String对象,初始化为1350995917ding
//抛出异常IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException
String str = (String)stringcon.newInstance("1350995917ding"/*传入的这个对象一定要和传入的类型一致*/);
//打印出这个字符串
System.out.println(str);
}
}
成员变量的反射
有这么一个类
public class Test0 {
private int x;
public int y ;
public Test0(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
和这个类
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws NoSuchFieldException{
Test0 test0 = new Test0(3,5);
Field fieldy = test0.getClass().getField("y");
//fieldy的只是多少呢?5吗?不是的。
//fieldy代表的是test0.getClass()这个字节码创建出来的一个成员变量y,这个成员变量 //之时在test0是5,这个值与fieldy无关。
//所以fieldy不是对象的变量而是类上的变量,要用它取对象上对应的值
System.out.println(fieldy.get(test0));//这个是5
}
}
第二个类改变一下
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws NoSuchFieldException{
Test0 test0 = new Test0(3,5);
Field fieldx = test0.getClass().getField("x");
System.out.println(fieldx);
}
}
这就会报错Exception in thread "main" java.lang.NoSuchFieldException: x
at java.lang.Class.getField(Class.java:1520)
at test.Test.main(Test.java:36)
(没有这个字段)
这是因为x是私有的
要获取私有字段应该这样
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws NoSuchFieldException{
Test0 test0 = new Test0(3,5);
//看到声明的变量,只要是声明过的就能看到
Field fieldx = test0.getClass().getDeclaredField("x");
//设置可获取的属性为true,否则只能看到不能获取()
fieldx.setAccessible(true);
System.out.println(fieldx.get(test0));
}
}
对字节码的比较要用 == ,判断某个字节码是不是某种类型(类名.getType() == 类型名.class )
Method反射
要得到某一个方法比如得到String类型的charAt();
String string = "ding";
Method charAt = String.class/*类型的字节码*/.getMethod("charAt"/*要调用的方法名*/, int.class/*要调用的方法名的参数类型*/);
System.out.println(charAt.invoke(string/*要处理的字符创*/, 要传入的参数));
如果invoke()方法的第一个参数是null这说明该Method对象对应的是一个静态方法
jdk1.4与1.5的invoke的区别
对接收数组的成员方法的反射如果一个方法的参数接收的是一个数组比如:public static void main(String[] args){},通过反射方式调用这个main方法时传递参数要注意,jdk1.5整个数组是一个参数,而jdk1.4数组中的每个元素对应一个参数,当吧一个字符串数组传递给invoke方法时,jdk1.5要兼容jdk1.4的语法,会按照jdk1.4的语法进行处理,就是把数组打散成为若干个单独的参数,所以会出现参数类型不对的问题,这个解决办法就是在参数的外部再包一层(就像IO的处理流)比如给main方法:mainMethod.invoke(null ,new Object[]{new String[]{"",""}})或者mainMethod.invoke(null ,(Object)new String[]{"",""})这样编译器处理时就不会把参数当成数组看待,也不会把数组打散成若干参数。