这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战
前言:目前很多三方框架都有用到反射的知识,本文将要介绍反射常用API。
什么是反射?
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
优缺点
- 优点 反射提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类;反射是其它一些常用语言,如C、C++、Fortran 或者Pascal等都不具备的
- 缺点
- 性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此Java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。
- 使用反射会模糊程序内部逻辑:程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
反射则是一开始并不知道我们要初始化的类对象是什么,自然也无法使用 new关键字来创建对象,这时,我们使用JDK提供的反射API进行反射调用。反射就是在运行状态中,对于任意一个类,都能知道,调用且改变这个类的所有属性和方法。
Java反射机制主要提供了以下功能:
- 在运行时构造任意一个类的对象
- 在运行时获取或者修改任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法(属性)
获取Class对象
- 通过类名获取:类名.class()
- 通过对象获取:对象名.getClass()
- 通过全类名获取: Class.forName(全类名) classLoader.loadClass(全类名)
public static String str = new String("shixf");
public static void main(String[] args) {
//1.通过类名获取
Class<String> stringClass = String.class;
//2.通过对象获取
Class<? extends String> strClass = str.getClass();
//3.通过全类名获取
try {
//str.getClass().getName() == java.lang.String
Class<?> aClass = Class.forName(str.getClass().getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
判断是否为某个类的实例
一般我们常常用"instanceof" 关键字来判断某个类是否为一个类的实例,同时也可以借助反射中Class对象的 " isInstance()" 方法来判断是否为某个类的实例,它是一个native方法。
public native boolean isInstance(Object obj);
//判断是否为某个类的类型
public native boolean isAssignableFrom(Class<?> cls);
反射创建实例对象
- Class.newInstance()
- 通过Class获取Constructor对象,在调用Constructor.newInstance()方法来创建实例.
public static Object createInstance_1(Class cls){
if(null != cls){
Object o = null;
try {
o = cls.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return o;
}
return null;
}
public static Object createInstance_2(Class cls){
//获取String所对应的Class对象
Class<?> c = String.class;
//获取String类带一个String参数的构造器
Constructor constructor = null;
//根据构造器创建实例
Object obj = null;
try {
constructor = c.getConstructor(String.class);
obj = constructor.newInstance("23333");
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
获取构造器信息
/**
* @param parameterTypes ...代表可传入该类型的数组,单个实例,或者不传参数,
* 获得使用特殊的参数类型的public构造函数(包括父类)
*/
public Constructor<T> getConstructor(Class<?>... parameterTypes)
/**
* 获得类的所有公共构造函数
*/
public Constructor<?>[] getConstructors()
/**
* 获得使用特定参数类型的构造函数(包括私有)
*/
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
/**
* 获得类的所有构造函数
*/
public Constructor<?>[] getDeclaredConstructors()
获取类的成员变量(字段)信息
/**
* 获取命名的公共字段(包含父类的public成员变量)
*/
public Field getField(String name)
/**
* 获得类的所有公共字段(包含父类的public成员变量)
*/
public Field[] getFields()
/**
* 获取类本身的属性成员(包括私有、共有、保护)
*/
public Field getDeclaredField(String name)
/**
* 获取类本身的所有属性成员(包括私有、共有、保护)
*/
public Field[] getDeclaredFields()
通过反射调用方法
/**
* 使用特定参数类型,获得命名的公共方法
*/
public Method getMethod(String name, Class<?>... parameterTypes)
/**
* 获得类的所有公共方法
*/
public Method[] getMethods()
/**
* 获得类声明的命名的方法,包括私有的。
*/
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
/**
* 获得类声明的所有命名的方法,包括私有的。
*/
public Method[] getDeclaredMethods()
java.lang.IllegalArgumentException: wrong number of arguments
注意:当我们获取到一个方法时,可以调用invoke()方法来调用这个方法。以及需要注意的 “wrong number of arguments”异常
/**
*
* @param obj
* @param args 此处需要注意,该参数为Object
* @return
*/
public Object invoke(Object obj, Object... args)
public class GetMethod {
private static GetMethod getMethod = new GetMethod();
private static String[] strArray = {"1","2","3"};
public static void main(String[] args) {
getMethod();
}
public void printStr(String[] str){
for (String s : str) {
System.out.println(s);
}
}
public static void getMethod(){
Class<?> cls = getMethod.getClass();
try {
Method printStr = cls.getMethod("printStr",String[].class);
//此处需要强制转换为Object,否则抛出wrong number of arguments
printStr.invoke(cls.newInstance(),(Object) strArray);
} catch (Exception e) {
e.printStackTrace();
}
}
}
利用反射创建数组
数组在 Java 里是比较特殊的一种类型,它可以赋值给一个 Object Reference 其中的 Array 类为 java.lang.reflflect.Array类。我们通过 Array.newInstance() 创建数组对象,它的原型是 :
public static Object newInstance ( Class <?> componentType , int length );
反射获取泛型真实类型
当我们对一个泛型类进行反射时,需要得到泛型中的真是数据类型,来完成如json反序列化的操作。此时需要通过Type体系来完成。Type接口包含了一个实现类(Class)个四个接口,分别是:
TypeVariable: 泛型类型变量,可以泛型上下限等信息。
ParameterizedType 具体的泛型类型,可以获得元数据中泛型签名类型(泛型真是类型)
GenericArrayType 当需要描述的类型是泛型类的数组时,比如List[] , map[],此接口会作为Type的实现。
WildcardType 通配符泛型,获得上下限信息。
TypeVariable
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
public class TypeVariableTest<K extends Comparable & Serializable, V> {
private K key;
private V value;
public static void main(String[] args) {
try {
Field fKey = TypeVariableTest.class.getDeclaredField("key");
Field fValue = TypeVariableTest.class.getDeclaredField("value");
TypeVariable genericTypeKey = (TypeVariable)fKey.getGenericType();
TypeVariable genericTypeValue = (TypeVariable)fValue.getGenericType();
System.out.println("genericTypeKey.getName(): " + genericTypeKey.getName());
System.out.println("genericTypeValue.getName(): " + genericTypeValue.getName());
System.out.println(genericTypeKey.getGenericDeclaration());//class Reflect.TypeVariableTest
System.out.println(genericTypeValue.getGenericDeclaration());//class Reflect.TypeVariableTest
System.out.println("K的上界");
for (Type bound : genericTypeKey.getBounds()) {
System.out.println(bound);
//interface java.lang.Comparable
//interface java.io.Serializable
}
System.out.println("V的上界");
for (Type bound : genericTypeValue.getBounds()) {
System.out.println(bound);
//class java.lang.Object
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
ParameterizedType
public class ParameterizedTypeTest {
Map<String, String> mMap;
public static void main(String[] args) {
try {
Field map = ParameterizedTypeTest.class.getDeclaredField("mMap");
System.out.println("get mMap GenericType: " + map.getGenericType());//java.util.Map<java.lang.String, java.lang.String>
ParameterizedType pType = (ParameterizedType)map.getGenericType();
System.out.println(pType.getRawType());//interface java.util.Map
for (Type actualTypeArgument : pType.getActualTypeArguments()) {
System.out.println(actualTypeArgument);//俩边class java.lang.String
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
GenericArrayType
public class GenericArrayTypeTest<T> {
List<String>[] lists;
public static void main(String[] args) {
try {
Field field = GenericArrayTypeTest.class.getDeclaredField("lists");
GenericArrayType genericArrayType = (GenericArrayType)field.getGenericType();
System.out.println(genericArrayType.getGenericComponentType());//java.util.List<java.lang.String>
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
WildcardType
public class WildcardTypeTest {
private List<? extends Number> up;//上限
private List<? super String> down;//下限
public static void main(String[] args) {
try {
Field up = WildcardTypeTest.class.getDeclaredField("up");
Field down = WildcardTypeTest.class.getDeclaredField("down");
//先拿到泛型类型
ParameterizedType parameterizedTypeUp = (ParameterizedType)up.getGenericType();
ParameterizedType parameterizedTypeDown = (ParameterizedType)down.getGenericType();
//在从泛型类型里拿到通配符类型
WildcardType wildcardTypeUp = (WildcardType)parameterizedTypeUp.getActualTypeArguments()[0];
WildcardType wildcardTypeDown = (WildcardType)parameterizedTypeDown.getActualTypeArguments()[0];
System.out.println(wildcardTypeUp.getUpperBounds()[0]);//class java.lang.Number
System.out.println(wildcardTypeDown.getLowerBounds()[0]);//class java.lang.String
System.out.println(wildcardTypeUp);//? extends java.lang.Number
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}