反射:需要了解Class。
Class是反射的基石,在反射中作用非常重要。Java程序中各个java类都属于同一类事物,描述这些事物的java类名就是Class。
说简单点,它就是java中所有类的统称,用它来描述每一个类的各种属性。其实,每一个类都有一个Class对象,每当编写并编译程序时,就会生成一个Class对象。
创建Class实例对象有三种方式:
通过类名获取,如:
Class cls=String.class;-------->类名.class
通过对象获取,如:
String str=”abc”;
Class cls1=str.getClass();--------->对象.getClass()
通过Class中的静态方法forName()获取
Class cls2=Class.forName(“java.lang.String”);-------->Class.forName(包名+类名)
对于同一个类来说,通过三种方式得到的Class实例对象,都是同一份。当在获取到字节码需要将字节码进行比较时,采用“==”进行比较,因为jvm只有一份字节码,不需要使用equals进行比较。
主要说后两种的区别:
第二种:获取Class实例对象必须要有对象;
第三种:首先它是返回的一个对字节码的引用,当我们通过它获得对一份字节码的引用或者创建Class对象时,其实它有两种方式,如果JVM的内存中没有所对应的字节码,它就会通过类加载器,将硬盘上的.CLASS文件加载到内存中,并返回。如果内存中有已经有了相应的字节码,则直接返回一份字节码。
反射言简意赅就是将java类中的各成员映射成相应的java类,如:构造方法类(Constructor)、字段类(Field)和方法类(Method)。
Constructor类:
获取java类中的所有构造方法
Constructor[]con=Class.forName(String name).getConstructors();
获取java类中指定的构造方法,以获取String中的new String(StringBuffer)为例
Constructor con=String.class.getConstructor(StringBuffer.class); //获取指定的构造函数
String str2=(String)con.newInstance(new StringBuffer("abc"));//通过newInstance创建实例对象
getConstructor(参数)的参数根据构造方法中的参数而定。在通过获取的构造方法创建对象时,创建对象的参数类型需要和获取构造方法时的参数类型一致。同时,Class为方便无参数构造函数的使用,在Class类中可以直接调用newInstance()进行对象的建立。
Field类:
获取java中的字段有两种方法
Field fieldXx=Class.forName(String name).getField(“Xx”);
Field []fields=Class.forName(String name).getFields();
//Field类描述java类中的成员变量
ReflectPoint pt1=new ReflectPoint(3,5);
Field fieldY=pt1.getClass().getField("y");
//fieldY的值是多少??fieldY代表Field类的一个实例对象
System.out.println(fieldY.get(pt1));//fieldY对应的不是在对象里的值,而是Field类中的一个成员变量
//获取x变量,x是私有的无法通过常规方法获取
Field fieldX=pt1.getClass().getDeclaredField("x");//通过使用getDeclaredField()可以获取私有变量
fieldX.setAccessible(true);//暴力反射,通过该方法可以将私有字段值反射输出
System.out.println(fieldX.get(pt1));
private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException {
Field[]fields=obj.getClass().getFields();
for(Field field:fields){
if(field.getType()==String.class){//字节码用==进行比较,因为只有同一份字节码
String oldV=(String)field.get(obj);
String newV=oldV.replace('b','a');
field.set(obj, newV);//当需要对原有类中的成员修改值是,记住需要重新设定值,使用set()方法。}
}
}
Method类
获取Method的主要方式是,以String的charAt为例,如下代码:
//Method类 利用反射调用charAt()
Method methodCharAt=str1.getClass().getMethod("charAt", int.class);
//获取指定方法,getMethod(“name”,.class)
System.out.println(methodCharAt.invoke(str1, 1));//invoke()调用,静态调用invoke(null,...)method对应静态方法
通过反射操作数组
int[]a1=new int[]{1,2,3};//对于数组,只要类型和维数相同,则引用的是同一份字节码
int[]a2=new int[3];
String[]s5=new String[]{"111","222","333"};
String[]s8=new String[4];
int[][]a3=new int[3][];
System.out.println(a1.getClass()==a2.getClass());
// System.out.println(a1.getClass()==a3.getClass());类型不相同,维数不相同的,编译都无法通过
System.out.println(Arrays.asList(a1));//由于a1属于object类,而非Object[]类,所以,不能按照jdk1.4标准进行list化,而jkd1.5中的asList()接收的是可变参数,把a1当做 一个参数处理,而int[]数组没有重写toString()方法,故只能调用Object的toString()方法
System.out.println(Arrays.asList(s5));//因为在jdk1.4asList(Object[]obj)里面所对应的对象数组,故按照jdk1.4进行数组化
System.out.println(Arrays.asList("ddd","ccc","ddc"));
printChange(s5);
printChange("acb");
数组反射的应用
public static void printChange(Object obj){
Class cla=obj.getClass();
if(cla.isArray()){
int len=Array.getLength(obj);//得到数组的长度
for(int i=0;i<len;i++){
System.out.println(Array.get(obj, i));
}
}
else{
System.out.println(obj);
}
}
学入逆水行舟,不进则退——至自己。