JAVA中的反射技术
一,反射的基础:Class类
JAVA程序中的各个JAVA类属于同一类事物,描述这类事物的JAVA类名就是Class;Class类描述了类的名字,类的访问属性,类所属的包名,字段名称的列表,方法名称的列表等;
1,常用方法:getName()返回类名;
getPackage()返回所属的包;
getMethods()放回包含的方法列表;
getInterfaces()返回所实现的接口;
2.一个Class的实力对象代表着一份内存中的字节码,就是系统加载其他类到内存中时调用的那一份字节码;
得到Class的实力对象的方法:
①某个类的实例对象.getClass();
②Class.forName("该类的完整名称");
③类名.class;
*:当使用forName()方法时若此类已经被加载过,则直接返回此类的字节码映射;若还没有加载过,则虚拟机会先加载这个类到内存中,然后将其字节码的映射地址返回;
3,八种基本类型数据的字节码:
①isPrimitive()方法,返回这个Class对象实例是否一个基本数据类型的字节码,
②:int.class == Integer.TYPE;其他基本数据类型也是一样的;
③isArray():返回此对象是否是一个数组类型的字节码对象;
二,反射:反射就是把一个JAVA类中的各种成分映射成相应的JAVA类;包括成员变量(Field),方法Method,构造方法(Constructor),修饰符,包Package等信息;
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些对象;
三,Constructor类:代表了一个类中的一个构造方法;
1,得到一个类中的所有构造方法:
Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
2,得到一个类中的某一个构造方法:
Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
3,用得到的构造方法构建一个此类的实例对象:
String str1 = (String)constructor1.newInstance(new StringBuffer("abc"));
*:第二步和第三步所用到的构造方法中的参数要是相同的类型,否则就会报错;
4,Class类直接提供了newInstance()方法,用于直接调用无参的构造方法来创建一个实例对象;
四,Filed类:代表了类中的一个成员变量:
1,得到一个类中的所有成员变量:
①所有公共成员变量:getFields();
②所有声明过的成员变量:getDeclaredFields();
2,得到一个类中的一个成员变量:
①普通成员变量:Field f1 = Point.class.getField("变量名");——返回指定名称的公共成员变量;
②私有变量:Field f2 = Point.class.getDeclarField("变量名");——返回指定名称的变量,无论是否是可访问的;
此时得到的这个对象是这个类中表示这个成员变量的对象,而不是某个实例对象中的这个变量;
3.得到某个实例对象中某个成员变量的具体值:
①得到普通成员变量的值:f1.get("对象名");
②得到私有变量的值:f2.setAccessible(true); f2.get("对象名");
4,应用:将传入的对象的所有字符串变量的值中的字符‘b‘改为‘a‘:
public static void changeStringValues(Object obj) throws Exception {
Field[] fields = obj.getClass().getFields();
for (Field field : fields) {
if (field.getType() == String.class) {
String oldString = (String)field.get(obj);
String newString = oldString.replace(‘b‘, ‘a‘);
field.set(obj, newString);
}
}
}
五,Method类:代表了类中的一个方法:
1,得到一个类中的所有的方法:Method[] methods = 类名.class.getMethods();
2,得到一个类中的指定的某个方法:Method method = 类名.class.getMethod("方法名",参数.class ...);
3,用反射的方式调用这个方法:method.invoke(对象名,参数...);
当调用静态方法时使用invoke(null,参数....);
实例:在一个类中用执行另一个类的main方法,那个类的类名通过执行这个类时传递的字符串参数传递进来;
String className = args[0];
method mainMethod = Class.forName(className).getMethod("main",String[].class);
mainMethod.invoke(null,(Object)new String[]{});
//此处为什么传递字符串数组的时候要在前面转换为Object呢,原因是JDK为了兼容1.4版本他会将传递过去的数组类型的参数解包,从而报出“参数数量不对”的错误,所以有两种解决方法:
//第一种就是将这个字符串数组强转成为一个Object对象;第二种方法就是将这个字符串数组在打包一次变成一个包含一个元素的Object数组;
五,数组与Object的关系:
相同维度,相同元素类型的数组使用的是同一份字节码,不论其元素个数为多少;
所有的数组的父类都是Object;
int [] a1 = new int[]{1,2,3};
int [] a2 = new int[4];
int[][] a3 = new int[2][3];
String [] a4 = new String[]{"a","b","c"};
System.out.println(a1.getClass() == a2.getClass());——true
System.out.println(a1.getClass() == a4.getClass());——false
System.out.println(a1.getClass() == a3.getClass());——false
System.out.println(a1.getClass().getName());——[I
System.out.println(a1.getClass().getSuperclass().getName());——Object
System.out.println(a4.getClass().getSuperclass().getName());——Object
Object aObj1 = a1;true
Object aObj2 = a4;true
//Object[] aObj3 = a1;false
Object[] aObj4 = a3;true
Object[] aObj5 = a4;true
//基本数据类型的一维数组可以转换成Object,但不可以转换成Object[];因为基本数据类型不是Object
//所以当使用Arrays.asList()转换成List集合时字符串数组会转换成装有字符串元素的集合,但是整形数组会被转换成装有一个整形数组元素的集合
六,数组的反射的应用:
private static void printObject(Object obj) {
Class clazz = obj.getClass();
if(clazz.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);
}
}