------ android培训、java培训、java基础学习技术总结、期待与您交流! ----------
反射
1. 反射的基石--> Class类-->
* java程序中的各个类属于同一类事物,描述这类事物的Java类名就是Class
* 对比提问:众多的人用一个什么类表示?众多的Java类用一个什么类表示?
* 人-->Person
* Java--> Class
* 对比问题:Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,
* Class类代表Java类,它的各个实例对象又分别对应什么呢?
* 对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。
* 一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容是不同的
* 这一个个的空间可分别用一个个的对象来表示,这些对象显然具有
* 相同的类型,这个类型是什么呢?
* 如何得到各个字节码对应的示例对象(Class类型)
* 类名.class,例如,System.class
* 对象.getClass(),例如,new Date().getClass()
* Class.forName("类名"),例如,class.forName("java.util.Date");
* 九个预定义Class实例对象
* 参看Class.isPrimitive方法的帮助
* int.class==Integer.TYPE
示例1:
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException {
String str1="abc";
Class cls1=str1.getClass();
Class cls2=String.class;
Class cls3=Class.forName("java.lang.String");
System.out.println(cls1==cls2);
System.out.println(cls1==cls3);
System.out.println(cls1.isPrimitive());
System.out.println(int.class.isPrimitive());
System.out.println(int.class==Integer.class);
System.out.println(int.class== Integer.TYPE); //
System.out.println(int[].class.isPrimitive()); //判断是否是原始类型
System.out.println(int[].class.isArray()); //判断是否为数组类型
}
}
* 数组类型的Class示例对象
* Class.isArray()
* 总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[]
* 反射就是把Java类中的各种成分映射成相应的java类。例如,一个Java类中用一个Class类的
* 对象来表示,一个类中的组成部分:成员变量,方法,构造方法,
* 包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个
* 的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造函数,
* 修饰符,包等信息,这些信息就是用相应的类的实例对象来表示,它们是Field\
* Method、Constructor、Package等等。
* 一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的
* 方法可以得到这些实例对象后,得到这些实例对象后有什么用呢?
*怎么用呢? 这正是学习和应用反射的要点。
*Constructor类
*Constructor类代表某个类中的构造方法
*得到某个类所有的构造方法
* 例子:Constrcutor[] 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"));
* Class.newInstance()方法:
* 例子:String obj=
* (String)Class..forName("java.lang.String").newInstance();
* 该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
* 该方法内部的具体代码是这样写的呢?用到了缓存机制来
* 保存默认构造方法的实例对象
* class-->constructor-->new object
示例2:
public class ReflectTest2 {
public static void main(String[] args) throws SecurityException, NoSuchMethodException,
IllegalArgumentException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Constructor constructor1=String.class.getConstructor((StringBuffer.class));
String str2=(String) constructor1.newInstance(new StringBuffer("abc"));// 需要产生同样类型的对象
System.out.println(str2.charAt(2));
}
}
* Field类
* Field类代表某个类中的一个成员变量
* 演示用eclipse自动生成Java类的构造方法
* 问题:得到的Field对象是对应到类上面的成员变量,
* 还是对应到对象上的成员变量?类只有一个,而该类的
* 实例对象有多个, 如果是与对象关联,哪关联的是哪一个对象呢?
* 所以字段FieldX代表的是x的定义,而不是具体的x变量。
示例1:
public class ReflectPoint {
private int x;
private int y;
Date birthday=new Date();
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
// TODO Auto-generated method stub
ReflectPoint pt1=new ReflectPoint(3,5);
Field fieldY =pt1.getClass().getField("y");
//fielY的值是多少呢?是5,错!fieldY不是对象身上的变量,而是类上,要用它
System.out.println( fieldY.get(pt1));
Field fieldX=pt1.getClass().getDeclaredField("x");
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));
}
@Override
public String toString() {
return "ReflectPoint [x=" + x + ", 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;
ReflectPoint other = (ReflectPoint) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}
* 作业:将任意String变量中的'b'改成'a'.
* Method类
* Method类代表某一个类中的一个成员方法
* 得到类中的某一个方法:
* 例子: Method charAt=
* Class.forName("java.lang.String").getMethod("charAt",int.class);
* 调用方法:
* 通常方式:System.out.println(str.chatAt(1));
* 反射方式: System.out.println(charAt.invoke(str,1));
* 如果传递给method对象的invoke()方法的一个参数为null,
* 这有着什么样的意义呢?说明该Method对象对应的是一个
* 静态方法!
* jdk 1.4和jdk 1.5的invoke方法的区别:
* Jdk1.5:public Object invoke(Object obj,Object... args)
* Jdk 1.4 public Object invoke(Object obj,Object[] args),
* 即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法是,数组中的
* 每一个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的
* 代码也可以用jdk 1.4改写为charAt.invoke("str",new Object[]{1}形式。
* 用反射的方式执行某个类中的main方法
* 目标
* 写一个程序,这个程序能够根据用户提供的类名,去执行类中的main方法。
* 问题:
* 启动Java程序的main方法的参数是一个字符串数组,即public static void
* main(String[] args),通过反射的方式来调用这个main方法时,如何为
* invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk 1.4的语法,
* 数组中的每一个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法
* 时,javac会到底按照那种语法进行处理,及把数组打散成为若干个单独的参数
* 。所以,在main方法传递参数时,不能使用代码
* mainMethod.invoke(null,new String[]{"xxx"});,javac只把它当作jdk 1.4的语法进行
* 理解,而不把它当成jdk 1.5的语法解释,因此会出现参数类型不对的问题。
* 解决办法:
* mainMethod.invoke(null,new Object[]{new String[]{"xxx"});
* mainMethod.invoke(null,(Object)new String[]{"xxx"});,编译器会作特殊处理,
* 编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
示例2:
public class ReflectPoint2 {
private int x;
private int y;
public String str1="ball";
public String str2="basketball";
public String str3="itcast";
public ReflectPoint2(int x, int y) {
super();
this.x = x;
this.y = y;
}
public String toString(){
return str1+"::"+str2+"::"+str3;
}
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchMethodException, InvocationTargetException, ClassNotFoundException {
// TODO Auto-generated method stub
String str1="abc";
ReflectPoint2 pt2=new ReflectPoint2(3,5);
changeStringValue(pt2);
System.out.println(pt2);
Method methodCharAt= String.class.getMethod("charAt",int.class);
System.out.println(methodCharAt.invoke(str1, 1));
System.out.println(methodCharAt.invoke(str1, new Object[]{2}));
//TestArguments.main(new String[]{"abc","xyz","123"});
String startingClassName=args[0];
Method mainMethod=Class.forName(startingClassName).getMethod("main", String.class);
mainMethod.invoke(null, (Object)new String[]{"abc","xyz","123"});
}
private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException{
Field[] fields=obj.getClass().getFields();
for(Field field:fields){
if(field.getType()==String.class){
String oldValue=(String)field.get(obj);
String newValue=oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}
}
}
class TestArguments{
public static void main(String[] args){
for(String arg:args){
System.out.println(arg);
}
}
}
* 数组的反射
* 具有相同的维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
* 代表数组的Class实例对象的getSuperClass()方法返回的父类
* 为Object类对应的Class。
* 基本类型的一位数组可以当作Object类型使用,不能当作
* Object[]类型使用;非基本类型的一位数组,即可以当作Object类型使用
* ,又可以当作Object[]类型使用。
* Arrays.asList()方法处理int[]和String[]时的差异。
* Array工具类用于完成对数组的反射操作。
* 思考题:怎么得到数组中的元素类型?
示例3:
public class ReflectPoint3 {
public static void main(String[] args) {
int[] a1=new int[3];
int[] a2=new int[4];
int[][] a3=new int[2][3];
String[] a4=new String[3];
System.out.println(a1.getClass()==a2.getClass());
/* System.out.println(a1.getClass()==a4.getClass());
System.out.println(a1.getClass()==a3.getClass()); */
System.out.println(a1.getClass().getName());
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a4.getClass().getSuperclass().getName());
Object aObj1=a1;
Object aObj2=a4;
//Object[] aObj3=a1;
Object[] aObj4=a3;
Object[] aObj5=a4;
System.out.println(Arrays.asList(a1));
System.out.println(Arrays.asList(a4));
Object obj=null;
printObject(obj);
}
public 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);
}
}
}
* 反射的作用-->实现框架功能
* 框架与框架要解决的核心问题
* 我做房子卖给住户,由用户自己安装门窗和空调,我做的房子就是框架,
* 用户需要使用我的框架,把门窗插入我提供的框架中,框架与工具有区别,
* 工具类被用户的类调用,而框架则是调用用户提供的类
* 框架要解决的核心问题
* 我在写框架(房子)时,你这个用户可能还在上学,还不会写程序呢?
* 我写的框架程序怎样能调用你以后写的类(门窗)呢?
* 因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类
* 的实例对象了,而要用发射方式来做。
* 综合案例
* 先直接用new语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成
* ReflectPoint类的equals和hashcode方法,比较两个集合的运行结果差异。
* 然后改为采用配置文件加反射的方式创建ArrayList和HashSet的实例对象,比较
* 观察运行结果差异。
* 引入了eclipse对资源文件的管理方式的讲解
* 内省 -->IntroSpector-->对JavaBean进行操作-->特殊的Java类-->javaBean的属性根据方法取出来
* 符合 int getAge()
* void setAge(int age) 这样规则的类叫做JavaBean。
* Age -->如果第二个字母是小的,则把第一个字母变成小的--age
* gettime-->time
* setTime-->time
* setCPU-->CPU
* isLast()-->last
* JDK中提供了对JavaBean进行操作的一些API,这套API就称为内省
示例6:
public class ReflectTest3 {
public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException {
// TODO Auto-generated method stub
//InputStream ips=new FileInputStream("config.properties");
//InputStream ips=ReflectTest3.class.getClassLoader().getResourceAsStream("cn/itcast/study2/config.properties");
InputStream ips=ReflectTest3.class.getResourceAsStream("config.properties");
Properties props=new Properties();//
props.load(ips);
ips.close();
String className=props.getProperty("className");
Collection collections=(Collection)Class.forName(className).newInstance();
//Collection collections=new HashSet();
ReflectPoint pt1=new ReflectPoint(3,3);
ReflectPoint pt2=new ReflectPoint(5,5);
ReflectPoint pt3=new ReflectPoint(3,3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
System.out.println(collections.size());
}
}
示例7:
public class IntroSpectorTest {
public static void main(String[] args) throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
// TODO Auto-generated method stub
ReflectPoint pt1=new ReflectPoint(3,5);
String propertyName="x";
//"x"-->"X"-->"getX"-->MethodX-->
Object retVal;
PropertyDescriptor pd = getProperty(pt1, propertyName);
Object value=7;
setProperty(pt1, pd, value);
System.out.println(BeanUtils.getProperty(pt1, "x").getClass());
BeanUtils.setProperty(pt1, "x", "9");
/*Map map={name:"zxx",age:18};
BeanUtils.setProperty(map, "name", "1hm");
BeanUtils.setProperty(map, "name", "111");*/
BeanUtils.setProperty(pt1, "birthday.time", "111");
System.out
.println(BeanUtils.getProperty(pt1, "birthday.time"));
PropertyUtils.setProperty(pt1, "x",9);
System.out.println(PropertyUtils.getProperty(pt1, "x"));
}
private static void setProperty(ReflectPoint pt1, PropertyDescriptor pd,
Object value) throws IllegalAccessException,
InvocationTargetException {
Method methodSetX=pd.getWriteMethod();
methodSetX.invoke(pt1, value);
System.out.println(pt1.getX());
}
private static PropertyDescriptor getProperty(ReflectPoint pt1,
String propertyName) throws IntrospectionException,
IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Object retVal;
PropertyDescriptor pd=new PropertyDescriptor(propertyName,pt1.getClass());
Method methodGetX=pd.getReadMethod();
retVal=methodGetX.invoke(pt1);
System.out.println(retVal);
return pd;
}
}