黑马程序员_反射

来源:互联网 发布:ubuntu搭建shadowsock 编辑:程序博客网 时间:2024/06/06 02:43

------- android培训、java培训、期待与您交流! ----------

/*|- java.lang   |- Class|- isPrimitive()  是否是基本类型|- isArray()  是否是数组类型|- getConstructors()  获取所有构造方法|- getConstructor(args)  获取某个构造方法|- getField(strName)  获取公共字段(public)|- getDeclaredField(strName)  获取已声明的字段(暴力反射,取消java语言访问检查)|- getMethod()  获取方法|- java.lang.reflect |- Constructor|- newInstance(Object... initargs) 创建实例对象|- Field|- setAccessible(flag) true取消Java语言访问检查。|- getType() 得到字段类型|- get(obj) 得到字段的值|- set(Object obj, Object value) 设置字段的值|- Method|- invoke(Object obj, Object... args) 调用方法*/

一.反射的基石:Class类。

1) 获取字节码对象(Class类型)

1. 类名.class, 例:System.class

2. 对象.getClass(), 例:new Date().getClass()

3. Class.forName("包名.类名"), 例:Class.forName("java.lang.String");

写源程序时不知道 类的名称,在运行的时候获取。 

2) 九个预定义Class实例对象:

1. 基本数据类型和void

2. Class.isPrimitive():Class对象是否是基本类型

3. int.class == Integer.TYPE,基本类型(Integer.TYPE,Long.TYPE...)

4. Class.isArray(): Class 对象是否是一个数组类。

4) 总之,只要是在源程序中出现的类型,都有各自的Class实例对象,如:int[],void...


二.反射的概念

1) 反射就是把Java类中的各种成分映射成相应的java类

构造方法 Constructor类

字段(成员变量) Field类

成员方法 Method类


三.构造方法的反射,Constructor类

1) 获取所有构造方法:

Constructor[] constructor = Class.forName("java.lang.String").getConstructors();

2) 获取某个构造方法:

Constructor constructor = Class.forName("java.lang.String").getConstructor(参数类型,...);

JDK1.5新特性,可变参数

重点:参数类型用Class实例对象表示。例如:

int.class,(int[]),class

int[] i = new int[0];

i.getClass();

3) 创建实例对象:

1.一般方式:String str = new String(new StringBuffer("abc"));

2.反射方式:String str = (String)constructor.newInstance(new StringBuffer("abc"));

调用获得的方法时要用到上面相同类型的实例对象。

4) Class.newInstance()方法:创建实例对象

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

Class类中也有newInstance()

反射创建一个实例对象class->constructor-->new object反射比较占用性能,反射会导致程序性能严重下降


四.成员变量的反射, Field类。

1) getField获取公共字段

2) getDeclaredField获取已声明的字段。

3) setAccessible(boolean flag):

值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。

值为 false 则指示反射的对象应该实施 Java 语言访问检查。

这就是暴力反射。

//ReflectPoint pt1 = new ReflectPoint(3,5);//Field fieldY = pt1.getClass().getField("y");Field fieldY = Class.forName("cn.itcast.day1.ReflectPoint").getField("y");//public int y;System.out.println(fieldY.get(pt1));Field fieldX = pt1.getClass().getDeclaredField("x");//private int x;fieldX.setAccessible(true);System.out.println(fieldX.get(pt1));


4) 成员变量的反射综合案例。

例:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"

1. 创建一个方法扫描对象中所有String类型字段

2. 得到类的字节码对象,获得所有字段。 obj.getClass().getFields()

3. 遍历所有字段,得到字段类型,判断是否是String类型的字段。 field.getType() == String.class

4. 得到字段的值,强转为String,再替换字符。oldValue.replace(oldChar,newChat);

5. 替换过字符的字段设置为指定的新值。field.set(obj, newValue);

public static void main(String[] args)throws Exception{ReflectPoint pt1 = new ReflectPoint();changeStringValue(pt1);System.out.println(pt1);}private static void changeStringValue(Object obj) throws Exception {Field[] fields = obj.getClass().getFields();//获取字节码对象,获取所有字段for(Field field : fields){// 遍历所有字段。//if(field.getType().equals(String.class)){if(field.getType() == String.class){// 找出String字段String oldValue = (String)field.get(obj);// 获取字段内容。String newValue = oldValue.replace('b', 'a');// 替换字符field.set(obj, newValue);// 设置新的字段}}}

五.成员方法的反射。 Method类。

1) 得到类中某个方法:

Method 方法对象 = Class.getMethod("方法名",参数类型);

例:Method methodCharAt = String.class.getMethod("charAt",int.class);

2) 调用方法:方法对象.invoke(对象,调用的方法中的参数);

普通方式:str.charAt(1);

反射方式:methodCharAt.invoke(str,1);

3) 调用的是静态方法:

静态方法不需要对象,所以调用一个静态方法时,传递的第一个参数是null.

方法对象.invoke(null,调用的方法中的参数);

4) JDK1.4和JDK1.5的invoke方法的区别:

1. JDK1.4:public Object invoke(Object obj,Object[]args),

需要将一个数组作为参数传递给invoke方法时,数组中每个参数对应调用方法中的参数。

charAt.invoke(str,new Object[]{(Integer)1});

2. JDK1.5:public Object invoke(Object obj,Object...args),因为有可变参数。


六.需求:写一个程序,这个程序能根据用户提供的类名。去执行该类的main方法

为什么要用反射的方式调用?

1) 不知道要调用的类的名称,要通过命令行传递类名。

2) 命令行传递类名:args[0]表示命令行的第一个参数,把类名写到命令行中。String className = args[0];把参数赋给字符串className

3) 获取字节码对象:Class.forName(className)获取传递的类名的字节码对象。

4) 获取main方法:Class.getMethod("main",String[].class)获取字节码对象里面的main方法。

5) 调用main方法:方法对象.invoke(null,args)

6) args :在这里,我要接收一个字符串数组,是一个对象。但JDK1.5兼容1.4的语法,会按1.4的语法。

将数组作为一个Object[],把里面的多个元素作为了多个参数。而我想获取一个字符串数组参数。

 所以:将字符串数组作为一个Object[]传递进去。

Object[]{字符串数组},第一个元素就是我要的参数:new Object[]{new String[]{"111","222","333"}},

数组也是一个Object。类型转换就当作Object对象:(Object)new String[]{"111","222","333"}

public class ReflectTest{public static void main(String[] args){//TestArguments.main(new String[]{"111","222","333"});//一般调用方法String startingClassName = args[0];Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);//mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});mainMethod.invoke(null, (Object)new String[]{"111","222","333"});}}class TestArguments{public static void main(String[] args){for(String arg : args){System.out.println(arg);}}}

七.数组反射。

1) 相同维数和元素类型的数组属于同一个类型。即具有相同的Class实例对象

2) 数组的Class实例对象的getSuperClass()方法返回的父类 为Object类对应的Class

3) 基本类型的一维数组可以当作Object类型使用。非基本类型的一维数组可以当作Object类型使用,也可以当作Object[]类型使用。

4) Arrays.asList()方法处理int[]和String[]时的差异。

int[]:JDK1.5把它当作一个Object对象 。获取的是[[I@170bea5]

String[]:当作一个Object[]获取到的是[a, b, c]

5) Array工具类用于完成对数组的反射操作。


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);}}



0 0
原创粉丝点击