Java高新技术之反射

来源:互联网 发布:mac山狮系统 编辑:程序博客网 时间:2024/05/16 14:57

Class类

Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。Java程序中的各个Java类,它们是否属于同一类事物,是不是可以用一个类来描述这类事物呢?这个类的名字就是Class,要注意与小写class关键字的区别哦。Class类描述了哪些方面的信息呢?类的名字,类的访问属性,类所属于的包名,字段名称的列表、方法名称的列表,等等。学习反射,首先就要明白Class这个类。写如下代码进行对比理解:

Person p1 = new Person("zhangsan");Person p2 = new Person("lisi");Class x1 = Vector.class;//Vector类在内存里的字节码Class x2 = Date.class;//Date类在内存里的字节码

Class对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。

一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型就是Class类型。另外,类加载的时候会先判断内存中是否已经有了,有了就直接使用,没有才去加载

获得类的字节码文件对应的实例对象有三种方法:

1、通过类的静态属性class获取
      Class cls =System.class
2、用对象的getClass方法
      Class cls2=p1.getClass();
3、使用Class类中的静态方法forName
      Class cls3 =Class.forName(“name”);

String str1 ="abc";Class cls1=str1.getClass();Class cls2=String.class;Class cls3=Class.forName("java.lang.String");//下面结构都为true,说明用这三种方法得到的字节码文件对象是同一个System.out.println(cls1==cls2);System.out.println(cls2==cls3);
有九种预定义的 Class 对象(参看Class.isPrimitive方法),表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即booleanbytecharshortintlongfloatdouble。如:int.class==Integer.TYPE。

判断一个Class 对象是否表示一个数组类用Class.isArray方法,如int[].class.isArray()

反射

反射就是把Java类中的各种成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。

为什么要使用反射?

因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new 某个类的实例对象了,而要用反射方式来做。

通过反射创建一个类的对象

通过反射创建一个类的对象有两种方法

1、通过Class的getConstructor方法得到该类的构造方法,此方法返回一个Constructor对象,然后通过Constructor的newInstance方法返回该类的实例对象,此方法可以指定构造方法进行实例化

Constructor constructor = String.class.getConstructor(StringBuffer.class);// 指定构造方法String str = (String) constructor.newInstance(new StringBuffer("abc"));// 并使用该构造方法
2、通过Class类的newInstance方法直接创建对象,但是这里只能通过无参的构造方法进行实例化
String obj=(String)Class.forName("java.lang.String").newInstance();

成员变量的反射

Field类代表某个类中的一个成员变量

我们先创建一个类,作为要反射的类

public class ReflectPoint {private int x;public int y;public ReflectPoint(int x, int y) {this.x = x;this.y = y;}}
获取成员变量
ReflectPoint ref=new ReflectPoint(3, 5);//取得属性y的名称fieldY不是对象上的变量,而是类上的变量Field fieldY=ref.getClass().getField("y");//从对象ref的引用中取得y的值System.out.println(fieldY.get(ref));//当成员变量是私有的时候getField会NoSuchFieldException异常,这时候可以用getDeclaredFieldField fieldX=ref.getClass().getDeclaredField("x");//此时取值的时候应先设置变量可以读取(暴力访问)fieldX.setAccessible(true);System.out.println("x="+fieldX.get(ref));
练习:获取并修改成员变量的值

//得到类中所有的成员变量Field[] fields=ref.getClass().getFields();for(Field field:fields){if(field.getType()==String.class){//这里要用等号,因为是同一份字节码//从对象ref的引用中取得某一成员变量的值String oldValue=(String)field.get(ref);String newValue=oldValue.replace("b","c");//设置或修改对象ref的引用中取得某一成员变量的值field.set(ref, newValue);}}System.out.println(ref);

成员方法的反射

Method类代表某个类中的一个成员方法

String str2="abc";//方法跟对象没有关系 是类的方法 但是需要通过对象来调用,int.class传递的参数的类型Method method=String.class.getMethod("charAt", int.class);//通过invoke调用该方法,但要指定在某一对象上调用,如果对象为null。则说明该方法不需要对象可以调用 即为静态方法System.out.println(method.invoke(str2, 1));

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})形式。

main方法的反射

class TestMain{public static void main(String[] args) {for(String arg:args){System.out.println(arg);}}}
Method method2=Class.forName("darkhorse.enhance.day01.TestMain").getMethod("main",String[].class);//方法1method2.invoke(null, new Object[]{new String[]{"22","33","44"}});//方法2method2.invoke(null, (Object)new String[]{"22","33","44"});
通过getMethods方法可以获取类中所有的方法,包括该类的父类的所有方法
Method [] methods=Person.class.getMethods();for(Method meth:methods){System.out.println("person:"+meth.getName());}

数组的反射

具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象(此处比较与值无关)。代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。

示例:通过反射遍历数组

public class Reflect4Array {public static void main(String[] args) {int[] a1 = new int[]{1,2,3};printObject(a1);}private 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);}}}
java.lang.reflect.Array工具类用于完成对数组的反射操作。

反射的应用

通过反射加载配置文件信息

配置文件:config.properties

className=java.util.HashSet
对配置文件中的信息进行操作

public class ReflectDemo {public static void main(String[] args) throws Exception{//InputStream ips = new FileInputStream("config.properties");//绝对路径//InputStream ips =ReflectDemo.class.getClassLoader().getResourceAsStream("darkhorse/enhance/day01/config.properties");//InputStream ips =ReflectDemo.class.getResourceAsStream("config.properties");//若文件在当前路径,不用写路径,只需文件名InputStream ips =ReflectDemo.class.getResourceAsStream("resources/config.properties");//当前路径下的文件夹下//InputStream ips =ReflectDemo.class.getResourceAsStream("/darkhorse/enhance/day01/resources/config.properties");//相对于项目路径Properties props=new Properties();props.load(ips);ips.close();//可以对配置文件中的内容进行遍历Set<Object> keySet=props.keySet();Iterator<Object> its=keySet.iterator();while(its.hasNext()){String key=(String)its.next();System.out.println(props.get(key));}String className=props.getProperty("className");System.out.println(className);//通过配置文件中的className使用反射创建对象@SuppressWarnings("unchecked")Collection<ReflectPoint> collection=(Collection<ReflectPoint>)Class.forName(className).newInstance();ReflectPoint pt1=new ReflectPoint(3,3);ReflectPoint pt2=new ReflectPoint(5,5);ReflectPoint pt3=new ReflectPoint(3,3);collection.add(pt1);collection.add(pt2);collection.add(pt3);collection.add(pt3);System.out.println(collection.size());}}


原创粉丝点击