------- android培训、java培训、期待与您交流! ----------
反射:
我总结的是:用字节码得到类组件,然后用这些组件去作用于这个类的某个对象。
反射的基石→Class类
Java程序中所有类都属于同一类事物,描述这类事物的java类名就是Calss
Class有9个预定义Class实例对象 8个基本数据类型和void
其他类型数据类型也有Class实例对象
反射就是把Java类中的各种成分映射为相应的Java类
如何得到Class实例化对象:
对象用:点class
对象变量用:点getClass()
未知对象用:Class.fonName(“完整的类名”);
方法:
Class.isPrimitive();判断字节码是否是原始类型,基本数据类型的包装类Class.TYPE能得到该包装类的原始数据类型的字节码。包括Void也可以获得void的字节码。
Class.isArray();判断字节码是否是数组类型。
Constructor类:
代表某个类中的一个构造方法
//得到String类中一个需要传入Stringbuffer对象的构造方法
Constructor constr = String.class.getConstructor(StringBuffer.class);
//根据得到的构造方法创建一个String类型的对象。
String str = (String)constr.newInstance(new StringBuffer(“aa”));
//也可用字节码直接调用newInstrance();得到一个无参的对象
Field 类:
getField();获得类中的属性,注意:这里得到的是这个类的属性,而不是这个类对象的属性!
getFields();获得类中所有的属性
getDeclaredFields();获得类种的所有属性,包括用private修饰的属性
getDeclaredField();用来获得用private修饰了的属性。
setAccessible(true);获得private修饰的属性的第二步,设置可以获得private修饰的属性,称之为暴力反射。
Method类:
getMethod(“方法名”,方法需要传入的参数类型);获得method,如果没有参数,参数类型就不填。
invoke(对象,参数);执行方法,如果对象位置写的null表示该方法为静态的。
注意:在JDK1.5后,传入的参数是引用类型数组会被认为是一个可变参数列表,基本数据类型的数组只被认为是一个Object,所有会对传入的数组进行一次拆包,也就是说你传入的一个数组,在方法方法中执行的时候变为几个参数了,就会抛出异常:参数不对
IllegalArgumentException: wrong number of arguments
所以我们在传入引用类型的数组的时候要对数组进行一次封装,可以把数组转换为Object,也可以把数组放到一个数组中。
1 package test.enhance.reflect; 2 3 import java.lang.reflect.Constructor; 4 import java.lang.reflect.Field; 5 import java.lang.reflect.Method; 6 7 /** 8 * 反射 9 * @author Administrator 10 * 11 */ 12 public class ReflectTest { 13 public static void main(String[] args) throws Exception { 14 15 //------------------获取构造函数 16 //根据类的字节码的getConstructor();把该类构造方法需要参数的字节码传入得到该类构造方法ReflectPoint(int,int,String,String) 17 Constructor constrRP = ReflectPoint.class.getConstructor(int.class,int.class,String.class,String.class); 18 19 //-----------------获取对象 20 //根据得到的构造方法创建一个对象 21 ReflectPoint rp = (ReflectPoint)constrRP.newInstance(1,2,"zhangsan","itcast"); 22 System.out.println(rp.getStr()); 23 24 25 //-----------------获取属性 26 //根据getField();得到该类的属性 27 //注意:这里得到的是这个类的属性,而不是这个类对象的属性! 28 Field fieldStr = rp.getClass().getField("str"); 29 String str = (String)fieldStr.get(rp); 30 System.out.println(str); 31 32 //-----------------获取私有属性 33 //获得private修饰的属性getDeclaredField(); 34 Field fieldX = rp.getClass().getDeclaredField("x"); 35 //暴力反射 36 fieldX.setAccessible(true); 37 int x = (Integer)fieldX.get(rp); 38 System.out.println(x); 39 40 //将rp对象属性值中带有a的把a全部替换为b 41 Field[] fields = rp.getClass().getDeclaredFields(); 42 for(Field field : fields){ 43 //用双等号比用equals好,字节码每个类只有一份 44 if(field.getType() == String.class){ 45 46 field.setAccessible(true); 47 48 //取得值,替换,重新赋值 49 field.set(rp, (((String) field.get(rp)).replaceAll("a","b"))); 50 System.out.println(field.get(rp)); 51 } 52 53 } 54 55 56 //----------------获取方法 57 //获得名字getX的方法 58 Method methodGetX = rp.getClass().getMethod("getX"); 59 //执行rp对象的getX方法 60 System.out.println("getX: " + methodGetX.invoke(rp)); 61 62 Method methodShow = rp.getClass().getMethod("show", String[].class); 63 //这行在执行的时候会报错 64 // methodShow.invoke(null, new String[]{"ss","aa","bb"}); 65 methodShow.invoke(null, (Object)new String[]{"ss","aa","bb"}); 66 } 67 }
通过反射获得泛型参数的实际类型:
我们用普通方法是得不到泛型的类型的,用发射就可以,要多经过几个步骤,首先建一个方法把泛型传进去,然后利用反射得到该方法,在1.5以后方法就可以获得泛型参数,再根据泛型参数获得实际类型。
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
//获得该方法
// Method method = ReflectGetGenericType.class.getMethod("test", List.class);
Method method = ReflectGetGenericType.class.getDeclaredMethod("test", List.class);
//获得该方法的所有泛型参数
Type[] types = method.getGenericParameterTypes();
//types[0]的值为:java.util.List<java.util.Date>
//转换为参数化类型
ParameterizedType pt =(ParameterizedType)types[0];
//获得原始类型
System.out.println(pt.getRawType());
//获取第一个泛型,有的可能有几个泛型如map集合
System.out.println(pt.getActualTypeArguments()[0]);
}
//创建方法传入泛型
private void test(List<Date> list){
}