1.反射
反射是指在运行状态时,对于任意一个类,都能够知道它的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性(即使是private)。(注意关键词:运行状态)。
反射是一种动态机制,运用反射可以在运行时加载、使用编译期间完全未知的class。也就是说,Java程序可以加载在运行时才得知名称的class,获悉其完整构造方法,并生成其对象实体,对其属性设值或唤起其成员方法。
我们知道,要让Java程序能够运行,就必须让Java类被Java虚拟机加载。正常情况下,运行的所有的程序在编译期时候就已经把那个类加载了。 而反射机制是在编译时并不确定是哪个类被加载了,而是在程序运行的时候才加载,使用的是在编译期并不知道的类。
反射机制主要提供的功能:
①在运行时判断任意一个对象所属的类;
②在运行时构造任意一个类的对象;
③在运行时判断任意一个类所具有的成员变量和方法;
④在运行时调用任意一个对象的方法;
反射的实质就是把Java类中的各种存在给解析成相应的Java类。要正确使用Java反射机制就得使用Class(C大写) 类。它是Java反射机制的起源。当一个类被加载以后,Java虚拟机就会自动产生一个Class对象。通过这个Class对象就能获得加载到虚拟机当中这个Class对象对应的方法、成员以及构造方法的声明和定义等信息。
为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念:
静态编译:在编译时确定类型,绑定对象。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,降低类之间的耦合性。
所以,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性。而它的缺点是对性能有影响,它总是慢于直接执行相同的操作。比如利用反射调用一个类里面的方法总是比直接调用这个类实例的方法要慢。
2.通过反射加载类
可以通过JVM对想要的类进行查找,然后将获得的Class对象进行加载,这样就能获取到Class类的对象了,并通过调用newInstance()方法让加载完的类在内存中创建对应的实例,并把实例赋值给要进行查找的类。
在这里需要先引入类加载概念,当使用一个类的时候(比如new一个类的实例),jvm会检查此类是否被加载到内存,如果没有,则会执行加载操作。加载操作的内容是,读取类对应的class文件数据,解析此数据,构造一个此类对应的Class类的实例,此Class类的实例描述了类的结构,并且提供了调用此类成员的接口。此时jvm就可以使用该类了,比如实例化此类,或者调用此类的静态方法。这里使用的forName方法其实就是JVM类加载中的手动加载方法,目的是为了获取这个类的实例。在使用这个类前都会默认进行类初始化,类初始化操作就是执行一遍类的静态语句,包括静态变量的声明还有静态代码块。需要注意的是,这里并不包括静态方法。
看一下用反射机制和用以前的方法新建对象实例有什么不同。以新建一个Person对象为例。
用以前的方法是:
Person p = new Person();
在内存中新建一个Person的实例,对象p对这块内存地址进行引用。
用反射机制实现 (有三种方法):
①第一种:
Class<?> cls=Class.forName( “com.fanshe.Person”);
Person p=(Person)cls.newInstance();
首先通过JVM查找并加载指定的类(指定加载了com.fanshe包中的Person类);然后调用newInstance()方法让加载完的类在内存中创建对应的实例,并把实例赋值给p。
注:当类没有加载到内存时可使用这种方式。
②第二种:
Person p = new Person();
Class<?> cls=p.getClass();
Person p2=(Person)cls.newInstance();
首先在内存中新建一个Person的实例,对象p对这个内存地址进行引用;然后对象p调用getClass()返回对象它所对应的Class对象;最后调用newInstance()方法让Class对象在内存中创建对应的实例,并且让p2引用实例的内存地址。
③第三种:
Class<?> cls=Person.Class();
Person p=(Person)cls.newInstance();
首先获取指定类型的Class对象,这里是Person;然后调用newInstance()方法让Class对象在内存中创建对应的实例,并且让p引用实例的内存地址。
注意:
①cls.newInstance()方法返回的是一个泛型T,需要强转成Person类。
②cls.newInstance()默认返回的是Person类的无参数构造对象,所以被反射机制加载的类必须有无参数构造方法,否者运行会抛出异常。
3.反射涉及的方法
①获取父类
public Class<? super T> getSuperclass();
②获取内部类:
1)获取类中本身定义的公共、私有、保护的内部类
public Class<?>[] getDeclaredClasses();
2)获取类本身和其父类定义的公共、私有、保护的内部类
public Class<?>[] getClasses();
③获取定义它的外部类
1)获取定义它的外部类,如果为匿名内部类则返回null
public Class<?> getDeclaringClass();
2)获取定义它的外部类,匿名内部类同样有效 public Class<?> getEnclosingClass();
③Field相关
通过Class获取Field
1)获取类本身的所有字段,包括公有、保护、私有
public native Field[] getDeclaredFields();
2)获取类本身和其所有父类的公有和保护字段
public Field[] getFields();
3)获取类本身的指定字段,包括公有、保护、私有
public native Field getDeclaredField(String name) throws NoSuchFieldException;
4)获取类本身和其所有父类指定的公有和保护字段
public Field getField(String name) throws NoSuchFieldException;
④Field的相关属性
1)获取字段的作用域public、protected、private、abstract、static、final …
Modifier.toString(field.getModifiers());
2)获取字段的类型,配合getSimpleName()使用:int、long、String …
field.getType().getSimpleName();
3)获取字段名称
field.getName();
⑤对Field设置值
public native Object get(Object object)
public native boolean getBoolean(Object object)
public native byte getByte(Object object)
public native char getChar(Object object)
public native double getDouble(Object object)
public native float getFloat(Object object)
public native int getInt(Object object)
public native long getLong(Object object)
public native short getShort(Object object)
public native void set(Object object, Object value)
public native void setBoolean(Object object, boolean value)
public native void setByte(Object object, byte value)
public native void setChar(Object object, char value)
public native void setDouble(Object object, double value)
public native void setFloat(Object object, float value)
public native void setInt(Object object, int value)
public native void setLong(Object object, long value)
public native void setShort(Object object, short value)
1)每个方法里都有一个Object参数,对于非静态字段来说,它必须设置为具体的实例,而对于静态字段来说它没有实际意义,可设为null;
2)对于私有(private)字段,在进行访问的时候需要先调用 field.setAccessible(true),而公有、保护字段可直接进行访问;
理解:静态变量和具体实例无关,私有变量外部不能访问。
⑥使用方法
MyClass myClass = new MyClass();
Class<?> cls = myClass.getClass();
// 获取私有域
Field field = cls.getDeclaredField(“myPrivateInt”);
// 如果为静态变量可按以下调用
field.setAccessible(true);
field.getInt(null);
field.setInt(null, 33);
// 如果为非静态变量则必须按以下调用
field.setAccessible(true);
field.getInt(myClass);
field.setInt(myClass, 33);
⑦Method相关
通过Class获取Method:
1)获取类本身的所有方法,包括公有、保护、私有
public Method[] getDeclaredMethods();
2)获取类本身和其所有父类的公有和保护方法
public Method[] getMethods();
3)获取类本身的指定方法,包括公有、保护、私有 (方法名,参数类型)
public Method getDeclaredMethod(String name, Class<?>… parameterTypes) throws NoSuchMethodException;
4)获取类本身和其所有父类指定的公有和保护方法(方法名,参数类型)
public Method getMethod(String name, Class<?>… parameterTypes) throws NoSuchMethodException;
⑧通过Class获取构造方法
1)获取类本身的所有构造方法,包括公有、保护、私有
public Constructor<?>[] getDeclaredConstructors();
2)获取类本身非私有构造方法
public Constructor<?>[] getConstructors();
3)获取类本身指定的构造方法(参数类型)
public Constructor getDeclaredConstructor(Class<?>… parameterTypes) throws NoSuchMethodException;
4)获取类本身指定的非私有构造方法(参数类型)
public Constructor getConstructor(Class<?>… parameterTypes) throws NoSuchMethodException;
Method的常用属性,构造方法没有返回值属性
①获取方法的作用域:public、protected、private、abstract、static、final …
Modifier.toString(method.getModifiers());
②获取方法的返回值类型,配合getSimpleName()使用:int、long、String …
method.getReturnType().getSimpleName();
③获取方法名称
method.getName();
④获取方法参数
Class<?>[] parameterTypes = method.getParameterTypes();
⑤获取方法声明所在类
method.getDeclaringClass();
执行方法
①执行方法
public native Object invoke(Object receiver, Object… args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;
②执行构造方法
public native T newInstance(Object… args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException;
比如下面的Person类,以前的写法是:
public class fanshe03 {
public static void main(String[] args) {
Person person=new Person();
person.setName(“Lipt0n”);
System.out.print(person.getName);
}
}
class Person {
String name;
public Person() {
}
public Person(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
现在用反射机制来实现上面的代码:
public static void main(String[] args) {
try {
Class<?> cls=Class.forName(“test.Person”); //加载Person类
Object object=(Object) cls.newInstance(); //实例化Person
Method setname=cls.getDeclaredMethod( “setName”, String.class);//获取setName()方法
setname.invoke(object, “Lipt0n”);//设置调用setName方法的对象和传入setName的值
Method getname=cls.getDeclaredMethod( “getName”);//获取getName方法
System.out.print(getname.invoke(object, null));//设置调用getName方法的对象.把值打印到控制台
} catch (Exception e) {
e.printStackTrace();
}
}
4.反射中Class.forName()和ClassLoader.loadClass()的区别
先了解一下类的加载过程:在Java中,类加载器把一个类加载进Java虚拟机中,要经过三个步骤来完成:加载、链接和初始化,其中链接又可以分成验证、准备和解析三步,除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要工作如下:
①加载:查找和导入类或接口的二进制数据;
②链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
(1)验证:检查导入类或接口的二进制数据的正确性;
(2)准备:给类的静态变量分配并初始化存储空间;
(3)解析:将符号引用转成直接引用;
③初始化:激活类的静态变量的初始化Java代码和静态Java代码块。
Class.forName() 与 ClassLoader.loadClass()都是反射用来构造类的方法,但是他们的用法是有一定区别的:
Class.forName(className) 方法,其实调用的方法是Class.forName(className,true,classloader); 注意看第2个boolean参数,它为true表示在加载之后必须初始化。在执行过此方法后,目标对象的静态块代码已经被执行,静态参数也已经被初始化。再看ClassLoader.loadClass(className) 方法,其实它调用的方法是ClassLoader.loadClass( className,false); 注意看第2个 boolean 参数,该参数为false表示目标对象被加载后不进行链接,这就意味着不会去执行该类静态块中的内容。因此两者的区别就显而易见了。
Class.forName()默认是需要初始化的。一旦初始化,就会触发目标对象的static代码块执行,static参数也会被初始化。
ClassLoader.loadClass()方法不进行解析,意味着不进行包括初始化等一系列步骤,那么静态代码块和静态对象就不会得到执行。
5.反射对性能有影响吗?
①类加载器访问一个类时,把类信息加载到JVM中
②反射方法时,动态解析Class将需要获取的方法中的数据组成Method对象
③反射执行时,通过Method执行真正需要执行的方法
整个过程会产生额外的对象,而且执行方法时,相当于需要使用Method作为中间者来执行,自然会比直接执行方法慢。就好比一座山,如果本来就有路(不用反射)当然比没有路,自己开拓一条路(反射)快。
既然反射性能有损失,那具体损失有哪些?
①反射是基于程序集和元数据的,在使用反射的时候,会搜索元数据,而元数据是基于字符串的,并且无法预编译,所以这一系列的操作对性能有影响。
②大量的装箱拆箱也对性能有影响,由于我们对目标类型是未知的,而且向方法传递的参数通常是Object类型的,所有会有大量的装箱和拆箱。
但是简单少量的反射并不会对性能造成太大的影响,真正完成性能问题的还是编写的代码。
6.反射工具类
提供一些Java基本的反射功能:
public class ReflectUtils {
public static final Class<?>[] EMPTY_PARAM_TYPES = new Class<?>[0];
public static final Object[] EMPTY_PARAMS = new Object[0];
/**
* 从指定的类中获取指定的字段
*
* @param sourceClass 指定的类
* @param fieldName 要获取的字段的名字
* @param isFindDeclaredField 是否查找Declared字段
* @param isUpwardFind 是否向上去其父类中寻找
* @return
*/
public static Field getField(Class<?> sourceClass, String fieldName, boolean isFindDeclaredField, boolean isUpwardFind) {
Field field = null;
try {
field = isFindDeclaredField ? sourceClass.getDeclaredField(fieldName) : sourceClass.getField(fieldName);
} catch (NoSuchFieldException e1) {
if (isUpwardFind) {
Class<?> classs = sourceClass.getSuperclass();
while (field == null && classs != null) {
try {
field = isFindDeclaredField ? classs.getDeclaredField(fieldName) : classs.getField(fieldName);
} catch (NoSuchFieldException e11) {
classs = classs.getSuperclass();
}
}
}
}
return field;
}
/**
* 从指定的类中获取指定的字段,默认获取Declared类型的字段、向上查找
*
* @param sourceClass 指定的类
* @param fieldName 要获取的字段的名字
* @return
*/
public static Field getField(Class<?> sourceClass, String fieldName) {
return getField(sourceClass, fieldName, true, true);
}
/**
* 获取给定类的所有字段
*
* @param sourceClass 给定的类
* @param isGetDeclaredField 是否需要获取Declared字段
* @param isGetParentField 是否需要把其父类中的字段也取出
* @param isGetAllParentField 是否需要把所有父类中的字段全取出
* @param isDESCGet 在最终获取的列表里,父类的字段是否需要排在子类的前面。只有需要把其父类中的字段也取出时此参数才有效
* @return 给定类的所有字段
*/
public static List getFields(Class<?> sourceClass, boolean isGetDeclaredField, boolean isGetParentField, boolean isGetAllParentField, boolean isDESCGet) {
List fieldList = new ArrayList();
//如果需要从父类中获取
if (isGetParentField) {
//获取当前类的所有父类
List<Class<?>> classList = null;
if (isGetAllParentField) {
classList = getSuperClasss(sourceClass, true);
} else {
classList = new ArrayList<Class<?>>(2);
classList.add(sourceClass);
Class<?> superClass = sourceClass.getSuperclass();
if (superClass != null) {
classList.add(superClass);
}
}
//如果是降序获取
if (isDESCGet) {
for (int w = classList.size() - 1; w > -1; w–) {
for (Field field : isGetDeclaredField ? classList.get(w).getDeclaredFields() : classList.get(w).getFields()) {
fieldList.add(field);
}
}
} else {
for (int w = 0; w < classList.size(); w++) {
for (Field field : isGetDeclaredField ? classList.get(w).getDeclaredFields() : classList.get(w).getFields()) {
fieldList.add(field);
}
}
}
} else {
for (Field field : isGetDeclaredField ? sourceClass.getDeclaredFields() : sourceClass.getFields()) {
fieldList.add(field);
}
}
return fieldList;
}
/**
* 获取给定类的所有字段
*
* @param sourceClass 给定的类
* @return 给定类的所有字段
*/
public static List getFields(Class<?> sourceClass) {
return getFields(sourceClass, true, true, true, true);
}
/**
* 设置给定的对象中给定名称的字段的值
*
* @param object 给定的对象
* @param fieldName 要设置的字段的名称
* @param newValue 要设置的字段的值
* @param isFindDeclaredField 是否查找Declared字段
* @param isUpwardFind 如果在当前类中找不到的话,是否取其父类中查找
* @return 设置是否成功。false:字段不存在或新的值与字段的类型不一样,导致转型失败
*/
public static boolean setField(Object object, String fieldName, Object newValue, boolean isFindDeclaredField, boolean isUpwardFind) {
boolean result = false;
Field field = getField(object.getClass(), fieldName, isFindDeclaredField, isUpwardFind);
if (field != null) {
try {
field.setAccessible(true);
field.set(object, newValue);
result = true;
} catch (IllegalAccessException e) {
e.printStackTrace();
result = false;
}
}
return result;
}
/**
* 从指定的类中获取指定的方法
*
* @param sourceClass 给定的类
* @param isFindDeclaredMethod 是否查找Declared字段
* @param isUpwardFind 是否向上去其父类中寻找
* @param methodName 要获取的方法的名字
* @param methodParameterTypes 方法参数类型
* @return 给定的类中给定名称以及给定参数类型的方法
*/
public static Method getMethod(Class<?> sourceClass, boolean isFindDeclaredMethod, boolean isUpwardFind, String methodName, Class<?>… methodParameterTypes) {
Method method = null;
try {
method = isFindDeclaredMethod ? sourceClass.getDeclaredMethod(methodName, methodParameterTypes) : sourceClass.getMethod(methodName, methodParameterTypes);
} catch (NoSuchMethodException e1) {
if (isUpwardFind) {
Class<?> classs = sourceClass.getSuperclass();
while (method == null && classs != null) {
try {
method = isFindDeclaredMethod ? classs.getDeclaredMethod(methodName, methodParameterTypes) : classs.getMethod(methodName, methodParameterTypes);
} catch (NoSuchMethodException e11) {
classs = classs.getSuperclass();
}
}
}
}
return method;
}
/**
* 从指定的类中获取指定的方法,默认获取Declared类型的方法、向上查找
*
* @param sourceClass 指定的类
* @param methodName 方法名
* @param methodParameterTypes 方法参数类型
* @return
*/
public static Method getMethod(Class<?> sourceClass, String methodName, Class<?>… methodParameterTypes) {
return getMethod(sourceClass, true, true, methodName, methodParameterTypes);
}
/**
* 从指定的类中获取指定名称的不带任何参数的方法,默认获取Declared类型的方法并且向上查找
*
* @param sourceClass 指定的类
* @param methodName 方法名
* @return
*/
public static Method getMethod(Class<?> sourceClass, String methodName) {
return getMethod(sourceClass, methodName, EMPTY_PARAM_TYPES);
}
/**
* 获取给定类的所有方法
*
* @param clas 给定的类
* @param isGetDeclaredMethod 是否需要获取Declared方法
* @param isFromSuperClassGet 是否需要把其父类中的方法也取出
* @param isDESCGet 在最终获取的列表里,父类的方法是否需要排在子类的前面。只有需要把其父类中的方法也取出时此参数才有效
* @return 给定类的所有方法
*/
public static List getMethods(Class<?> clas, boolean isGetDeclaredMethod, boolean isFromSuperClassGet, boolean isDESCGet) {
List methodList = new ArrayList();
//如果需要从父类中获取
if (isFromSuperClassGet) {
//获取当前类的所有父类
List<Class<?>> classList = getSuperClasss(clas, true);
//如果是降序获取
if (isDESCGet) {
for (int w = classList.size() - 1; w > -1; w–) {
for (Method method : isGetDeclaredMethod ? classList.get(w).getDeclaredMethods() : classList.get(w).getMethods()) {
methodList.add(method);
}
}
} else {
for (int w = 0; w < classList.size(); w++) {
for (Method method : isGetDeclaredMethod ? classList.get(w).getDeclaredMethods() : classList.get(w).getMethods()) {
methodList.add(method);
}
}
}
} else {
for (Method method : isGetDeclaredMethod ? clas.getDeclaredMethods() : clas.getMethods()) {
methodList.add(method);
}
}
return methodList;
}
/**
* 获取给定类的所有方法
*
* @param sourceClass 给定的类
* @return 给定类的所有方法
*/
public static List getMethods(Class<?> sourceClass) {
return getMethods(sourceClass, true, true, true);
}
/**
* 获取给定的类中指定参数类型的ValuOf方法
*
* @param sourceClass 给定的类
* @param methodParameterTypes 方法参数类型
* @return 给定的类中给定名称的字段的GET方法
*/
public static Method getValueOfMethod(Class<?> sourceClass, Class<?>… methodParameterTypes) {
return getMethod(sourceClass, true, true, “valueOf”, methodParameterTypes);
}
/**
* 调用不带参数的方法
*
* @param method
* @param object
* @return
* @throws Exception
*/
public static Object invokeMethod(Method method, Object object) throws
Exception {
return method.invoke(object, EMPTY_PARAMS);
}
/**
* 获取给定的类中给定参数类型的构造函数
*
* @param sourceClass 给定的类
* @param isFindDeclaredConstructor 是否查找Declared构造函数
* @param isUpwardFind 是否向上去其父类中寻找
* @param constructorParameterTypes 构造函数的参数类型
* @return 给定的类中给定参数类型的构造函数
*/
public static Constructor<?> getConstructor(Class<?> sourceClass, boolean isFindDeclaredConstructor, boolean isUpwardFind, Class<?>… constructorParameterTypes) {
Constructor<?> method = null;
try {
method = isFindDeclaredConstructor ? sourceClass.getDeclaredConstructor(constructorParameterTypes) : sourceClass.getConstructor(constructorParameterTypes);
} catch (NoSuchMethodException e1) {
if (isUpwardFind) {
Class<?> classs = sourceClass.getSuperclass();
while (method == null && classs != null) {
try {
method = isFindDeclaredConstructor ? sourceClass.getDeclaredConstructor(constructorParameterTypes) : sourceClass.getConstructor(constructorParameterTypes);
} catch (NoSuchMethodException e11) {
classs = classs.getSuperclass();
}
}
}
}
return method;
}
/**
* 获取给定的类中所有的构造函数
*
* @param sourceClass 给定的类
* @param isFindDeclaredConstructor 是否需要获取Declared构造函数
* @param isFromSuperClassGet 是否需要把其父类中的构造函数也取出
* @param isDESCGet 在最终获取的列表里,父类的构造函数是否需要排在子类的前面。只有需要把其父类中的构造函数也取出时此参数才有效
* @return 给定的类中所有的构造函数
*/
public static List<Constructor<?>> getConstructors(Class<?> sourceClass, boolean isFindDeclaredConstructor, boolean isFromSuperClassGet, boolean isDESCGet) {
List<Constructor<?>> constructorList = new ArrayList
/**
* 获取给定的类所有的父类
*
* @param sourceClass 给定的类
* @param isAddCurrentClass 是否将当年类放在最终返回的父类列表的首位
* @return 给定的类所有的父类
*/
public static List<Class<?>> getSuperClasss(Class<?> sourceClass, boolean isAddCurrentClass) {
List<Class<?>> classList = new ArrayList
/**
* 获取给定的类的名字
*
* @param sourceClass 给定的类
* @return 给定的类的名字
*/
public static String getClassName(Class<?> sourceClass) {
String classPath = sourceClass.getName();
return classPath.substring(classPath.lastIndexOf(’.’) + 1);
}
@SuppressWarnings(“unchecked”)
public static T getObjectByFieldName(Object object, String fieldName, Class clas) {
if (object != null && !TextUtils.isEmpty(fieldName) && clas != null) {
try {
Field field = ReflectUtils.getField(object.getClass(), fieldName, true, true);
if (field != null) {
field.setAccessible(true);
return (T) field.get(object);
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
} else {
return null;
}
}
/**
* 判断给定字段是否是type类型的数组
*
* @param field
* @param type
* @return
*/
public static final boolean isArrayByType(Field field, Class<?> type) {
Class<?> fieldType = field.getType();
return fieldType.isArray() && type.isAssignableFrom(fieldType.getComponentType());
}
/**
* 判断给定字段是否是type类型的collectionType集合,例如collectionType=List.class,type=Date.class就是要判断给定字段是否是Date类型的List
*
* @param field
* @param collectionType
* @param type
* @return
*/
@SuppressWarnings(“rawtypes”)
public static final boolean isCollectionByType(Field field, Class<? extends Collection> collectionType, Class<?> type) {
Class<?> fieldType = field.getType();
if (collectionType.isAssignableFrom(fieldType)) {
Class<?>
first = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
return type.isAssignableFrom(first);
} else {
return false;
}
}
}