java 反射
一 认识反射
1.1 简介
- 程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。
- 尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。
- 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。
- JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
1.2 应用场景
- Spring框架:IOC(控制反转)
- Hibernate框架:关联映射等
- 白盒测试
二 反射讲解
2.1 什么是反射
可以通过类名动态的创建对象,可以根据对象获取类中所有的属性和方法。
2.2 类
Class
根据字符串得到类名Class cls=Class.forName(String className);
根据对象获取类:Class =obj.getClass();
根据类获取类:Class cls =Person.class;
根据全路径来获取:Class cls=Class.forName(报名.类名);
2.3 字段
Filed
获取所有属性Filed[] filed =cls.getDeclaredFields();
获取所有公共属性: Filed[] fields=cls.getFileds();
获取指定属性:Field fields=cls.getDeclaredFiled(Stringname)
取得指定公共属性Field fields=cls.getField(String name)
2.4 方法
Method
获得所有方法: Methodmethods=cls.getDeclaredMethods();
获取所有公共方法:Method[] methods=cls.getMethods();
取得指定的方法:Method method=cls.getDeclaredMethod(Stringname,Class[] paramsType);
获取指定公共的方法: Method method =cls.getMethod(Stringname,Class[] paramsType);
2.5 构造函数
Constructor
获得使用特殊的参数类型的公共构: Constructor constructor=cls.getConstructor(Class[]params)
获得类的所有公共构造函数:Constructor[] constructor=cls. getConstructors()
获得使用特定参数类型的构:Constructor constructor=cls.getDeclaredConstructor(Class[]params)
获得类的所有构造函数:Constructor[] constructor=cls.getDeclaredConstructors()
2.6 执行某个方法
Method.invoke(Object target,Object[] params);
2.7 获取类
获取方式 | 说明 | 示例 |
object.getClass() 每个对象都有此方法 | 获取指定实例对象的Class | List list = new ArrayList(); Class listClass = list.getClass(); |
class. getSuperclass() | 获取当前Class的继承类Class | List list = new ArrayList(); Class listClass = list.getClass(); Class superClass = listClass. getSuperclass(); |
Object.class | .class直接获取 | Class listClass = ArrayList.class; |
Class.forName(类名) | 用Class的静态方法,传入类的全称即可 | try { Class c = Class.forName("java.util.ArrayList"); } catch (ClassNotFoundException e) { e.printStackTrace(); } |
Primitive.TYPE | 基本数据类型的封装类获取Class的方式 | Class longClass = Long.TYPE; Class integerClass = Integer.TYPE; Class voidClass = Void.TYPE; |
2.8 类生成对象
平常情况我们通过new Object来生成一个类的实例,但有时候我们没法直接new,只能通过反射动态生成。
实例化无参构造函数的对象,两种方式:
①Class. newInstance();
②Class. getConstructor (new Class[]{}).newInstance(new Object[]{})
实例化带参构造函数的对象:
clazz.getConstructor(Class<?>... parameterTypes) .newInstance(Object... initargs)
三 处理类的继承
3.1 类与对象的父子关系判断
isAssignableFrom()方法与instanceof关键字的区别总结为以下两个点
- isAssignableFrom()方法是从类继承的角度去判断,instanceof关键字是从实例继承的角度去判断。
- isAssignableFrom()方法是判断是否为某个类的父类,instanceof关键字是判断是否某个类的子类。
使用方法
父类.class.isAssignableFrom(子类.class)
子类实例 instanceof 父类类型
四 范型反射(Type)
为了程序的扩展性,最终引入了Type接口作为Class,ParameterizedType,GenericArrayType,TypeVariable和WildcardType这几种类型的总的父接口。这样实现了Type类型参数接受以上五种子类的实参或者返回值类型就是Type类型的参数。为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType,GenericArrayType,TypeVariable 和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
4.1 反射+泛型接口类型
- java.lang.reflect.Type:java语言中所有类型的公共父接口
- java.lang.reflect.ParameterizedType
- java.lang.reflect.GenericArrayType
- java.lang.reflect.WildcardType
4.2 Type直接子接口
ParameterizedType,GenericArrayType,TypeVariable和WildcardType四种类型的接口
- ParameterizedType: 表示一种参数化的类型,比如Collection
- GenericArrayType: 表示一种元素类型是参数化类型或者类型变量的数组类型
- TypeVariable: 是各种类型变量的公共父接口
- WildcardType: 代表一种通配符类型表达式,比如?, ? extends Number, ? super Integer【wildcard是一个单词:就是“通配符”】
4.3 ParameterizedType
public interface ParameterizedType extends Type {
Type[] getActualTypeArguments();
Type getRawType();
Type getOwnerType();
}
定义测试类型
- private List<String> list1;
- private List list2;
- private Map<String,Long> map1;
- private Map map2;
- private Map.Entry<Long,Short> map3;
field.getGenericType() instanceof ParameterizedType 判断是否范型
Field[] fields = ParameterType.class.getDeclaredFields();
for(Field field:fields){
System.out.println(field.getGenericType() instanceof ParameterizedType);
}
true
false
true
false
true
Type[] getActualTypeArguments() 获取范型类里面的实际类型
Field[] fields = ParameterType.class.getDeclaredFields();
for (Field field : fields) {
if (field.getGenericType() instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) field.getGenericType();
Type[] types = pType.getActualTypeArguments();
for (Type t : types) {
System.out.print("类型:" + t.getTypeName());
}
System.out.println("");
}
}
类型:java.lang.String
类型:java.lang.String类型:java.lang.Long
类型:java.lang.Long类型:java.lang.Short
Type getRawType() 获取实际的变量类型
Field[] fields = ParameterType.class.getDeclaredFields();
for (Field field : fields) {
if (field.getGenericType() instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) field.getGenericType();
Type rawType = pType.getRawType();
System.out.println(rawType.getTypeName());
}
}
java.util.List
java.util.Map
java.util.Map$Entry
pType.getOwnerType() 返回当前内部类的父类型
Field[] fields = ParameterType.class.getDeclaredFields();
for (Field field : fields) {
if (field.getGenericType() instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) field.getGenericType();
Type rawType = pType.getOwnerType();
System.out.println(rawType);
}
}
null
null
interface java.util.Map
4.4 GenericArrayType
public interface GenericArrayType extends Type {
Type getGenericComponentType();
}
表示泛型数组类型。比如:void method(ArrayList[] al){…}
【注意】<>不能出现在数组的初始化中,即new数组之后不能出现<>,否则javac无法通过。但是作为引用变量或者方法的某个参数是完全可以的。
获取泛型数组中元素的类型
源码声明:Type getGenericComponentType();
【注意】无论从左向右有几个[]并列,这个方法仅仅脱去最右边的[]之后剩下的内容就作为这个方法的返回值。
为什么返回值类型是Type?
public static E methodV(
String[] p1,
E[] p2,
ArrayList[] p3,
E[][] p4){}
{1}. 对于String[],通过getComponentType()返回之后,脱去最右边的[]之后,剩余的类型是String。因此对这个参数的返回类型是 Class
{2}. 对于E[],通过getComponentType()返回之后,脱去最右边的[]之后,剩余的类型是E。因此对这个参数的返回类型是 TypeVariable
{3}. 对于ArrayList[],通过getComponentType()返回之后,脱去最右边的[]之后,剩余的类型是ArrayList。因此对这个参数的返回类型是 ParameterizedType
{4}. 对于E[][],通过getComponentType()返回之后,脱去最右边的[]之后,剩余的类型是E[]。因此对这个参数的返回类型是 GenericArrayType
4.5 WildcardType
public interface WildcardType extends Type {
Type[] getUpperBounds();
Type[] getLowerBounds();
}
表示通配符类型的表达式。
比如 void printColl(ArrayListal); 中的 ? extends Number
【注意】根据上面API的注释提示:现阶段通配符表达式仅仅接受一个上边界或者下边界,这个和定义类型变量时候可以指定多个上边界是不一样。但是API说了,为了保持扩展性,这里返回值类型写成了数组形式。实际上现在返回的数组的大小就是1
获取通配符表达式对象的泛型限定的上边界的类型
源码声明:Type[] getUpperBounds();
【注意】上面说了,现阶段返回的Type[ ]中的数组大小就是1个。写成Type[ ]是为了语言的升级而进行的扩展。
例如下面的方法:
{1}. public static void printColl(ArrayList< ? extends ArrayList> al){}
通配符表达式是:? extends ArrayList,这样 extends后面是?的上边界,这个上边界是ParameterizedType类型。
{2}. public static void printColl(ArrayList< ? extends E> al){}
通配符表达式是:? extends E,这样 extends后面是?的上边界,这个上边界是TypeVariable类型
{3}.public static voidprintColl(ArrayList< ? extends E[]> al){}
通配符表达式是:? extends E[],这样 extends后面是?的上边界,这个上边界是GenericArrayType类型
{4}.public static void printColl(ArrayList<? extends Number> al){}
通配符表达式是:? extends Number,这样 extends后面是?的上边界,这个上边界是Class类型