黑马程序员_java反射笔记

时间:2023-02-19 17:04:27
------- android培训java培训、期待与您交流! ----------

Class类

java中所有的类是一类事物,我们用一个名为Class的类来描述这类事物。

获取Class类对象的三种方法:

类名.class
new 类名().getClass()
Class.forName("类的全称") // 类的全称指包名.类名

第三种方式,可以用一个字符串变量代替括号里的字符串。在程序运行时,从配置文件里加载字符串变量的值,所以可以事先不知道类名。但前两种方式必须要先知道类名才行。

java中的9中数据类型(boolean, byte, char, short, int, long, float, double, void)都有对应的Class对象。


反射的概念

java的类,这类事物有很多属性,比如包名、方法、成员变量等等。每一个属性又是一类事物,所以把每一种属性又定义为一个类。这就是java中反射的概念。

构造函数的反射:

得到所有构造方法实例

//得到所有类的构造方法对象
Constructor[] conss = Class.forName("java.lang.String").getConstructors();
得到一个构造方法

//获取String类的传入参数为StringBuffer对象的构造方法对象
Constructor cons = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
创建实例对象

//普通方式
String s1 = new String(new StringBuffer("abc"));
System.out.println(s1);
//反射方式由构造函数对象得到原类的实例对象
String s2 = (String)cons.newInstance(new StringBuffer("abc"));
System.out.println(s2);

调用空参构造方法创建实例的反射方式

String s3 = (String)Class.forName("java.lang.String").newInstance();

与下面的方式是一样的

String s4 = (String)Class.forName("java.lang.String").getConstructor().newInstance();

分析:上面这些例子里之所以需要String类型强制转换,是因为在编译阶段,虚拟机是不知道赋值号右边是String类型的,因为类名是通过字符串方式获得的。


成员变量的反射:

先定义一个类,在这个类里定义两个成员变量,然后我们用成员变量的反射来取这两个成员变量。

public class ReflectPoint {
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}

下面我们用成员变量的反射,先来取public的变量y

//创建ReflectPoint对象,给x,y传值
ReflectPoint rp = new ReflectPoint(3,5);
//获取成员变量y的反射
Field fy = rp.getClass().getField("y");
//通过反射获得对象rp对应的y值
int y = fy.getInt(rp);
//输出rp对应的y值
System.out.println(y);

接下来,我们看private的变量x

首先,如果我们像取y的反射一样来获得x的反射,会发生NoSuchFieldException异常,因为x是私有的。

Field fx = rp.getClass().getField("x");
我们通过下面的方法获得x的反射
Field fx = rp.getClass().getDeclaredField("x");

然后我们想用类似于y的方法获得rp对象对应的x值

int x = fx.getInt(rp);

这时又出现了新的异常IllegalAccessException。通过上面的方法我们能够获得x的反射,但是无法获得私有变量的值。

我们能够通过一种称为暴力反射的方式获得私有成员变量x的值

fx.setAccessible(true);
int x = fx.getInt(rp);

下面的例子是成员变量反射的一个应用:

需求:获得一个类的所有String类型成员变量,并把其中含有的‘b’修改成‘a’

过程:

我们先定义一个类

public class ReflectPoint {
private int x = 3;
private int y = 5;
public String str1 = "baba";
public String str2 = "book";
public String str3 = "element";
}

然后通过成员变量的反射实现修改。其中有些函数的异常没有处理,请先在主函数抛出。


//创建ReflectPoint对象
ReflectPoint rp = new ReflectPoint();
//获取ReflectPoint的所有成员变量
Field[] fields = rp.getClass().getFields();
//遍历所有成员变量
for(Field field: fields){
//判断String类型,由于String类型字节码文件只有一个,所以比较用==
if(field.getType() == String.class){
//获取rp对象对应的成员变量值
String oldValue = (String)field.get(rp);
//替换字母
String newValue = oldValue.replace('b','a');
//将新值写入rp对象中
field.set(rp, newValue);
}
}
//打印修改后的字符串
System.out.println(rp.str1);
System.out.println(rp.str2);
System.out.println(rp.str3);

成员方法的反射:

得到类中的一个方法:

Method methodCharAt = String.class.getMethod("charAt", int.class);
方法调用这个动作是方法自身的属性。所以用下面的方式来调用指定对象的方法,并打印,结果为b

System.out.println(methodCharAt.invoke("abcd", 1));

如果第一个参数是null,代表这是个静态方法。

System.out.println(methodCharAt.invoke(null, 1));


数组的反射:

只要数组元素类型相同而且维度相同,那么它们的字节码文件就是同一个。

数组的字节码文件类的父类是Object类。

注意:基本类型数组,例如int[ ],不能自动转化为Object[ ]。因为int类型不是Object类型,但int[ ] 类型是Object类型,所以int[]会被当成一个Object进行处理。

例如:

1. 很多函数的传入参数是Object[ ]类型,当我们传入一个int[ ]是不行的,无法转化。

2. 很多函数在jdk1.4里面的传入参数是Object[ ]类型,但在jdk1.5中改成了可变参数Object...类型。但jdk向下兼容,当我们传入一个String[ ]时,虚拟机会按照jdk1.4的方式进行处理,但是当我们传入参数类型为int[ ]时,虚拟机不会认为它是Object[ ]类型,所以会按照jdk1.5的传入方式,把int[ ]当成一个Object变量传入函数。


数组反射的应用例子:

需求:打印Object对象的内容,如果是个数组就打印数组内容,如果是单个对象,就打印单个对象内容。

如果我们用下面的方法:

public static void printObject(Object obj){
System.out.println(obj.toString());
}

调用上面的函数

String[] a2 = new String[]{"a","b","c"};
String s = "abc";
printObject(a2);
printObject(s);

结果是:

[Ljava.lang.String;@6d9dd520
abc

当我们往里面传入数组时,打印出来的是这个数组的类型以及它的哈希值。这不是我们希望的结果。

下面我们用数组反射Array来实现

public static void printObject(Object obj){
Class cls = obj.getClass();
if(cls.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.toString());
}
}
调用上面的函数:

String[] a2 = new String[]{"a","b","c"};
String s = "abc";
printObject(a2);
printObject(s);

打印结果:

a

b

c

abc


反射的作用:

用来构建框架。框架是提前写好的,当我再用这个框架时,是框架调用我写的类,也就是说,在我们写类之前,框架就可以写调用程序,这时框架不知道类名,肯定无法用new关键字创建对象,这时候就体现了反射的作用。

------- Windows Phone 7手机开发.Net培训、期待与您交流! -------