反射(二)——反射机制

            反射机制

  在一个已有的java程序中,随着需求的变更,如果我们需要扩充或者改进程序,一般来说修改源代码不是一件好事。

而如果我们可以重新定义新的类,并且在原来程序的基础上进行扩充,那我们就不用再为修改复杂的源代码而烦恼。换

言之,在不改变原来类的源代码的情况下能不能定义新的,再保存原有功能的基础上进行扩展呢?

 

  幸运的是,java的反射机制,很好的解决了这个问题。新定义的类经过编译以字节码文件产生,形成class文件(字节

码文件)。java反射技术将该class文件打开,进行解剖,获取内部数据结构。由于应用程序不能进行更改,但程序运行时

可以指定class文件的内部数据,对class文件的内部数据进行获取和处理。对于应用程序来说,不需要知道我们定义新的可

扩展的类,只需要知道新的类的字节码交给它,它就会用反射技术来处理。JAVA反射机制是在运行状态中,对任意一个类,

都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个属性和方法;这种动态获取信息以及

动态调用对象的方法的功能称为java语言的反射机制。需要注意的是,要想解剖一个类必须要先获取该类的class文件对象。

而解剖使用的就是java.lang.Class类中的方法,该类的放有关方法可参考JAVAAPI官方文档。竟然我们可以对程序进行扩展,

那我们自己定义的类能不能“乱写”、瞎搞呢?程序为了更加安全的使用新扩展的类,反射机制对其新的类进行一种特殊的

接口约束。所以使用反射机制是安全的。

 

  在任何一门面向对象的编程语言中,对象一定是真实存在的事物,而类是事物特征的抽象描述。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官方文档。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

猜你喜欢

转载自www.cnblogs.com/Lynnblog/p/8962165.html