------- android培训、java培训、期待与您交流! ----------
反射,是1.2的新特性
Class类代表Java类,它的各个实例对象对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型就是Class。Java中的所有类,同属于同一个Class类。
Class类型的变量代表内存中的一段字节码,如:
内存中加载了多少个字节码,就有多少个Class对象/实例;
一旦用到某个类,该类的class文件就会被加载到内存,内存也就有了该字节码。
一道面试题:Class.forName()的作用:返回字节码,返回的方式有两种:一、该类字节码已经被加载到JVM里面了,直接返回;二、javaVM中还没有这个字节码,用类加载器去加载,把加载进来的类缓存在虚拟机里了,以后再要得到这份字节码就不需要加载了。
得到字节码的方式有三种:
1. 类名.class 如:System.class
2. 对象.getClass()如:new Date().getClass() 该对象应该是除了Class之外的其他类的对象。
3. Class.getName(“类的完整名字(含包名)”)例如:Class.getName(“java.util.Date”)
4. 以上的第三种最常用,因为可以将参数中的类名定义为一个变量,等待文件传递具体值。
九个预定义的Class实例对象:八个基本类型和void,他们对应的字节码。如int.class ==Integer.TYPE;
本类型的class(字节码)文件,和对应的包装类型的TYPE字段是相等的。 isArray()判断一个Class对象是不是数组类。
反射就是把Java类中的各种成分映射成相应的java类。
一个Java类用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,如某个类中的所有方法都可以用Method这个类来表示,而每一个方法就是Method类的一个实例对象。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象,就可以实现好多Java类不能实现的功能。
构造方法的反射
Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:
例子:Constructor [] constructors=Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子: Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
//获得方法时要用到类型
创建实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(newStringBuffer("abc"));
//调用获得的方法时要用到上面相同类型的实例对象
Class.newInstance()方法:
例子:String obj =(String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。(比较消耗资源,只有当获取该类构造方法比较麻烦的时候,才用此方法)
当需要用默认的空构造方法创建某个java类的实例时,就可以用此方法,省去了中间的先获得构造方法在创建对象的繁琐流程。
成员变量的反射
Field类代表某个类中的一个成员变量
得到的Field对象是对应到类上面的成员变量,而不是对应到对象上的成员变量。字段fieldX 代表的是x的定义,而不是具体的x变量。
示例代码:
public classReflectPoint {
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
ReflectPoint point =new ReflectPoint(1,7);
Field y =Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
System.out.println(y.get(point));
//Field x =Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");
Field x =Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
x.setAccessible(true);
System.out.println(x.get(point));
将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
class Xxx
{
Stringname="abc";
Stringemail="abd";
int x = 5;
}
func(Object obj)
{
Field [] fields = obj.getClass().getDeclaredFields();
for(Field field : fields)
{
if(field.getType()==java.lang.String.class)
{
field.setAccesible(true);
String original =(String)field.get(obj);
field.set(obj,original.replaceAll("b","a");
}
}
}
用构造函数创建一个类的对象,想要获取一个类的某个成员变量(字段)的在某个具体对象中的值。
步骤:
1. Field f =类名.class.getField(“参数名”); 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。getField(“参数名”),但是看不见私有参数,该方法只能传入非私有的参数。
2. f.get(具体对象); 返回指定对象上此 Field 表示的字段的值。但看不到私有字段。
1. Field f =类名.class.getDeclaredField(“参数名”);只要是类中声明的变量(字段),不论是否私有,都可以传入。这就是暴力反射。
2. f.setAccessible(true);将字段设为可见的。
3. f.get(具体对象); 如果字段是私有的,此处是无法获取的;只有在2中将字段设为可见的才可以。
成员变量反射的其他方法:
F是一个Field对象,则
f.getType(); 获取字段的类型。返回一个 Class
对象,它标识了此 Field
对象所表示字段的声明类型。
f.set(对象,该对象的字段新设值);
Fields[] fs =类名.class.getFields(); 返回一个包含某些 Field
对象的数组,这些对象反映此 Class
对象所表示的类或接口的所有可访问公共字段。
在此建议:分别总结Class/Field的常用方法。
成员方法的反射
Method类代表某个类中的一个成员方法
常用方法:
getMethod(方法名, 参数列表.class)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
invoke(对象,参数列表)执行Method对象所反射的方法。
得到类中的某一个方法:
例子: Method charAt =Class.forName("java.lang.String").getMethod("charAt",int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str, 1));
如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!
jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:publicObject invoke(Object obj,Object... args)
Jdk1.4:publicObject invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
对接收数组参数的成员方法进行反射
注意:用反射调用main(String[] args)方法的时候,考虑到1.5对1.4版本的兼容,会按1.4的语法解析参数,将 .invoke(null,String类型数组) 中的数组中的每个元素解析成参数,解决办法两种:
a、给数组再打包一层数组;
b、用(Object)强制类型转换,告诉编译器这是一个对象,不要当做多个对象形成的数组。
如:
mainMethod.invoke(null,new Object[]{newString[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
数组与Object的关系及其反射类型
所有元素类型,维数都想同的数组都对应一个类。
Arrays.asList();注意该方法,对int[] (int 类型一位数组的不同)。
1. 该方法,可以将数组变成字符串以方便直接将数组打印出来
2. 但对于一位整型数组int[],结果却是个特例,会打印出hashcode;
3. 原因,1.4版本的该方法传递的参数是Object[] ,也就是要求数组中的元素都是一个对象(Object或其子类);而int[]中的元素都是整型数据,不符合参数标准,所有智能按照1.5版本方法来解析。
4. 1.5方法中,传递的参数是元素列表,所以就把int[]当做一个元素来处理了,当然打印出来的是hashcode.
数组的反射应用
可以赢用数组的反射解决以下问题:创建一打印方法,如果要打印的值是数组,就打印出每个元素,如果不是数组就直接打印。
我们只能获得一个数组中某个元素的具体类型,无法获得数组中说有元素的类型。方法:
a[0].getClass().getName();
Array类的应用:方法……主要记住下面两个:
static Object get
(
数组对象, 下标)
返回指定数组对象中索引组件的值。
static int getLength
(
数组对象)
以 int
形式返回指定数组对象的长度
反射的作用:实现框架功能
采用配置文件加反射的方式创建ArrayList和HashSet的实例:
public static void main(String[] args) throwsException{
Properties props = new Properties();
InputStream ips = ReflectTest2.class.getResourceAsStream("/cn/itcast/javaenhance/config.properties");
props.load(ips);
Ips.close();
String className = props.getProperty("className");
Class clazz = Class.forName(className);
Collection collection = (Collection)clazz.newInstance();
ReflectPoint pt1 = new ReflectPoint(3,3);
ReflectPoint pt2 = new ReflectPoint(5,5);
ReflectPoint pt3 = new ReflectPoint(3,3);
collection.add(pt1);
collection.add(pt2);
collection.add(pt3);
collection.add(pt1);
System.out.println(collection.size());
}