反射的作用
可以于运行时加载、探知、使用编译期间完全未知的classes。Java程序可以加载一个运行时才得知名称的class,
获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。
Class类
a.class、b.class是两个类,他们同样有共性的东西,如都有名字,构造方法等等。把他们抽象成一个特殊的类Class类。
故a.class、b.class他们都是Class的类的实例对象,当他们被类加载器加载到内存时,占用一部分空间,这部分空间就叫做
字节码。
不同的类字节码是不同的,即在内存空间的内容是不同的,这一个个空间可分别用一个个对象来表示-----a.class、b.class
获取各个字节码对应的实例对象有三种方法:
1,类名.class
2,对象.getClass()
3,Class.forName("类名")
第三种方法在反射常用。因为写代码时不知道类名,需要当做变量。
注:无论哪种方法,字节码实例对象如果相同则在内存中只有一份。如String.class在内存中只有一份。
Constructor类
Constructor类代表某一类的构造方法。
通过字节码实例对象.getConstructor(参数类型的字节码)获取类的指定参数的构造方法。
在通过Constructor类中的newInstenc(参数)方法创建对象。
例子:
/*用反射完成:String st=new String(new StringBuffer("abc"));*/
//先获得带参数的构造方法
Constructor constructor=
Class.forName("java.lang.String").getConstructor(StringBuffer.class);
//再利用构造器中的newInstence方法创建对象
String st=(String)constructor.newInstance(new StringBuffer("abc"));
System.out.println(st);
Field类
Field类代表某个类的成员变量。
例子:
假定已有ReflectPoint类,其成员为private int x,public int y;
获取public成员变量的值:
通过字节码实例对象.getField("成员变量名")获取Field类对象,通过Field类对象的get(对象)方法得到某一对象的成员变量的值。
/*利用反射Field得到类的字段(成员变量),先定义好一个Point类模拟*/
ReflectPoint pt=new ReflectPoint(1,6);
Field fieldY=pt.getClass().getField("y");
//fiedldY不是具体的值,不是6,他是字节码文件的成员变量,得到某个对象的值需要用get方法
System.out.println(fieldY.get(pt));
获取private成员变量的值:
注意方法区别
//暴力反射,得到私有成员变量及其值
Field fieldX=pt.getClass().getDeclaredField("x");
//上面只是让你得到私有成员,但取不出来,还需要一步
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt));
练习:利用反射,将任意对象中的所有String类型的成员变量对应的字符串内容中的字母b改成a
public class ReflectStr {
public String str1="babababa";
public String str2="aaaaaab";
public String str3="bbbaaaccc";
@Override
public String toString() {
return "ReflectStr [str1=" + str1 + ", str2=" + str2 + ", str3=" + str3
+ "]";
}
}
import java.lang.reflect.Field;
/*
* 利用反射,将任意对象中的所有String类型的成员变量对应的字符串内容中的字母b改成a
* 思路:
* 先定义一个测试类ReflectStr,并定义对象
* 得到对象的字节码文件,利用反射Field得到字节码的所有成员变量
* 遍历成员变量如果类型为String则得到其值
* 利用String方法将其值中的b改成a得到新的String
* 再将成员变量的值设置成新的字符串
* */
public class ReflectTest1{
public static void main(String[] args) throws Exception {
ReflectStr st=new ReflectStr();
System.out.println(st); //之前的字符串
func(st);
System.out.println(st);//之后的字符串
}
//封装方法
private static void func(Object obj) throws Exception{
// TODO Auto-generated method stub
Field[] fields=obj.getClass().getFields();
for(Field field:fields){
//由于字节码文件只有一个,所以用==而不必用equals
if(field.getType()==String.class){
String oldValue=(String)field.get(obj);
String newValue=oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}
}
}
Method类
Method类代表某个类中的一个成员方法。
通过字节码实例对象.getMethod(“方法名”,方法参数类型的字节码)获取Method对象。
再利用invoke方法实现功能。
例子:
String str1=new String("abc");
/*利用反射完成str1.charAt(2)*/
//获得字节码,并得到字节码中的指定方法charAt对象
Method method=str1.getClass().getMethod("charAt", int.class);
//charAt方法对象调用方法类中的invoke方法实现charAt功能
System.out.println(method.invoke(str1, 2));
练习:利用反射调用别的类的主函数
//别的主函数
class TestClass{
public static void main(String[] args){
for(String arg:args){
System.out.println(arg);
}
}
}
执行下面代码时,记得将上面的类名当做参数传递给下面类的主函数参数。
import java.lang.reflect.Method;
public class ReflectMethod {
public static void main(String[] args)throws Exception {
/*利用反射调用别的类的主函数*/
//一般方法:TestClass.main(new String[]{"111","222","333"});
//需要知道为什么要用反射?写代码时,不知道调用哪个类的主函数,类的名字需要当参数传递
String startingClassName=args[0];
Method mainMethod= Class.forName(startingClassName).getMethod("main", String[].class);
/*
* mainMethod.invoke(null, new String[]{"111","222","333"});
*这句话有两点注意:
*第一个参数一般是需要调用方法的对象,当null时,说明方法是静态的
*第二个参数由于JDK1.5需要兼容JDK1.4,故当参数为数组时,会把数组的参数拆开
*故这句话的参数会当做有4个。又两种解决方法
*/
mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});
//或mainMethod.invoke(null, (Object)new String[]{"111","222","333"});
}
}