引用一位黑马前辈的话说“反射就是把一个Java类中的所有成分映射成相应的Java类”。我们知道Java类用于描述一类事物的共性,该类事物与什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同事物实例对象有不同的属性值。Java程序中的各个Java类,它们是否属于同一类事物,是不是可以用一个类来描述这类事物呢?这个类的名字就是Class,注意与小写class关键字的区别。Class类描述了类的名字、类的访问属性、类所属的包名、字段名称的列表、方法名称的列表等。Class类代表Java类,它的各个实例对象又分别对应各个类在内存中的字节码。
2、对象.getClass(),例如,new Date().getClass()
//对象都是由内存中该类的字节码弄出来的
3、Class.forName("类名"),例如,Class.forName("java.util.Date");
Class类中还包含了对其他类型对象的判断方法,如:isArray()--判定此 Class 对象是否表示一个数组类,isInterface()--判定指定的 Class 对象是否表示一个接口类型等等。
九个预定义Class实例对象:八个基本类型和 void。
通过以上3种反射方法得到一个Class对象,调用isPrimitive() 方法来判定指定的 Class 对象是否表示一个基本类型。
(int.class==Interger.TYPE)//true
(int.class==Interger.class)//flase
(int.class.isPrimitive())//true
(int[].class.isPrimitive())//false
我们通反射得到一个类在内存中的字节码对象后,用来做什么呢?表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息。
Java反射机制的实现要借助于3个类:Constructor,Field,Method;Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。
Constructor[] getConstructors() -- 获得类的所有公共构造函数
Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)
如何得到各个字节码对应的实例对象(Class类型),有3种方法:
1、类名.class,例如,Person.class2、对象.getClass(),例如,new Date().getClass()
//对象都是由内存中该类的字节码弄出来的
3、Class.forName("类名"),例如,Class.forName("java.util.Date");
Class类中还包含了对其他类型对象的判断方法,如:isArray()--判定此 Class 对象是否表示一个数组类,isInterface()--判定指定的 Class 对象是否表示一个接口类型等等。
九个预定义Class实例对象:八个基本类型和 void。
通过以上3种反射方法得到一个Class对象,调用isPrimitive() 方法来判定指定的 Class 对象是否表示一个基本类型。
(int.class==Interger.TYPE)//true
(int.class==Interger.class)//flase
(int.class.isPrimitive())//true
(int[].class.isPrimitive())//false
我们通反射得到一个类在内存中的字节码对象后,用来做什么呢?表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息。
Java反射机制的实现要借助于3个类:Constructor,Field,Method;Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。
Constructor类代表某个类中的一个构造方法
1、得到某个类所有的构造方法:Constructor[] getConstructors() -- 获得类的所有公共构造函数
Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)
2、得到某一个构造方法:
Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的公共构造函数
Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(与接入级别无关)
//通过区分参数类型与个数来获得构造方法,参数也要用字节码对象表示
Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的公共构造函数
Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(与接入级别无关)
//通过区分参数类型与个数来获得构造方法,参数也要用字节码对象表示
3、创建实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式:String str = (String)constructor.newInstance(new StringBuffer("abc"));
//调用获得的方法时要用到上面相同类型的实例对象
Class.newInstance()方法用到的默认构造函数是通过缓存机制来保存的
Field类代表某个类中的一个成员变量
1、得到某个类所有的字段:Field[] getFields() -- 获得类的所有公共字段
Field[] getDeclaredFields() -- 获得类声明的所有字段
2、得到某一个字段:
Field getField(String name) -- 获得命名的公共字段
Field getDeclaredField(String name) -- 获得类声明的命名的字段
示例代码:
ReflectPoint point = new ReflectPoint(1,7);
Field fieldY = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
System.out.println(y.get(point));
Field fieldX = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
x.setAccessible(true);//暴力反射
System.out.println(x.get(point));
由以上程序可知Field对象是对应到类上面的成员变量而非对象上的成员变量,字段fieldX代表的是x的定义,而不是具体的x变量。
因为变量x是私有的,所以要由getDeclaredField和setAccessible(true)来得到。
思考题:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
public static void changeStr(Object obj) {
for (Field field : obj.getClass().getDeclaredFields()) {// 获得类声明的所有字段
if (field.getType() == (String.class)) {
field.setAccessible(true);// 设置访问权限
try {
String oldValue = (String) field.get(obj);
String newValue = oldValue.replace('b', 'a');
field.set(obj, newValue);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
在做题的时候发现了一个很好用的方法field.getModifiers()得到字段前面的修饰符,值是枚举Modifier的对象。
Method类代表某个类中的一个成员方法
1、得到类中的某一个方法:例子:
Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
Method getMethod(String name, Class[] params) -- 使用特定的参数类型,获得命名的公共方法
Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型,获得类声明的命名的方法
2、得到类中的所有方法:
Method[] getMethods() -- 获得类的所有公共方法
Method[] getDeclaredMethods() -- 获得类声明的所有方法
3、调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式: System.out.println(charAt.invoke(str, 1));
如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是一个静态方法!
4、
jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args)
即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
用反射方式执行某个类中的main方法:
Class.forName(args[0]).getMethod("main", String[].class).invoke(null, (Object)new String[]{"a","b"});args[0]是通过MyEclipse在调用类里配置好的被调用类的完整类名,这里要注意按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,所以要强转成Object。
数组的反射:
1、具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。(int[].class==int[][].class)//false
(int[].class==String[].class)//false
2、代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
System.out.println(int[].class.getSuperclass().getName());//结果为java.lang.Object
System.out.println(String[].class.getSuperclass().getName());//结果为java.lang.Object
3、基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
int[] a1 = new int[3];
int[][] a2 = new int[3][4];
String[] b = new String[3];
Object obj1 = a1;
Object obj2 = b;
Object[] obj3 = a1;//编译无法通过
Object[] obj4 = a2;//编译可以通过
Object[] obj5 = b;