Java 高新技术(2)
---------- android培训、java培训、期待与您交流!----------
1、反射的概念
什么是字节码:它是经过编译器预处理过的一种文件,是Java执行文件存在的形式,它本身是二进制文件,但是不可以被系统执行,需要虚拟机加载执行。反射就是把Java类中的各个成分映射成相应的java类,一个类用一个Class类的对象表示,一个类中的组成部分,如成员变量、构造方法、普通方法等信息也可以用一个个java类来表示,Class类中提供了很多方法,而且Field、Method、Constructor等可以用于获取类中内容所属于的类。
Class的实例对象:加载的每个类在内存中的字节码就是一个Class对象,在Java中允许通过一个实例化对象找到一个类的完整信息,获取的方法主要有三种,如下面的代码中所示:
public class DemoReflect {
public static void main(String[] args) throws ClassNotFoundException {
String s1 = new String("abc");
Class c1 = s1.getClass();
Class c2 = String.class;
Class c3 = Class.forName("java.lang.String");
System.out.println(c1 == c2);
System.out.println(c1 == c3);
System.out.println(c3 == c2);
}
}
运行结果:
true
true
true
(1) 可通过一个类的实例化对象获取,由于每个类都是Object类的子类,而Object类中定义了getClass方法可以获取类的信息
(2) 可通过类中的Class属性获得,每个类中都有一个Class属性
(3) 可调用Class类的静态方法forName获取
Class类的常用方法有:
public String getName(); Class类中最常用的方法getName以 String 的形式返回此 Class 对象所表示
的实体(类、接口、数组类、基本类型或 void)名称
public package getPackage();得到一个类的包信息
public Class[] getInterface();得到一个类中所实现的全部接口
public Method[] getMethods();得到一个类中的全部方法
public Method getMethod(String name, Class ...Parameter Types);得到一个Method对象,并设置
一个方法中的所有参数类型
public Field[] getDeclaredFields();得到本类中单独定义的全部属性
public Field[] getFields();得到本类继承而来的全部属性
public Constructor[] getConstructors();得到一个类中的全部构造方法
public static Class<?> forName(String className);得到实例化Class对象 public Object newInstance(); 根据Class定义的类实例化对象
public Class<?> getComponentType(); 返回表示数组组件类型的 Class
public Class getSuperclass(); 返回表示此 Class 所表示的实体的超类的 Class
public boolean isArray(); 判定此 Class 对象是否表示一个数组类
从以上三种方法可以看出,前两种都需要传入导入一个明确的类,如果一个类不明确的时候,使用起来就会受到一些限制,所以第三种方式更加的灵活,也是最常用的一种方式。
2、反射的应用
(1) Constructor类Constructor类代表一个类中的一个构造方法,若想获取类的全部构造方法,则可 通过使用Class的方法getConstructors()即可获取,如:Constructor[] con = Class.forName("java.lang.String").getConstructors()。
还可以得到某个单独的构造方法,如:
Constructor con = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
这里我们使用了Class中的方法: public Constructor<T> getConstructor(Class<?>... parameterTypes),可以根据可变参数列表得到具有相应参数的构造方法。
获取构造方法后可以使用该构造方法创建一个对象,即使用Constructor类中的newInstance方法,如下代码所示:
import java.lang.reflect.Constructor;
public class DemoReflect1 {
public static void main(String[] args) throws Exception {
Constructor con1 = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
String s = (String)con1.newInstance(new StringBuffer("abcd"));
System.out.println(s);
}
}
在Class类中还存在一个无参的newInstance方法,使用该方法可以通过Class类本身实例化一个其他类的对象,但是必须要保证被实例化的类中存在一个无参构造方法。
public class DemoReflect {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Person");
System.out.println(c);
Person p = (Person) c.newInstance();
p.setAge(20);
System.out.println(p.getAge());
}
}
class Person{
private String name;
private int age;
public Person(){}
public Person(String name, int age){
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
(2) Field类
Field类表示一个类中的成员变量,使用Class类中的方法: public Field[] getDeclaredFields()与public Field[] getFields()可以得到本类中单独定义的全部属性或者是 得到本类从父类继承而来的或者是继承接口的全部属性。这两种方法返回的都是Field类型的数组,每一个对象表示一个类中的属性,Field类中的主要方法有:
public Object get(Object obj);得到一个对象中属性的具体内容
public void set(Object obj, Object value);设置指定对象中属性的具体内容
public int getModifiers();得到属性的修饰符
public String getName();返回此属性的名称
public boolean isAccessible();判断此属性是否可被外部访问
public void setAccessible();设置一个属性是否可被外部访问
以下代码为通过反射获取Person类中的私有属性的内容:
import java.lang.reflect.Field;
public class DemoReflect1 {
public static void main(String[] args) throws Exception {
Person p = new Person("hao",20);
Class c = p.getClass();
Field[] f = c.getDeclaredFields();//获取全部的属性
for(int i=0;i
下面的程序演示了使用反射获取对象中的属性,并修改其属性
import java.lang.reflect.Field;
public class DemoReflect1 {
public static void main(String[] args) throws Exception {
Person p = new Person("hao",20,"china");
Class c = p.getClass();
Field[] f = c.getDeclaredFields();//获取本类全部的属性,包括private,但是访问需要设置Accessible
for(Field s : f){
s.setAccessible(true);//设置私有属性可被访问
if(String.class == s.getType()){
String buf = (String)s.get(p);
buf = buf.toUpperCase();//将String类型的属性变为大写
s.set(p, buf);
}
System.out.println(s.get(p));//获取p对象各个属性的值
}
}
}
class Person{
private String name;
private String country;
private Integer age;
public Person(){}
public Person(String name, int age, String c){
this.name = name;
this.age = age;
this.country = c;
}
private Person(String name){
this.name = name;
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
(3) Method类
Method类表示类中的方法,要取得类中的方法,可以使用Class类中的getMethods方法,此方法返回一个Method类型的对象数组,Method类中的方法有:
public int getModifiers();取得本方法中的访问修饰符
public String getName();取得方法的名称
public Class<?> getParameterTypes();得到方法的全部参数类型
public Class<?> getReturnType();得到方法的返回值类型
public Class<?> getExceptionTypes();得到一个方法的全部抛出异常
public Object invoke(Object obj, Object...args);通过反射调用类中的方法
3、反射的深入应用
反射除了可以得到一个类中的结构之外,还能够调用类中指定的方法,并且可以通过反射完成对数组的操作。
(1) 通过反射调用一个类中的方法
想要调用类中的方法可以通过Method类完成,步骤如下:
(a) 通过Class类的getMethod方法取得一个Method对象,并设置此方法操作时所需要的参数类型;
(b) 使用invoke方法调用,并向方法中传递要设置的参数。
下面的程序演示了使用反射调用一个类中的main方法:
import java.lang.reflect.*;
public class DemoReflect2 {
public static void main(String[] args) throws Exception {
Method me = Class.forName("com.day2.Test")
.getDeclaredMethod("main", String[].class);
me.invoke(null, (Object)new String[]{"hello","java"});
}
}
class Test{
public static void main(String[] args){
for(String arg : args)
System.out.println(arg);
}
}
一个类中的属性必须封装,而属性的设置要通过setter及getter方法,那么使用反射进行setter及getter的设置是极为重要的应用。下面的程序演示了使用反射调用类中的setter及getter方法:
import java.lang.reflect.*;
public class DemoReflect2 {
public static void main(String[] args) throws Exception{
Person p1 = new Person();
setter(p1, "setName", "howard", String.class);
setter(p1, "setAge", 30, int.class);
System.out.println("姓名:");
getter(p1,"getName");
System.out.println("年龄:");
getter(p1,"getAge");
}
public static void setter(Object obj, String method, Object value, Class
c) throws Exception {
Method m = obj.getClass().getDeclaredMethod(method, c);
m.invoke(obj, value);
}
public static void getter(Object obj, String method) throws Exception {
Method m = obj.getClass().getDeclaredMethod(method);
System.out.println(m.invoke(obj));
}
}
class Person{
private String name;
private Integer age;
public Person(){}
public Person(String name, int age){
this.name = name;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
(2) 通过反射操作数组
反射不仅能够应用在类上,还能够应用在数组上,即可以使用反射操作数组。
具有相同维数和相同数据类型、且长度可以不同的两个数组的Class为同一类型,具有相同的字节码。基本数据类型的一维数组可以当做Object使用,非基本类型的一维数组可以当做Object或者Object[]使用。
在java.lang.reflect包中使用Array类表示一个数组,可以通过此类取得数组长度、内容等操作,Array类的常用方法有:
public static Object get(Object array, int index);根据下标取得数组的内容
public static Object newInstance(Class<?> componentType, int length);根据已有的数组类型开辟新的数组对象
public static void set(Object array, int index, Object value);修改指定位置的内容
4、通过反射模拟框架的功能
采用配置文件加反射的方式创建ArrayList于HashSet的实例对象,比较程序运行的差异。
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
public class DemoReflect3 {
public static void main(String[] args) throws Exception {
//加载配置文件
InputStream in = DemoReflect3.class.getResourceAsStream("config.properties");
Properties prop = new Properties();
prop.load(in);
in.close();
String className = prop.getProperty("className");
System.out.println(className);
//根据加载的配置文件创建指定类型的集合
Collection c = (Collection)Class.forName(className).newInstance();
Person p1 = new Person("a",23);
Person p2 = new Person("b",33);
Person p3 = new Person("c",34);
c.add(p1);
c.add(p2);
c.add(p3);
c.add(p3);
System.out.println(c.size());
}
}
//定义一个config.properties文件,存放到与源文件相同的包下
//属性文件的内容为
className=java.util.HashSet
//然后修改属性文件的内容为,观察前后运行的结果
className=java.util.ArrayList
以上代码实现了通过外部配置文件改变程序功能,Java程序通过反射的方式从配置文件获取信息,并创建相应的类,这就简单的模拟了一个框架的功能。
---------- Windows Phone 7手机开发、.Net培训、期待与您交流! ----------