------- android培训、java培训、期待与您交流! ----------
反射
当创建一个对象时有两种思路,在传统的方式中我们在编译阶段,就必须指明这个类的类型。还用一种方式就是反射,它可以在运行阶段获取一个Class对象,然后根据这个Class来创建对象。
Class类
Class类是反射的基础。
每一个类中都有构造器,变量,方法,这些东西都是共性的。所以应该有一个特殊的类,这个类规定了普通类中构造器,变量,方法各个部分应该如何定义。Class类就是这个特殊的类。Class对象对应各个类在内存中的字节码,例如ArrayList类的字节码,等等。每一个类都是一个Class对象。可以理解为每当编译一个新类是,就会创建一个Class对象(此对象,被保存在一个同名的.class文件中)。
获取各个字节码对应的实例对象( Class类型)
有三种方法
类名.class,例如,System.class
对象.getClass(),例如,new Date().getClass()
Class.forName("类名"),例如,Class.forName("java.util.Date");
九个预定义Class实例对象:
有九种预定义的 Class 对象,表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与
其表示的基本类型同名,即 boolean、byte、char、short、int、long、float 和 double。
数组类型的Class实例对象
Class.isArray()
反射
反射就是把Java类中的各种成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示, 一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。
Constructor类
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"));
Class.newInstance()方法:
例子:String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。如果没有默认构造器,会抛出异常。
Field类
Field类代表某个类中的一个成员变量
获取一个类中的成员变量
ReflectPoint point = new ReflectPoint(1,7);
Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
此时y表示的是ReflectPoint类中成员变量,当我们需要获取某个对象上的变量时。
需要用到方法:
Object get(Object obj) 返回指定对象上此 Field 表示的字段的值。
例如System.out.println(y.get(point)); 打印point对象上的成员变量y。
当成员为私有时:
x为私有变量,getDeclaredField("x")可获得私有变量x.
Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
设置私有变量x.
x.setAccessible(true);
类ReflectPoint代码
package cn.itcast.day1;
import java.util.Date;
public class ReflectPoint {
private int x;
public int y;
public ReflectPoint(int x,int y){
super();
this.x = x;
this.y = y;
}
}
Method类
Method类代表某个类中的一个成员方法
得到类中的某一个方法:
例子:Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式: System.out.println(charAt.invoke(str, 1));
如果传递给Method对象的invoke()方法的第一个参数为null,表示Method对象是一个静态方法。
用反射方式执行某个类中的main方法
目标:
写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
问题:
启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[]args),通过反射方式来调用这个main方法时,需要传入一个数组。
Class clazz = Class.forName(arg[0]);
Method mMain = clazz.getMethod("main", String[].class);,
mainMethod.invoke(null,new String[]{"xxx"});
此时涉及到一个知识点
jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),
按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时。jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.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"}); ,
编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了.
Method mMain = clazz.getMethod("main", String[].class);
mMain.invoke(null,new Object[]{new String[]{"aaa","bbb"}});
mMain.invoke(null,(Object)new String[]{"aaa","bbb"});
数组的反射
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象(此处比较与值关)。代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
private static void printObject(Object obj) {
Class clazz = obj.getClass();
//判断clazz是否为数组类,如果是则打印其中的所有元素
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);
}
}
Arrays.asList()方法处理int[]和String[]时的差异。
asList(T... a) 返回一个受指定数组支持的固定大小的列表
当传入的是int[]时,整个数组将作为list中的一个元素。
String[]中的每个字符串都将变为list中的一个元素。
反射的作用实现框架功能
作为初学者简单理解是:框架就是一些类和接口的集合,通过这些类和接口协调来完成一系列的程序实现。
框架是一开始就已存在的,我们在后期需要不断的添加各种功能,这时我们就需要用到反射,将这些功能添加进去。
主要步骤是,首先通过配置文件的方式,将需要添加到框架中去的类的名称,传递到框架中去。然后再框架中通过方式的方式创建此类。并实现相应的功能。
框架的好处就在于我们可以根据后期需要不断的添加各种功能。
综合案例
采用配置文件加反射方式创建HashSet实例对象。
InputStream ips =
ReflectTest2.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();
config.properties文件.
className=java.util.HashSet