----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
对于反射的理解,从以前开始学习编程到工作,这么长的时间,通过平时的总结外加这次的学习,这终于算是理清了思路,首先我先说下为什么要使用反射?
在java中,我们通常要是使用一个类里面的方法、属性和构造函数的时候,首先思考的就是要获取这个类。通常的方法是:只要知道是那个类,只要获取它的对象就可以,对它内容进行操作了。问题就出来了,你要是不知道这个类或者不确定是那个类呢?那么你该怎么处理?或者你修改别人的架包里的某个私有成员变量的初始值等等,一些关于不确定某个类的内容时。你该怎么办?
针对这些问题,Java提供了一种反射机制,那解决上面的问题。
什么是反射机制呢?
就是对于任意类都可以调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能,简单的来说:就是将Java类中的各种成分映射成相应的java类,然后通过映射效果进行操作。
注意:反射机制破坏了java的封装机制,并且使用的时候,效率很低,不适合广泛使用。
其实呢,反射就是通过操作的Class类来达到映射效果。
Class类:Java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class,Class类描述了类的名字、访问属性、类所属的包名、字段名称的列表和方法名称的列表等信息。
当一个类被类加载器加载到内存中,会开辟一处内存空间,用来存储这个类的字节码文件信息,也就是Class类的实例对象。
那么,怎么获取各个字节码对应的实例对象(Class类型)?
Java提供了三种方法,如下:
一.类名.class
例如:Class strClass=String.class;
二.对象.getClass()
例如:Class cls=new String().getClass();
三.Class.forName(“类字符串信息”);
例如:Class.forName("java.util.Date");
Class.forName()的作用:
返回对应类的字节码文件信息,并且返回的时候,有两种情况:
1.这个类的字节码信息曾经被加载过,已经在java虚拟机中,直接返回。
2.虚拟机中没有这个类的字节码信息,用类加载器加载。加载进的字节码信息缓存在虚拟机中,再返回。
Java反射中,九个预定义类型的实例对象(Class类型)由基本数据类型(byte、char、short、int、long、float、double和boolean)和关键字void组成,其中需要注意的是基本类型的字节码获取方式只有一种就是类名.class。例如int.class;void.class
publicclass ReflectTest {
//先抛出异常,不做处理
publicstaticvoid main(String[] args) throws ClassNotFoundException{
String str1 = "abc";
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1 == cls2); // true
System.out.println(cls1 == cls3); // true
//isPrimitive()判读是不是基本类型
System.out.println(cls1.isPrimitive()); // false
System.out.println(int.class.isPrimitive()); // true
System.out.println(int.class == Integer.class); // false
//Integer.TYPE 获取它的数据类型
System.out.println(int.class == Integer.TYPE); // true
System.out.println(int[].class.isPrimitive()); // false
//isArray() 判断是不是数组
System.out.println(int[].class.isArray()); // true
}
}
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[] ,void...
一、通过反射获取类的构造函数
A. 得到某个类的指定(public)构造函数Connstructor<T> getConstructor(Class<?>... parameter types);
如:Constructor constructor=Class.forName("java.lang.String").getConstructor(StringBuffer.class);
B. 得到某个类的(public)所有构造函数Connstructor<T>[] getConstructors();
如:Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
这里需要注意的是:A和B都只能获取非private的构造函数
那么要是获取private的构造函数,我们可以是使用getDeclaredConstructor或getDeclaredConstructosr,这两方法和构造器的访问级别无关,可以直接访问。
1. Connstructor<T> getDeclaredConstructor(Class<?>... parameter types); //返回此Class对象所表示的类的指定方法,与方法的访问级别无关。
2. Connstructor<T>[] getDeclaredConstructosr();//返回此Class对象所表示的类的全部方法,与方法的访问级别无关。
那么,怎么创建反射实例化对象呢?
Java中提供了两种方式:
(1)、使用Class对象的newInstance()方法来创建该Class对象对应类的实例,这种方式要求该Class对象的对应类有默认的构造函数,而执行newInstance()方法时,实际上是利用默认的构造函数来创建类的实例。
获取指定类的默认构造函数,步骤:
(1)获取该类的Class对象。
(2) Class对象的newInstance()方法来创建该类的实例化对象。
例如:
// 创建对象的方法
public Object createObject(String _className) throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
// 根据字符串来获取对应的Class对象
Class<?> _class = Class.forName(_className);
// 使用Class对象的默认构造函数创建实例
return _class.newInstance();
}
(2)、先使用Class对象获取指定的Constructor对象,在调用Constructor对象的newInstance()方法来创建该Class对象对应的实例。通过这种方式可以选择使用某个类的指定构造函数来创建实例。
获取指定构造方法,步骤:
(1)获取该类的Class对象。
(2)利用Class对象的对应的getConstructor()或者getDeclaredConstructor()方法来获取指定的构造函数。
(3)调用Constructor的newInstance方法来创建该类的实例化对象。
例如:
/*
_className:类对应的字符串信息
_types:构造函数的参数类型
_obj:构造函数的参数值
*/
public Object createObject(String _className, Class<?> _types, Object _obj) throws Exception {
// 根据字符串来获取对应的Class对象
Class<?> _class = Class.forName(_className);
//获取Constructor类
Constructor _ctor = _class.getConstructor(_types);
// 使用Constructor对象的newInstance创建实例化对象
return _ctor.newInstance(_obj);
}
二、通过反射获取类的属性
A.获取Class对象所表示的类的指定的public属性 Field getField(String name);
如:
//obj为当前对象, strName为指定的属性名称
Field[] fields = obj.getClass().getField(_strName);
B.获取Class对象所表示的类的所有public属性Field[] getFields();
如:
//obj为当前对象
Field[] fields = obj.getClass().getFields();
这里需要注意的是:A和B都只能获取非private的属性
那么要是获取private属性,我们可以使用getDeclaredField和getDeclaredFields,这两方法和属性的访问级别无关,可以直接访问。
(1)Field getDeclaredField(String name):返回该Class对象所表示的类的指定属性,与属性的访问级别无关。
(2)Field[] getDeclaredFields():返回该Class对象所表示的类的全部属性,与属性的访问级别无关。
实例如下:
publicclass reflectPoint {
publicintx;
privateinty;
public reflectPoint(int x, int y) {
this.x = x;
this.y = y;
}
}
publicclass reflect1 {
publicstaticvoid main(String[] args) throws Exception {
reflectPoint pt1 = new reflectPoint(4, 7);
// 通过getField()方法,获取公共的字段
Field fieldX = pt1.getClass().getField("x");
System.out.println(fieldX.get(pt1));
// 通过getDeclaredField(()方法,可以无视权限访问,就可以访问private属性
Field fieldY = pt1.getClass().getDeclaredField("y");
fieldY.setAccessible(true);// 对私有字段取消访问权限,暴力访问。
System.out.println(fieldY.get(pt1));
}
}
拓展知识点:
要是获取Field数据类型,我们可以使用Class<?> a=field.getType(); 进行获取,但是我要是获取像Map<String,Integer>这种泛型的参数列表里面的数据类型时,该怎么获取呢?
java.lang.reflect包里面提供了对应的类,进行解决上面的问题,实现步骤如下:
1、 使用getGenericType(),获得Field实例对象的泛型类型
2、 ParameterizedType类提供了getActualTypeArguments(),可以获取泛型类型的所有泛型参数的参数类型。
实例:
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
publicclass Test {
private Map<String, Integer> map;
publicstaticvoid main(String[] args) throws Exception {
//获取map对应的Field对象
Field field = (Test.class).getDeclaredField("map");
//获取Field对象的泛型类型
Type type = field.getGenericType();
ParameterizedType pType = (ParameterizedType) type;
Type[] tArgs = pType.getActualTypeArguments();
for (int i = 0; i < tArgs.length; i++) {
System.out.println("第"+i+"个参数数据类型>>>" + tArgs[i]);
}
}
}
打印结果是:
第0个参数类型>>>class java.lang.String
第1个参数类型>>>class java.lang.Integer
三、通过反射获取类的方法
A. 获取Class对象所表示的类的指定public方法 Metnod getMethod(String name,Class<?>... parameter types);
如:Method methodCharAt = String.class.getMethod("startsWith", boolean.class);
B.获取Class对象所表示的类的所有public方法 Method[] getMethods();
如:Method[] methods = Class.forName("java.lang.String").gerMethods();
这里需要注意的是:A和B都只能获取非private的方法
那么要是获取private方法,我们可以使用getDeclaredMethod和getDeclaredMethods,这两方法,这里和方法的访问级别无关,可以直接访问。
(1) Method getDeclaredMethod(String name,Class<?>... parameter types);返回此Class对象所表示的类的指定方法,与方法的访问级别无关
(2) Method[] getDeclaredMethods();返回此Class对象所表示的类的全部方法,与方法的访问级别无关
Jdk1.4和jdk1.5中invoke方法的区别
JDK1.5:public Object invoke(Object obj , Object...args); //可变参数
JDK1.4:public Object invoke(Object obj , Object[] args); //数组作为参数传递,数组中的元素和参数列表一一对应。
实例:
String str = "abc";
Method methodcharAt = String.class.getMethod("charAt", int.class);
//在jdk1.5
methodcharAt.invoke(str,1);//b
//在jdk1.4
//methodcharAt.invoke(str,new Object[]{2});//c
注意:如果Method对象的invoke方法,传入的对象为null,说明Method对象对应的是一个静态方法。