标签:
一句话说明反射“反射就是把Java类中的各种成分映射成相应的Java类”。如果你对反射足够的了解,相信你一定会对这句话有很多共鸣。
换句话说:一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示(如下图),通过调用Class类的方法(getField、getMethod、getConstructor等)可以得到这些实例对象。
也就是说一个Java类用一个Class类的对象表示,一个类中的组成部分:成员变量、方法、构造方法、包等等信息也用一个个的Java类来表示。就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示Java类的Class类,显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等.
Java程序中各个Java类属于同一类事物,描述这一类事物的Java的类名就是Class(注意区别小写的class)。
例如,Person类代表人,它的实例对象就是“张三”、“李四”这样一个个具体的人;同理,Class类代表Java类,它的实例对象对应各个类在内存中的字节码,如:Person类的字节码、ArrayList类的字节码等。
获得字节码(Class类型)的三种方式如下:
根据下图理解字节码的概念:
下面我们简单介绍构造方法、成员变量、成员方法的反射应用:
Contructor类代表某个类中的一个构造方法。
得到某个类所有的构造方法:
例:Contructor[ ]constructors=Class.forName(“java.lang.String”).getConstructors();
得到某个类的某一个构造方法:
例:Contructor[ ]constructors=Class.forName(“java.lang.String”).getConstructors(StringBuffer.class);
示例代码如下:
package com.tgb.constructorReflect; import java.lang.reflect.Constructor; public class TestConstructorReflect { public static void main(String[] args) throws Exception { // 1、通常情况(非反射) String str1 = new String(new StringBuffer("zhipeng")); // 2、反射情况 Constructor<String> constructor = String.class.getConstructor(StringBuffer.class); String str2 = (String) constructor.newInstance(new StringBuffer("zhipeng")); } }
一个类有多个构造方法,用什么方式可以区分清楚想得到其中的哪个方法呢?根据参数的个数和类型,例如,CIass.getMethod(name,CIass…args)中的args参数就代表所要获取的那个方法的各个参数的类型的列表。重点:参数类型用什么方式表示?用Class实例对象。例如:int.Class、int[ ].Class等。
Class.newInstance()方法内部就是先得到类的默认的构造方法对象的字节码,然后用它创建实例对象。
Field类代表某一个类中的成员变量。
示例代码1如下:
先定义一个ReflectPoint类
public class ReflectPoint { private int x;// 注意x为private的 public int y; public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; } }
然后我们先拿到ReflectPoint的类的字节码,然后取出它某一属性的字节码,进而就可以得到这个类任何一个对象的相应的属性值:
import java.lang.reflect.Field; public class TestFieldReflect { /** * author: zhipeng */ public static void main(String[] args) throws Exception { ReflectPoint pt1 = new ReflectPoint(1, 2); ReflectPoint pt2 = new ReflectPoint(10, 12); Field fieldY = ReflectPoint.class.getField("y");// 拿到ReflectPoint【类】的是属性名为y的定义(字节码) System.out.println("pt1--->y--->" + fieldY.get(pt1));// 输出对象pt1的属性y:2 System.out.println("pt2--->y--->" + fieldY.get(pt2));// 输出对象pt2的属性y:12 Field fieldX = ReflectPoint.class.getDeclaredField("x");// x为private的,所以要用getDeclaredField fieldX.setAccessible(true);// 暴力反射 System.out.println(fieldX.get(pt1));// 输出对象pt1的属性x:1 } }
上面我们通过【FieldfieldY = ReflectPoint.class.getField("y");】拿到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量呢?我们分析,类只有一个,而类的实例对象有多个,如果是与对象关联,那么关联的是哪个对象呢?所以fieldX代表的是x的定义,而不是具体的x变量。也就是说我们拿到的是x在ReflectPoint.class中对应的定义(字节码),要用它去获取某一个实例对象身上对应的属性值。
示例代码2如下:
首先定义一个Student类
/** * @author wangzhipeng */ public class Student { // 1、定义两个Field public String userName; public String password; // 2、构造方法,给两个Field赋值 public Student(String username, String password) { this.userName = username; this.password = password; } // 3、重写它的toString方法 @Override public String toString() { return "用户名:" + this.userName + " 密码:" + this.password; } }
然后,我们通过反射实现,将任意一个Student对象中属性值中的字母“a”改为“【b】”,代码如下:
import java.lang.reflect.Field; public class TestChangeStringValue { public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException { Student zhipeng = new Student("akobe", "a123"); System.out.println(zhipeng);// 修改属性前,输出:用户名:akobe 密码:a123 ChangeStrValue(zhipeng);// 调用更改属性的方法 System.out.println(zhipeng);// 修改属性后,输出:用户名:【b】kobe 密码:【b】123 } public static void ChangeStrValue(Object object) throws IllegalArgumentException, IllegalAccessException { // 1、通过反射,拿到一个类所有的属性 Field[] fields = object.getClass().getDeclaredFields(); // 2、循环变量属性,如果属性类型为String则将其属性中的字母"a"改为"【b】" for (Field field : fields) { if (field.getType() == String.class) { String oldValue = (String) field.get(object); String newValue = oldValue.replace("a", "【b】"); field.set(object, newValue); } } } }
Method类代表某个类中的一个成员方法。
示例代码1如下:
首先定义一个Student类,里面有一个静态和一个非静态方法:
/** * @author wangzhipeng */ public class Student { // 1、定义一个静态方法Study() public static void Study() { System.out.println("static method--->Study()"); } // 2、定义一个非静态方法(String songName) public void Sing(String songName) { System.out.println("not static method--->Sing():" + songName); } }
然后我们反射执行这两个方法,注意它们的差异:
import java.lang.reflect.Method; public class MethodReflectTest { public static void main(String[] args) throws Exception { // 0、获得Student的class Class clazz = Student.class; // 1、反射执行Student类的【非静态】方法:Sing(String songName) Method methodSing = clazz.getMethod("Sing", String.class);// 第二个参数为参数的【类型】 methodSing.invoke(new Student(), "冰雨");// 非静态方法属于某个对象,所以第一个参数为一个Student的对象。 // 2、反射执行Student类的【静态】方法:static void Study() Method methodStudy = clazz.getMethod("Study", null); methodStudy.invoke(null, null);// 静态方法属于类,所以第一个参数为null;而这个方法没有参数,所以第二个方法为null } }
示例代码2如下:
我们通过反射来执行某一类的main方法.
首先定义一个Teacher类:
public class Teacher { // 定义static的main方法 public static void main(String[] args) { for (String arg : args) { System.out.println(arg); } } }
然后我们写一个工具类,执行任何类的main方法:
// 工具类,执行传入的类的main方法 public static void ExecuteMainMehod(Class clazz, String[] argStrings) throws Exception { Method teacherMainMethod = clazz.getMethod("main", String[].class); // teacherMainMethod.invoke(null, new String[] { "aaa", "bbb" // });//报wrong number of arguments(即参数个数异常) teacherMainMethod.invoke(null, new Object[] { argStrings });// 需要将argStrings包一层再传入,因为jdk1.4默认会将该参数拆箱 }
测试,执行Teacher类的main方法:
public static void main(String[] args) throws Exception { String[] argStrings = new String[] { "aaa", "bbb" }; ExecuteMainMehod(Teacher.class, argStrings);// 输出:aaa // bbb }
反射从java1.2就有了。它最大的用途应该是用来写框架,为什么呢?首先我们要理解,我们用框架(例如:Spring),其实是框架在调用我们的类。但是,框架已经写好了,而被调用的(我们的)类却还没写,这与我们的顺序思维有些不同。框架之所以能够调用还不存在的类,就意味着它离不开反射。
当你的类还没有写时,我就能调用,只要你运行时写出来就行,这就是反射所做的事情。例如Spring:
类似上面的spring配置,我们在所有的框架配置文件中都要配置类的名称【属性的名称】等,不用想就知道是为了反射。
反射就是把Java类中的各种成分映射成相应的Java类,例如:将构造方法映射为Constructor类;将属性映射为Field类;将方法映射为Method类。
我们接触到的反射的最大的用途应该是用来写框架,我们在Spring、Struts等等框架的配置文件中都要配置我们的业务类的类名、属性名等,就是为了运行时反射出它的对象或者属性值、方法等。因为框架都是要调用我们那些还没有写的不存在的业务类,所以它离不开反射。
标签:
原文地址:http://blog.csdn.net/wang379275614/article/details/46582813