反射机制
在一个已有的java程序中,随着需求的变更,如果我们需要扩充或者改进程序,一般来说修改源代码不是一件好事。
而如果我们可以重新定义新的类,并且在原来程序的基础上进行扩充,那我们就不用再为修改复杂的源代码而烦恼。换
言之,在不改变原来类的源代码的情况下能不能定义新的,再保存原有功能的基础上进行扩展呢?
幸运的是,java的反射机制,很好的解决了这个问题。新定义的类经过编译以字节码文件产生,形成class文件(字节
码文件)。java反射技术将该class文件打开,进行解剖,获取内部数据结构。由于应用程序不能进行更改,但程序运行时
可以指定class文件的内部数据,对class文件的内部数据进行获取和处理。对于应用程序来说,不需要知道我们定义新的可
扩展的类,只需要知道新的类的字节码交给它,它就会用反射技术来处理。JAVA反射机制是在运行状态中,对任意一个类,
都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个属性和方法;这种动态获取信息以及
动态调用对象的方法的功能称为java语言的反射机制。需要注意的是,要想解剖一个类必须要先获取该类的class文件对象。
而解剖使用的就是java.lang.Class类中的方法,该类的放有关方法可参考JAVA的API官方文档。竟然我们可以对程序进行扩展,
那我们自己定义的类能不能“乱写”、瞎搞呢?程序为了更加安全的使用新扩展的类,反射机制对其新的类进行一种特殊的
接口约束。所以使用反射机制是安全的。
在任何一门面向对象的编程语言中,对象一定是真实存在的事物,而类是事物特征的抽象描述。java中的java.lang.Class
类是用于描述类对象的类。Class类的实例表示正在运行的java应用程序中的类和接口。使用反射机制都能获取到任意一个类
的所有属性和方法。那怎么样才能获取到Class文件对象的方法呢?那下面将介绍获取Class文件对象的三中方法。为了更好形
象的描述以下的内容,我们创建了一个Good类。
public class Goods { public String goodsName; //商品名称 public double price; //价格 /*static { System.out.println("静态代码块"); }*/ //无参构造器 public Goods() { } //有参构造器 public Goods(String goodsName,double price) { this.goodsName = goodsName; this.price = price; } public void goods() { System.out.println("商品总揽"); } public void info(String gname, double gprice) { System.out.println(gname+"的价格是:"+gprice); } }
方法一:对象获取
使用某个类的对象直接调用getClass()方法,该方法返回一个Class对象。如果我们此时打印Class对象的返回值,
会发现打印出来的其实就是某个类的全名。比如:
Goods good = new Goods(); Class c = good.getClass(); System.out.println(c); //输出类的全名
方法二:类名获取
每个类型(包括基本类型和引用类型),JVM都会赋予该类型一个静态属性,而属性的名字就叫class。我们在自己写的
代码中是无法看见这个属性的,但是我们用类名去调用的时候,这个属性是确实存在的。相信我们都曾经在静态同步锁中遇
见过它,所有的类型都有class属性,如double.class等。
Class c = Goods.class; System.out.println(c); //输出类的全名
方法三:Class类的静态方法获取
使用静态方法forName(String className)进行获取,使用该方法的好处在于类名可以使用字符串,既然是字符串,那在使
用时就会提高它的灵活性。特别要注意的是className必须使用类的全名。在使用该方法时,所在的本地方法必须抛出异常,
如果嫌麻烦可以直接抛出Exception。
Class c = Class.forName("com.gzhxtc.win.Goods"); System.out.println(c); //输出类的全名
在类的加载器那里为类创建class文件的时候,class文件对象是唯一的吗?为了证明这个问题,我们对同一类分别
创建了两个对象,然后比较两个对象的引用和内用是否相同,而实验的结果告诉我们这的确是同一个对象。
Class c1 = Class.forName("com.gzhxtc.win.Goods"); Class c2 = Class.forName("com.gzhxtc.win.Goods"); //判断两个对象的引用是否想等,结果为:true System.out.println(c1 == c2); //判断两个对象的内容是否想等,结果为:true System.out.println(c1.equals(c2));
前面介绍了获取Class文件对象的三中方法,这三种方法各有千秋,但用的最多的是第三种方法——Class类的
静态方法获取。该方法由于一个字符串的传输使得程序更加灵活。现在有了这三种获取Class文件对象的方法,我
们接下来便切入正题,在程序中如何获取类中的成员属性和方法呢?下面将为类中各种变量的获取进行介绍。
一、反射获取构造方法
1. 无参数构造器
/* * 获取类中所有public修饰的构造器 * 在Class类中有很多供我们使用的方法,一般用的比较多的是getConstructors()方法, * 该方法可以获取class文件对象中的所有被public修饰的构造方法。它的返回值是 * Constructor[]数组,即接受多个构造器。Constructor是描述构造方法的类。 */ Class c = Class.forName("com.gzhxtc.win.Goods"); Constructor[] cons = c.getConstructors(); for (Constructor constructor : cons) { System.out.println(constructor); } //输出构造器的全名: //public com.gzhxtc.win.Goods() //public com.gzhxtc.win.Goods(java.lang.String,java.lang.String) /* * 获取指定的public修饰的构造器 */ Class c = Class.forName("com.gzhxtc.win.Goods"); //newInstance()方法这里因为是无参构造器,所以不传参数 Object obj = c.newInstance(); //如果要想让obj对象能使用类中的方法,必须强制转换类型 Goods good = (Goods)obj; //接下来good就可以调用Goods中的方法了 good.goods(); System.out.println();//输出:商品总揽
2.有参数构造器
/* * 获取有参的public修饰的构造器,需要传参数的class文件对象 */ Class c = Class.forName("com.gzhxtc.win.Goods"); //获取有参构造器 Constructor constructor = c.getConstructor(String.class, double.class); Object obj = constructor.newInstance("电热棒",99.99); Goods good = (Goods)obj; System.out.println(good.goodsName); //输出:电热棒 System.out.println(good.price); //输出:99.99
二、反射获取类的成员变量
/* * 使用getFields()方法,获取class文件中所有public的成员变量 * 在导包的时候导的是lang包里面的,千万别导错 */ Class c = Class.forName("com.gzhxtc.win.Goods"); Field[] fileds = c.getFields(); for (Field field : fileds) { System.out.println(field); } //获取指定的public的成员变量 Field field = c.getField("goodsName"); Object obj = c.newInstance(); //运行 //修改成员变量,参数必须要有对象的支持和修改后的值 field.set(obj, "电饭煲"); Goods good = (Goods)obj; System.out.println(good.goodsName); //输出:电饭煲
三、反射获取成员方法
1.无参public的成员方法
/* * 使用getMethons()获取的class文件中的所有public方法(包括继承的) */ Class c =Class.forName("com.gzhxtc.win.Goods"); Method[] methods = c.getMethods(); for (Method method : methods) { System.out.println(method); }
2. 有参public的成员方法
/* * 获取指定的成员方法 */ Class c =Class.forName("com.gzhxtc.win.Goods"); Object obj = c.newInstance(); //运行 Method method = c.getMethod("info",String.class,double.class); //调用Method类的invoke method.invoke(obj, "电冰箱", 2999.0); //输出:电冰箱的价格是:2999.0
以上是反射技术的基础,关于反射技术的更多方法请参考java的API1.8官方文档。