java基础——第十七章:反射

 

整章反射:

 

1、反射定义:

 

反射是在程序运行中对任意应该类,都可以知道这个类的所有属性和方法,对于任意应该对象,都能调用它的方法和获取它的属性,这种动态获取信息及动态调用对象方法的机制叫反射机制。

 

反射其实就是动态加载一个指定的类到内存中,形成类的字节码文件对象(或者内存中已加载该类的字节码文件对象),并获取该类中我们所需要的内容。再将这些内容封装成相对应的对象,便于对这些内容进行操作。

反射技术可以对一个类进行拆分,并动态获取类中的特有内容。


 

反射的好处:大大的增强了程序的扩展性。 

2、反射的步骤: 

(1)获取指定名称的字节码文件对象(Class类对象)。 

(2)获得类的属性、方法或构造函数(或者父类、子类、实现的接口等)。 

(3)访问属性、调用方法、调用构造函数创建对象。

 

3、Class类

 

Class类是程序中的类的字节码文件对象,只要在程序中出现的类型,都有各自的Class实例对象
Class类是反射的基础,只有获得了Class类对象才能对程序进行反射操作

得到Class对象的三种方式:

(1)Class cla = 类名.class    需要明确类名,但不需要有对象

(2)Class cla = 对象.getclass();    需要就明确具体的类,并创建该类对象

(3)Class cla = Class.forName("完整的类名");    编译时不需要明确类,运行时可以动态传入类名


 

9个预定义的Class实例对象:

八个基本数据类型和void


 

基本类型的class和TYPE:

Integer.class    包装类的字节码(与对应的基本类型的字节码对象不同)

Integer.TYPE     该字段返回对应的基本类型的字节码文件对象

int.class==Integer.TYPE    这两者是相同的

 

方法:

Constructor  Class对象 .getConstructor(参数列表的Class对象);   得到对应参数的构造方法

Constructors[]  Class对象.getConstructors();    得到某个类中所有的构造方法的数组

Method  Class对象.getMethod(方法名,参数列表的Class对象)    获取对应类的对应参数类型的方法

Method[]  Class对象.getMethods()    不多说,类似上面

Field  Class对象.getField("属性名")    获取对应类的成员属性

Field[]  Class对象.getFields()   不多说,你懂的

String  Class对象.getName()    获取类名

Package Class对象.getPackage()    获取此类的包

Class   Class对象.getSuperclass()    获取此类的父类对象

Object  Class对象.newInstance()    得到对应该类的无参构造方法创建的示例对象

 

 
···

 

boolean  Class对象.isPrimitive();   判断是否是基本类型的字节码文件对象
 
boolean  Class对象.isArray();   判断是否是数组类型的字节码文件对象
···
 
 


 

利用反射动态创建相应类的实例对象

步骤:

(1)寻找该名称类文件对象,如果内存中没有就从文件中加载进内存

Class clazz=Class.forName(类名);

(2)产生该类的对象(只能产生无参数构造方法构造的该类对象)

Object obj=clazz.newInstance();

(3)得到某一个指定构造方法

Constructor  constructor= Class对象.getConstructor(参数列表的Class对象);

(4)创建实例对象

Object obj=constructor.newInstance(创建对象时传入的参数);

代码展示:

 

/*
需求:利用反射动态创建相应类的实例对象
思路一:
(1)通过传入类名创建一个Class类(字节码文件对象),如果内存中没有就从class文件中加载进内存
(2)用Class类中的newInstance方法产生该类用无参构造函数创建的对象

思路二:
(1)创建一个该类的Class类对象
(2)得到该类的某个指定参数的构造方法对象
(3)用构造方法对象的newInstance方法创建对应初始化参数的实例对象

*/
import java.lang.reflect.*;

public class TestRef 
{
	int i;
	
	//构造方法
	public TestRef()
	{
		i = 1;
	}
	public TestRef(int i)
	{
		this.i = i;
	}

	public static void main(String[] args) throws Exception
	{
		//获取该类的字节码文件对象
		Class cla = Class.forName("TestRef");

		//用Class类中的实例化方法直接创建一个无参构造的该类对象
		TestRef r1 = (TestRef)cla.newInstance();    //返回的是Object,要强转
		r1.show();


		//获取该类的相应构造方法对象
		//如果构造方法不是公共的,要用getDeclaredConstructor方法,可以获取到所有声明的构造方法
		//规律:获取方法、属性等封装成对象也类似,需要获取非公共的就用方法名中间加了“Declared”的对应方法
		Constructor con = cla.getConstructor(int.class);  
		TestRef r2 = (TestRef)con.newInstance(5);    //创建对象,传入相应参数
		r2.show();

 		//获取一般方法并调用
		Method met = cla.getMethod("show");
		met.invoke(r1);    //需要明确对象

	}
	public void show()
	{
		System.out.println("haha"+i);
	}
}
 

 


 

4、Constructor类 

 

构造方法类:代表某个类中的一个构造方法抽取出来封装成对象。

 

(1)获取构造方法类

Constructor  constructor= Class对象.getConstructor(参数列表Class对象);

(2)创建实例对象,(参数需传入与上面相同类型的)如果需要获取到相应对象,可以加强制转换动作

Object obj =constructor.newInstance(传入对应参数);

5、Field 

 利用反射拆分出来的成员属性的封装类,代表某个类中的一个成员属性

 

对一个类中的属性进行反射。

ReflectPoint  rp=new ReflectPoint(3,5);    假设自定义的该类中有一个私有的“x"变量和公有的”y“变量

Field fieldy = rp.getClass().getField ("Y");    用属性名获取公有的属性对象

Field fieldx = rp.getClass().getDeclaredField("x")    获取属性对象,不管是私有还是被保护的

fieldx.setAccessible(true);    设置java语言访问检查,true为不检查(暴力反射

fieldx.get(pt1)    取出Y的值

 

将所有字符串字段中的"b"全部变成”a"

Field[] fields=obj.getClass().getFields();    获取类中的全部属性对象 

for(Field field :fields){

if(field.getType()==String.class){//如果是字符串

String oldValue = (String)field.get(obj);//获取字符串内容

String newValue = oldValue.replace('b','a');将字符串内容替换

field.set(obj,newValue);将新值赋给对象           

 

6、Method类 

成员方法类,表示某被反射类中的一个成员方法封装成的对象

 

用反射的形式获取成员方法(以String为例子):

Method  method = String.class.getMethod("split",String.class);    前面是方法名,后面是参数Class对象,可变参数的,无参可以不写

method.invoke(new String("asbasb"),"a");    前面是该方法作用的对象,后面是方法传入的参数

  

7、Array类

数组的反射类

 

方法:

get(ArrayObject,index)    用反射的形式获取指定数组指定索引的值

getInt(ArrayObject,index)    以int形式返回数组对象中索引组件的值

getByte(ArrayObject,index)    以byte形式返回数组对象中索引组件的值

···     你懂的

set(ArrayObject,index,newObjectValue)    将指定数组指定索引组件的值设为新值

setInt(ArrayObject,index,int)    你懂的···

···

getLength(ArrayObject)    获取指定数组的长度

newInstance(ClassTYPE,int)    创建具有指定组件类型和长度(维度)的数组

 

8、反射获取带泛型的属性(Type接口的使用)

 

通过指定的Class对象,都可以获得该类里面所有的Field,无论该Field的访问权限是什么。

获得Field对象后就可以使用getType() / getAccessible()方法来获取其类型。

 

但这种方式只能获取到普通属性的Field类信息,如果属性带有泛型,则无法获取到对应泛型的信息

 

泛型类型又叫参数化类型,可以理解为带参数的类型,就像带参数的方法,方法和类型属性都是类中的成员,既然方法可以带参数,那么类型属性也可以带属性,表示该类型属性中限定了一个参数属性,这是我对泛型的理解。

Type接口是java编程语言中所有类型的公共高级接口,它包含了原始类型、参数化类型、数组类型、类型变量和基本类型。

既然如此,它就应该有对应的操作这些类型的方法,其中也有操作参数化类型(泛型类型)的方法,但是很遗憾,这个接口中没有任何方法,仅作为表示关系作用,所幸的是它有一些子类接口用于对相应的类型进行操作

 

那么通过查阅,最终得到获取参数化类型的步骤:

Type genType = Field对象.getGenericType()    返回通用类型(即原来是什么类型,就准确反映出什么类型)

(ParameterizedType)genType    强转Type为参数化类型

Type[]  getActualTypeArguments()    返回此类型中的实际类型参数的Type类型的数组

 

利用反射获取带泛型的类型

步骤:

获取当前类的Class对象

获取目标字段

获取字段的准确类型 getGenericType()

强转至子类ParameterizedType (这里就已经获得了该泛型类的准确类型)

获得泛型中的实际参数类型 getActualTypeArguments()(即获得泛型类型的内部所有元素的数组)

 

 
 
本章总结:
1、反射是在程序运行中动态获取对象的类中的所有属性、方法、构造函数等信息的机制。利用反射可以实现程序框架等功能。
2、Class类是反射机制的基础,获取到Class对象就可以从该对象中抽取出被反射类的所有信息,从而对这些信息进行操作,比如调用构造方法创建该类对象,调用普通方法实现功能,获取属性得到或修改信息。
3、反射机制将从Class对象中抽取出来的各种信息都封装成了类,以便对这些信息进行操作。这些类有Constructor(构造方法)、Field(属性)、Method(方法)、Type(总的类型)、Package(包)等等。
 
 
 
 
 
 
 
 
 
 

你可能感兴趣的:(java编程)