整章反射:
1、反射定义:
反射是在程序运行中对任意应该类,都可以知道这个类的所有属性和方法,对于任意应该对象,都能调用它的方法和获取它的属性,这种动态获取信息及动态调用对象方法的机制叫反射机制。
反射其实就是动态加载一个指定的类到内存中,形成类的字节码文件对象(或者内存中已加载该类的字节码文件对象),并获取该类中我们所需要的内容。再将这些内容封装成相对应的对象,便于对这些内容进行操作。
反射技术可以对一个类进行拆分,并动态获取类中的特有内容。
反射的好处:大大的增强了程序的扩展性。
2、反射的步骤:
(1)获取指定名称的字节码文件对象(Class类对象)。
(2)获得类的属性、方法或构造函数(或者父类、子类、实现的接口等)。
(3)访问属性、调用方法、调用构造函数创建对象。
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); } }
构造方法类:代表某个类中的一个构造方法抽取出来封装成对象。
(1)获取构造方法类
Constructor constructor= Class对象.getConstructor(参数列表Class对象);
(2)创建实例对象,(参数需传入与上面相同类型的)如果需要获取到相应对象,可以加强制转换动作
Object obj =constructor.newInstance(传入对应参数);
利用反射拆分出来的成员属性的封装类,代表某个类中的一个成员属性
对一个类中的属性进行反射。
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);将新值赋给对象
成员方法类,表示某被反射类中的一个成员方法封装成的对象
用反射的形式获取成员方法(以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(包)等等。