反射的作用
反射的概念和实现原理
透析反射的基础_Class 类
(boolean, byte, char, short, int, long, float, double)以及关键词 void。当一个 class 被加载,或当加载器(class loader)的 defineClass() 被 JVM 调用,JVM 便自动产生一个Class object。
- getName():获得类的完整名字。
- getFields():获得类的public类型的属性。
- getDeclaredFields():获得类的所有属性。
- getMethods():获得类的public类型的方法。
- getDeclaredMethods():获得类的所有方法。
- getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
- getConstructors():获得类的public类型的构造方法。
- getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
- getSuperClass():
- getInterface():
- getModifiers() :
- getAnnotations() :
- getClassLoader() :
- newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
Class object获取方法 | 示例 |
---|---|
运用getClass() 注:每个 Object 的对象都有此函数 | String str = "abc"; Class c1 = str.getClass(); |
运用static method Class.forName() (最常被使用) | Class c1 = Class.forName ("java.lang.String"); Class c3 = Class.forName ("java.util.LinkedList$Entry"); Class c4 = Class.forName ("I"); Class c5 = Class.forName ("[I"); |
运用 .class 语法 | Class c1 = String.class; Class c3 = Main.InnerClass.class; Class c4 = int.class; Class c5 = int[].class; |
运用
Class.getSuperclass() 注:如果操作对象是Object,Class.getSuperClass()会返回null | Button b = new Button(); Class c1 = b.getClass(); Class c2 = c1.getSuperclass(); |
运用 | Class c3 = Character.TYPE; Class c5 = Integer.TYPE; Class c6 = Long.TYPE; Class c8 = Double.TYPE; Class c9 = Void.TYPE; |
java">class ReflectionDemo
{
public static void main(String[] args) throws Exception
{
Class clazz = ReflectionDemo.class;
Class clazz1 = Class.forName("ReflectionDemo");
Class clazz2 = clazz.getClass();
System.out.println("Hello World!");
}
}
上面程序反汇编结果如图:
java">package cn.itcast.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
/**
* @class: ReflectionClassDemo
* @package: cn.itcast.reflect
* @description: TODO
* @author: vivianZhao
* @date: 2013-7-20 上午10:55:13
* @version: 1.0
*/
public class ReflectionClassDemo {
public static void main(String args[]) throws Exception {
ReflectionClassDemo ref = new ReflectionClassDemo();
ref.getConstructor();
}
public void getConstructor() throws Exception {
Class<?> c = Class.forName("java.lang.Long");
Class<?> cs[] = { java.lang.String.class };
System.out.println("\n-------------------------------\n");
Constructor<?> cst1 = c.getConstructor(cs);
System.out.println("1、通过参数获取指定Class对象的构造方法:");
System.out.println(cst1.toString());
Constructor cst2 = c.getDeclaredConstructor(cs);
System.out.println("2、通过参数获取指定Class对象所表示的类或接口的构造方法:");
System.out.println(cst2.toString());
Constructor cst3 = c.getEnclosingConstructor();
System.out.println("3、获取本地或匿名类Constructor 对象,它表示基础类的立即封闭构造方法。");
if (cst3 != null)
System.out.println(cst3.toString());
else
System.out.println("-- 没有获取到任何构造方法!");
Constructor[] csts = c.getConstructors();
System.out.println("4、获取指定Class对象的所有构造方法:");
for (int i = 0; i < csts.length; i++) {
System.out.println(csts[i].toString());
}
System.out.println("\n-------------------------------\n");
Type types1[] = c.getGenericInterfaces();
System.out.println("1、返回直接实现的接口:");
for (int i = 0; i < types1.length; i++) {
System.out.println(types1[i].toString());
}
Type type1 = c.getGenericSuperclass();
System.out.println("2、返回直接超类:");
System.out.println(type1.toString());
Class[] cis = c.getClasses();
System.out.println("3、返回 Class 中使用的所有的类和所有的接口:");
for (int i = 0; i < cis.length; i++) {
System.out.println(cis[i].toString());
}
Class cs1[] = c.getInterfaces();
System.out.println("4、实现的接口");
for (int i = 0; i < cs1.length; i++) {
System.out.println(cs1[i].toString());
}
System.out.println("\n-------------------------------\n");
Field fs1[] = c.getFields();
System.out.println("1、类或接口的所有可访问公共字段:");
for (int i = 0; i < fs1.length; i++) {
System.out.println(fs1[i].toString());
}
Field f1 = c.getField("MIN_VALUE");
System.out.println("2、类或接口的指定已声明指定公共成员字段:");
System.out.println(f1.toString());
Field fs2[] = c.getDeclaredFields();
System.out.println("3、类或接口所声明的所有字段:");
for (int i = 0; i < fs2.length; i++) {
System.out.println(fs2[i].toString());
}
Field f2 = c.getDeclaredField("serialVersionUID");
System.out.println("4、类或接口的指定已声明指定字段:");
System.out.println(f2.toString());
System.out.println("\n-------------------------------\n");
Method m1[] = c.getMethods();
System.out.println("1、返回类所有的公共成员方法:");
System.out.println(m1.length);
for (int i = 0; i < m1.length; i++) {
System.out.println(m1[i].toString());
}
Method m3[] = c.getDeclaredMethods();
System.out.println("2、返回类自己定义所有的成员方法:");
System.out.println(m3.length);
for (int i = 0; i < m3.length; i++) {
System.out.println(m3[i].toString());
}
Method m2 = c.getMethod("longValue", new Class[] {});
System.out.println("3、返回指定公共成员方法:");
System.out.println(m2.toString());
}
}
更多 Class 类和 Class 对象的功能函数请参考 JDK API 手册。
构造方法的反射应用_Constructor 类
以字符串形式返回此构造方法的名称。
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
按照声明顺序返回一组 Class 对象,这些对象表示此 Constructor 对象所表示构造方法的形参类型。
返回一组表示声明要抛出的异常类型的 Class 对象,这些异常是由此 Constructor 对象表示的底层构造方法抛出的。
返回一组 Type 对象,这些对象表示声明要由此 Constructor 对象抛出的异常。
返回 Class 对象,该对象表示声明由此 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(new StringBuffer("abc"));
//调用 newInstance 的方法时要传递 constructor 所表示的构造方法形式参数相同类型实际参数,否则运行时会报异常
Class.newInstance()方法:
例子:String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到A类默认的构造方法 Constructor 对象,然后用该构造方法的对象创建A类实例对象。该方法内部的具体代码用到了缓存机制来保存默认构造方法的实例对象,免得再次获取构造方法的实例,这也侧面说明反射机制效率比较低。
演示:
java"> //用字节码获取获取 String 的带 StringBuffer 参数的构造方法.返回 Constructor 类型对象
Constructor constructor = String.class.getConstructor(Class.forName("StringBuffer"));
//再用 Constructor 类中的 newInstance 方法给构造方法实例化.
String str2=(String)constructor1.newInstance(new StringBuffer("abc"));
//打印返回的字符串角标位置上的字符
System.out.println(str2.charAt(2));
成员变量的反射_Field 类
Field类代表类中的成员变量,Field 对象代表某个类中的某个成员变量。
返回指定对象上此 Field 表示的字段的值。
将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
返回此 Field 对象表示的字段的名称。
获取一个静态或实例 boolean 字段的值。
返回表示类或接口的 Class 对象,该类或接口声明由此 Field 对象表示的字段。
返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型。
以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符。
得到的 Field 对象是对应到类上面的成员变量,还是对应到对象上的成员变量?Field Method Constructor 都是对类的成员的描述,所以它们的对象都是对类字节码的成员的封装。
演示:
java"> //成员变量的反射
ReflectPoint pt1 = new ReflectPoint(3, 6);
//成员变量时共有的可以正常反射
Field filedY = pt1.getClass().getField("y");
System.out.println(filedY.get(pt1));
//如果成员变量是私有的要强行反射getDeclaredField
Field fieldX = pt1.getClass().getDeclaredField("x");
//暴力反射修改字段的访问属性的方法方法 setAccessible(true); 这是继承自 java.lang.reflect.AccessibleObject 的方法
fieldX.setAccessible(true);
//获取
System.out.println(fieldX.get(pt1));
成员变量反射的综合案例
java">/**
* 需求:演示反射 API 中 Feild 类的用法
*
* 思路: 使用反射替换某个对象中字符字段的值
*
* 步骤:
*
* 总结:
* Class 对象中的 getFeilds 可以获取该 Class 所有public字段
* Feild 对象中的 get 方法可以获取某个对象的该字段的值
* Feild 对象中的 set 方法可以设置某个对象的该字段的值
* Feild 对象中的 setAccessable 方法可以这种某个对象的该字段的访问属性
*/
package cn.itcast.reflect;
import java.lang.reflect.Field;
/**
* @class: FieldReflectionDemo
* @package: cn.itcast.reflect
* @description: TODO
* @author: vivianZhao
* @date: 2013-7-20 下午11:34:20
* @version: 1.0
*/
public class FieldReflectionDemo {
public String string1 = "ball";
public String string2 = "basketball";
public String string3 = "itcast";
/**
* @method: main
* @description: TODO
* @param args
* @return: void
* @author: vivianZhao
* @date: 2013-7-20 下午11:34:20
* @version: 1.0
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
public static void main(String[] args) throws ClassNotFoundException,
IllegalArgumentException, IllegalAccessException {
// TODO Auto-generated method stub
String value;
FieldReflectionDemo fieldReflectionDemo = new FieldReflectionDemo();
Class<?> clazz = Class.forName("cn.itcast.reflect.FieldReflectionDemo");
Field[] feilds = clazz.getFields();
for (Field feild : feilds) {
if (feild.getType() == String.class) {
value = (String) feild.get(fieldReflectionDemo);
value = value.replace('b', 'a');
feild.set(fieldReflectionDemo, value);
}
}
System.out.println(fieldReflectionDemo);
}
@Override
public String toString() {
return "FieldReflectionDemo [string1=" + string1 + ", string2="
+ string2 + ", string3=" + string3 + "]";
}
}
成员方法的反射_Method 类
Method 类常用成员方法:
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
以 String 形式返回此 Method 对象表示的方法名称。
按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。
返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型。
例子: Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式: charAt.invoke(str, 1);
如果传递给 Method 对象的 invoke() 方法的第一个参数为 null,说明该 Method 对象对应的是一个静态方法。
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}) 形式。
java"> //获取 String 的字节码对象调用字节码的 getmethod 方法.获取 String 的 charAt 方法.int.class是1.5之后的
//新特性.可变参数.
Method methodCharAt = String.class.getMethod("charAt", int.class);
//调用Method的invoke方法.获取Str1,的第二个角标位置
System.out.println(methodCharAt.invoke(str1, 2));
对接收数组参数的成员方法进行反射
目标:
写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。用普通方式调完后,大家要明白为什么要用反射方式去调?
问题:
启动 Java 程序的 main 方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为 invoke 方法传递参数,按 jdk1.5的语法,整个数组是一个参数,而按 jdk1.4 的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac 会到底按照哪种语法进行处理jdk1.5肯定要兼容jdk1.4的语法,会按 jdk1.4 的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码ainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作 jdk1.4 的语法进行理解,而不把它当作 jdk1.5 的语法解释,因此会出现参数类型不对的问题。
解决办法:
mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
数组与 Object 的关系及其反射类型
java">/**
* 需求:演示数组 和 Object 的关系
*
* 思路:
* 1.获取数组的 Class 对象,比较是否相等
* 2.打印数组的 Class 对象的名字
* 3.数组 和 Object 类型之间的类型转换
*
* 步骤:
*
* 总结:
* 1.java 里面,相同元素类型和相同维度数的数组是同一个类型的数组,对应同一个 Class 对象
* 2.数组类型的签名是" [ + 元素类型名签名 ",如果是多维数组,也是符合前面的规则,结果就成了几维数组会有几
* 个" [ "符号
* 3.数组类型可以向上转型为 Object 类型
* 4.java 语言中没有多维数组,其实都是一维数组,所谓多维数组,是数组中的元素还是数组,只有最后一层是一个非
* 数组类型
*/
package cn.itcast.reflect;
/**
* @class: ArrayAndObject
* @package: cn.itcast.reflect
* @description: TODO
* @author: vivianZhao
* @date: 2013-7-21 上午10:03:02
* @version: 1.0
*/
public class ArrayAndObject {
/**
* @method: main
* @description: TODO
* @param args
* @return: void
* @author: vivianZhao
* @date: 2013-7-21 上午10:03:02
* @version: 1.0
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int [] a1 = new int[4];
int [] a2 = new int[5];
int [][] a3 = new int[2][3];
String [] a4 = new String[3];
// 返回 true,说明同类型同维度的数组是同一个 Class 对象
System.out.println(a1.getClass() == a2.getClass());
// 不可比较,说明同类型不同维度的数组不是同一个 Class 对象
//System.out.println(a1.getClass() == a3.getClass());
// 不可比较,说明不同类型同维度的数组不是同一个 Class 对象
//System.out.println(a1.getClass() == a4.getClass());
// 数组类型的名称是 [ + 类型名签名,如果是多维数组,几维数组用几个 [
System.out.println(a1.getClass().getName());
System.out.println(a3.getClass().getName());
System.out.println(a4.getClass().getName());
// 数组类型的父类型都是 Object 类型
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a3.getClass().getSuperclass().getName());
// 数组类型的父类都是 Object 类型,所以数组类型可以上转为 Object 类
Object aObject1 = a1;
Object aObject2 = a4;
// 数组中的元素有两种类型,一种是基本类型,一种是引用类型
//Object[] aObjects3 = a1;
// 数组类型的类型匹配需要匹配两个地方,第一个是否是数组,第二个数组中的元素类型的匹配
// Object [] aObject4 定义了一个 ,一维数组,其中数组中的元素是 Object 类型
// a3 是定义了一个一维数组A,数组中的元素是 一维数组B,一维数组B中的元素是 int 类型,一维数组B可以
// 向上转型为 Object 类型,所以可认为 Java 语言中没有多维数组,其实都是一维数组,所谓多维数组,是
// 数组中的元素还是数组,只有最后一层是一个非数组类型
Object[] aObject4 = a3;
Object[] aObject5 = a4;
}
}
数组的反射应用_Array 类
创建一个具有指定的组件类型和长度的新数组。
创建一个具有指定的组件类型和维度的新数组。
返回指定数组对象中索引组件的值。
将指定数组对象中索引组件的值设置为指定的新值。
以 int 形式返回指定数组对象的长度。
Arrays.asList()方法处理 int[] 和 String[] 时的差异
java"> int [] a11 = new int[]{1, 2, 3};
String [] a12 = new String[]{"a", "b","c"};
System.out.println(Arrays.asList(a11));
// 这说明数组中的元素向上转型的时候不会进行自动装箱拆箱
// 自动装箱拆箱只会在运算符表达式中进行
//System.out.println(Arrays.asList((Integer [])a11));
System.out.println(Arrays.asList(a12));
[a, b, c]
ArrayList_HashSet的比较及Hashcode分析
java">/**
*
*/
package cn.itcast.reflect;
import java.util.HashSet;
/**
* @class: HashSetModif
* @package: cn.itcast.reflect
* @description: TODO
* @author: vivianZhao
* @date: 2013-7-21 下午12:19:59
* @version: 1.0
*/
public class HashSetModify {
/**
* @method: main
* @description: TODO
* @param args
* @return: void
* @author: vivianZhao
* @date: 2013-7-21 下午12:19:59
* @version: 1.0
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
HashSet<Point> points = new HashSet<HashSetModify.Point>();
Point point1 = new Point(1, 2);
Point point2 = new Point(2, 4);
Point point3 = new Point(3, 5);
points.add(point1);
points.add(point2);
points.add(point3);
System.out.println(points);
point1.x = 16;
System.out.println(points);
for (Point point : points) {
System.out.println(point);
}
System.out.println(points.contains(point1));
}
static class Point {
int x;
int y;
public Point(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Point other = (Point) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
@Override
public String toString() {
return "Point [x=" + x + ", y=" + y + "]";
}
}
}
框架的概念及用反射技术开发框架的原理
什么是框架,例如,我们要写程序扫描.java文件中的注解,要解决哪些问题:读取每一样,在每一个中查找@,找到的@再去查询一个列表,如果@后的内容出现在了列表中,就说明这是一个我能处理和想处理的注解,否则,就说明它不是一个注解或者说至少不是一个我感兴趣和能处理的注解。接着就编写处理这个注解的相关代码。现在sun提供了一个apt框架,它会完成所有前期工作,只需要我们提供能够处理的注解列表,以及处理这些注解的代码。Apt框找到我们感兴趣的注解后通知或调用我们的处理代码去处理。你做的门调用锁,锁是工具,你做的门被房子调用,房子是框架,房子和锁都是别人提供的。
程序中不处理异常,而是main方法声明抛出异常,便于大家可以集中看主要的关键代码。Class类也提供getResourceAsStream方法的比喻:如果你每次都找我给你商店买可乐,那我还不如直接向你买可乐,即直接提供一个买可乐的方法给你。
框架与框架要解决的核心问题
我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
框架要解决的核心问题
我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样能调用到你以后写的类(门窗)呢?因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直接new 某个类的实例对象了,而要用反射方式来做。
先直接用new 语句创建 ArrayList 和 HashSet 的实例对象,演示用eclipse 自动生成 ReflectPoint 类的 equals 和 hashcode 方法,比较两个集合的运行结果差异。然后改为采用配置文件加反射的方式创建 ArrayList 和 HashSet 的实例对象,比较观察运行结果差异。
java">/**
*
*/
package cn.itcast.reflect;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
/**
* @class: HashSetModif
* @package: cn.itcast.reflect
* @description: TODO
* @author: vivianZhao
* @date: 2013-7-21 下午12:19:59
* @version: 1.0
*/
public class ReflectionProperties {
/**
* @method: main
* @description: TODO
* @param args
* @return: void
* @author: vivianZhao
* @date: 2013-7-21 下午12:19:59
* @version: 1.0
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
InputStream inputStream = new FileInputStream("config.properties");
Properties properties = new Properties();
properties.load(inputStream);
inputStream.close();
String className = properties.getProperty("className");
Collection points = (Collection) Class.forName(className)
.newInstance();
Point point1 = new Point(1, 2);
Point point2 = new Point(2, 4);
Point point3 = new Point(3, 5);
Point point4 = new Point(3, 5);
points.add(point1);
points.add(point2);
points.add(point3);
points.add(point4);
System.out.println(points.size());
}
static class Point {
//同上一个例子
}
}
用类加载器的方式管理资源和配置文件
java">/**
*
*/
package cn.itcast.reflect;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
/**
* @class: HashSetModif
* @package: cn.itcast.reflect
* @description: TODO
* @author: vivianZhao
* @date: 2013-7-21 下午12:19:59
* @version: 1.0
*/
public class ReflectionProperties {
/**
* @method: main
* @description: TODO
* @param args
* @return: void
* @author: vivianZhao
* @date: 2013-7-21 下午12:19:59
* @version: 1.0
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
// 这个是到运行时用户当前目录下去加载文件
InputStream inputStream = new FileInputStream("config.properties");
// 这个是到 classpath 文件目录中去加载文件, 这个只能是读入文件
inputStream = ReflectionProperties.class.getClassLoader()
.getResourceAsStream("cn/itcast/reflect/config.properties");
// Class 对象的 getResourceAsStream 可以使用相对路径
inputStream = ReflectionProperties.class
.getResourceAsStream("config.properties");
// Class 对象的 getResourceAsStream 可以使用其他包的相对路径
inputStream = ReflectionProperties.class
.getResourceAsStream("resource/config.properties");
// Class 对象的 getResourceAsStream 可以使用绝对路径
inputStream = ReflectionProperties.class
.getResourceAsStream("/cn/itcast/reflect/config.properties");
Properties properties = new Properties();
properties.load(inputStream);
inputStream.close();
String className = properties.getProperty("className");
}
}
由内省到JavaBean
-->getLast()的属性名是last
-->setCPU的属性名是CPU
-->getUPS的属性名是UPS
一个类被当作 javaBean 使用时,JavaBean 的属性是根据方法名推断出来的,它根本看不到 java 类内部的成员变量。
一个符合 JavaBean 特点的类可以当作普通类一样进行使用,也可以当 JavaBean 使用,把它当 JavaBean 用好处如下:
1.在 Java EE 开发中,经常要使用到 JavaBean。 很多环境就要求按 JavaBean 方式进行操作。
2.JDK 中提供了对 JavaBean 进行操作的一些 API,这套 API 就称为内省。如果在写程序的时候,你只知道要访问的属性名x保存在某个字符串变量 xstring 中,要通过 getX 方法来访问私有的 x,需要自己根据属性名来组合为 getter setter 方法名,然后再使用反射来访问这些方法,比较麻烦,所以JDK 提供了内省这套 API 操作JavaBean 比用普通类的方式更方便。
java">/**
* 需求:自己模拟内省的操作
*/
package cn.itcast.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @class: ReflectionCopyObject
* @package: cn.itcast.reflect
* @description: TODO
* @author: vivianZhao
* @date: 2013-7-20 下午1:33:47
* @version: 1.0
*/
public class ReflectionCopyObject {
public Object copy(Object object) throws Exception {
// 获得对象的类型
Class<?> classType = object.getClass();
System.out.println("Class:" + classType.getName());
// 通过默认构造方法创建一个新的对象
Object objectCopy = classType.getConstructor(new Class[] {})
.newInstance(new Object[] {});
// 获得对象的所有属性
Field fields[] = classType.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
String fieldName = field.getName();
String firstLetter = fieldName.substring(0, 1).toUpperCase();
// 获得和属性对应的getXXX()方法的名字
String getMethodName = "get" + firstLetter + fieldName.substring(1);
// 获得和属性对应的setXXX()方法的名字
String setMethodName = "set" + firstLetter + fieldName.substring(1);
// 获得和属性对应的getXXX()方法
Method getMethod = classType.getMethod(getMethodName,
new Class[] {});
// 获得和属性对应的setXXX()方法
Method setMethod = classType.getMethod(setMethodName,
new Class[] { field.getType() });
// 调用原对象的getXXX()方法
Object value = getMethod.invoke(object, new Object[] {});
System.out.println(fieldName + ":" + value);
field.setAccessible(true);
System.out.println(field.get(object).getClass());
// 调用拷贝对象的setXXX()方法
setMethod.invoke(objectCopy, new Object[] { value });
}
return objectCopy;
}
/**
* @method: main
* @description: TODO
* @param args
* @return: void
* @author: vivianZhao
* @date: 2013-7-20 下午1:33:47
* @version: 1.0
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Customer customer = new Customer("Tom", 21);
customer.setId(new Long(1));
Customer customerCopy = (Customer) new ReflectionCopyObject().copy(customer);
System.out.println("Copy information:" + customerCopy.getId() + " "
+ customerCopy.getName() + " " + customerCopy.getAge());
}
}
class Customer {
private Long id;
private String name;
private int age;
public Customer() {
}
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
对 JavaBean 的内省操作
内省访问JavaBean有两种方法:一、通过属性描述符 PropertyDescriptor 来操作 Bean 对象
二、通过Introspector类获得Bean对象的 BeanInfo,然后通过 BeanInfo 来获取所以属性的描述器( PropertyDescriptor ),通过某个属性描述器就可以获取这个属性对应的 getter/setter 方法,然后通过反射机制来调用这些方法。
PropertyDescriptor
java.lang.Object
java.beans.FeatureDescriptor
java.beans.PropertyDescriptor
PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性
构造器
PropertyDescriptor(String propertyName, Class<?> beanClass)
通过调用 getFoo 和 setFoo 存取方法,为符合标准 Java 约定的属性构造一个 PropertyDescriptor。
常用方法
Method getReadMethod()
获得应该用于读取属性值的方法。
Method getWriteMethod()
获得应该用于写入属性值的方法。
Class<?> getPropertyType()
获得属性的 Class 对象。
Introspector 类
Introspector 类为通过工具学习有关受目标 Java Bean 支持的属性、事件和方法的知识提供了一个标准方法。
java.lang.Object
java.beans.Introspector
常用方法
static BeanInfo getBeanInfo(Class<?> beanClass)
在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件。
PropertyDescriptor[] getPropertyDescriptors()
获得 beans PropertyDescriptor。
java">/**
* 需求:使用 PropertyDescriptor 进行内省操作
*
* 思路:
*
* 步骤:
*
* 总结:
* 1.没有 PropertyDescriptor ,我们需要自己根据属性名字符串,解析为 getter、setter 名字符串,再用 Class 对
* 象的 getMethod 方法获取对应的 getter、setter 方法对象
* 2.有 PropertyDescriptor 之后,我们只需要传递一个属性名 字符串 + Class对象,就可直接使用 getReadMethod
* 获取到 getter 方法对象
*/
package cn.itcast.reflect;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
/**
* @class: IntrospectorDemo
* @package: cn.itcast.reflect
* @description: TODO
* @author: vivianZhao
* @date: 2013-7-22 上午9:15:36
* @version: 1.0
*/
public class IntrospectorDemo {
/**
* @method: main
* @description: TODO
* @param args
* @return: void
* @author: vivianZhao
* @date: 2013-7-22 上午9:15:36
* @version: 1.0
*/
public static void main(String[] args) throws Exception {
Point point = new Point(2, 5);
String proName = "x";
System.out.println(getProperty1(point, proName));
setProperty(point, proName, 8);
System.out.println(getProperty2(point, proName));
}
private static void setProperty(Point point, String proName, int value)
throws Exception {
PropertyDescriptor proDescriptor = new PropertyDescriptor(proName,
Point.class);
Method methodSetX = proDescriptor.getWriteMethod();
methodSetX.invoke(point, value);
}
private static int getProperty1(Point point, String proName)
throws Exception {
PropertyDescriptor proDescriptor = new PropertyDescriptor(proName,
Point.class);
Method methodGetX = proDescriptor.getReadMethod();
Object objx = methodGetX.invoke(point);
return Integer.parseInt(objx.toString());
}
private static int getProperty2(Point point, String proName)
throws Exception {
BeanInfo beanInfo = Introspector.getBeanInfo(point.getClass());
PropertyDescriptor[] proDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor prop : proDescriptors) {
if (prop.getName().equals(proName)) {
Method methodGetx = prop.getReadMethod();
Object objx = methodGetx.invoke(point);
return Integer.parseInt(objx.toString());
}
}
return 0;
}
}
class Point {
private Integer x;
private Integer y;
public Point() {
}
public Point(Integer x, Integer y) {
super();
this.x = x;
this.y = y;
}
public Integer getX() {
return x;
}
public void setX(Integer x) {
this.x = x;
}
public Integer getY() {
return y;
}
public void setY(Integer y) {
this.y = y;
}
}
使用BeanUtils工具包操作JavaBean
java">/**
*
*/
package cn.itcast.reflect;
import org.apache.commons.beanutils.BeanUtils;
/**
* @class: IntrospectorDemo
* @package: cn.itcast.reflect
* @description: TODO
* @author: vivianZhao
* @date: 2013-7-22 上午9:15:36
* @version: 1.0
*/
public class IntrospectorDemo {
/**
* @method: main
* @description: TODO
* @param args
* @return: void
* @author: vivianZhao
* @date: 2013-7-22 上午9:15:36
* @version: 1.0
*/
public static void main(String[] args) throws Exception {
Point point = new Point(2, 5);
String propertieName = "x";
BeanUtils.setProperty(point, propertieName, "8");
System.out.println(point.getX());
System.out.println(BeanUtils.getProperty(point, propertieName));
System.out.println(BeanUtils.getProperty(point, propertieName).getClass()
.getName());
BeanUtils.setProperty(point, propertieName, 8);
System.out.println(BeanUtils.getProperty(point, propertieName).getClass()
.getName());
// 我们看到虽然属性x的类型是Integer,但是我们设置的时候无论是Integer还是String,BeanUtils的内部
// 都是当成String来处理的。
}
public static class Point {
private int x;
private int y;
public Point() {
}
public Point(Integer x, Integer y) {
super();
this.x = x;
this.y = y;
}
public Integer getX() {
return x;
}
public void setX(Integer x) {
this.x = x;
}
public Integer getY() {
return y;
}
public void setY(Integer y) {
this.y = y;
}
}
}
BeanUtils 操作的类必须是 public 修饰过的,所以这里吧 Point 设置 pulic static 了,把 JavaBean 放在某个类的里面,这种在正式使用的时候,估计不会有这种设计,因为 JavaBean 本来就是公开的,用来传递数据的类。
java">/**
* 需求:演示 BeanUtils 支持 javabean 属性的级联操作
*/
package cn.itcast.reflect;
import java.util.Date;
import org.apache.commons.beanutils.BeanUtils;
/**
* @class: BeanUtilsDemo
* @package: cn.itcast.reflect
* @description: TODO
* @author: vivianZhao
* @date: 2013-7-22 下午1:20:10
* @version: 1.0
*/
public class BeanUtilsDemo {
/**
* @method: main
* @description: TODO
* @param args
* @return: void
* @author: vivianZhao
* @date: 2013-7-22 下午1:20:10
* @version: 1.0
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Person person = new Person();
String propertyName = "birthday.time";
System.out.println(BeanUtils.getProperty(person, propertyName));
}
public static class Person {
private Date birthday;
public Person() {
birthday = new Date();
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
}
PropertiesUtils类
java"> //eanUtils工具包 和logging包实现对JavaBean的操作
//利用Beanutils工具包可以对嵌套的JavaBean类中的属性进行操作
BeanUtils.setProperty(jbc, "brithday.time", 111);
System.out.println(BeanUtils.getProperty(jbc, "brithday.time"));
//PropertyUtils工具类传入的参数和拿出的参数必须和属性本身的类型一致
PropertyUtils.setProperty(jbc, "brithday.time", 123);
System.out.println(PropertyUtils.getProperty(jbc, "brithday.time"));
//利用BeanUtils工具实现JavaBean与Map的转换
Map map = BeanUtils.describe(jbc);
System.out.println(map);
BeanUtils.setProperty(map, "name", "xx");
System.out.println(map);
将 Java 的反射以及内省应用到程序设计中去可以大大的提供程序的智能化和可扩展性。有很多项目都是采取这两种技术来实现其核心功能,例如我们前面提到的 Struts ,还有用于处理 XML 文件的 Digester 项目,其实应该说几乎所有的项目都或多或少的采用这两种技术。在实际应用过程中二者要相互结合方能发挥真正的智能化以及高度可扩展性。