反射

通过反射查看类信息

大部分对象在不同情况下是不同类型的,可能编译和运行时的类型是不同的,可能用到它时它的类型不是我们所需要的那个类型。解决这一问题通常有如下两种方法:
1.第一种做法是假设在编译时和运行时都完全知道类型的具体信息,在这种情况下,可以先使用instanceof运算符进行判断,再利用强制类型转换将其转换成其运行时类型的变量即可。
2.第二种做法是编译时根本无法预知该对象和类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息,这就必须使用反射。

每个类被加载之后,系统就会为该类生成- 一个对应的Class 对象,通过该Class对象就可以访问到JVM中的这个类。在Java程序中获得Class 对象通常有如下三种方式:
1.使用Class类的forName(String clazzName)静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名(必须添加完整包名)。
2.调用某个类的class属性来获取该类对应的Class对象。例如,Person.class 将会返回Person类对应的Class对象。
3.调用某个对象的getClass(0方法。该方法是java.lang.Object 类中的一个方法,所以所有的Java对象都可以调用该方法,该方法将会返回该对象所属类对应的Class对象。
下面4个方法用于获取Class对应类所包含的构造器:
1.Connstructor getConstructor(Class<?>… parameterTypes):返回此Class对象对应类的、带指定形参列表的public构造器。
2.Constructor<?>[] getConstructors(): 返回此Class对象对应类的所有public构造器。
3.Constructor getDeclaredConstructor(Class<?>… parameterTypes):返回此Class对象对应类的、带指定形参列表的构造器,与构造器的访问权限无关。
4.Constructor<?>[] getDeclaredConstructors():返回此Class对象对应类的所有构造器,与构造器的访问权限无关。
获取构造器时无须传入构造器名一同一个类的所有构造器的名字都是相同的,所以要确定一个构造器只要指定形参列表即可。
Class提供的功能非常丰富,它可以获取该类里包含的构造器、方法、内部类、注解等信息,也可以获取该类所包括的成员变量(Field)信息——通过getFields()或getField(String name)方法即可。

Java 8新增了一个Executable抽象基类,它有Constructor、Method 两个子类。
使用javac命令编译Java 源文件时,默认生成的class文件并不包含方法的形参名信息,因此调用isNamePresent0方法将会返回false,调用getName0方法也不能得到该参数的形参名。如果希望javac命令编译Java源文件时可以保留形参信息,则需要为该命令指定-parameters选项。

使用反射生成并操作对象

通过反射来生成对象有如下两种方式:
1.使用Class对象的newInstance(方法来创建该Class对象对应类的实例,这种方式要求该Class对象的对应类有默认构造器,而执行newInstance(方 法时实际上是利用默认构造器来创建该类的实例。
2.先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用指定的构造器来创建实例。
如果不想利用默认构造器来创建Java对象,而想利用指定的构造器来创建Java对象,则需要利用Constructor对象,每个Constructor对应一个构造器。 为了利用指定的构造器来创建Java对象,需要如下三个步骤:
1.获取该类的Class对象
2.利用Class对象的getConstructor(方法来获取指定的构造器
3.调用Constructor的newlnstance(方法来创建Java对象

当获得某个类对应的Class 对象后,就可以通过该Class对象的getMethods(方法或者getMethod()方法来获取全部方法或指定方法——这两个方法的返回值是Method数组,或者Method对象。每个Method对象对应一个方法,获得Method对象后,程序就可通过该Method来调用它对应的方法。在Method里包含一个invoke()方法,该方法的签名如下:
Object invoke(Object obj, 0Object… args):该方法中的obj是执行该方法的主调,后面的args是执行该方法时传入该方法的实参。
当通过Method的invoke()方法来调用对应的方法时,Java 会要求程序必须有调用该方法的权限。如果程序确实需要调用某个对象的private方法,则可以先调用Method对象的如下方法:
setAccessible(boolean flag):将Method对象的accessible 设置为指定的布尔值。值为true,指示该Method在使用时应该取消Java语言的访问权限检查;值为false,则指示该Method在使用时要实施Java语言的访问权限检查。

通过Class对象的getFields()或getField(方法可以获取该类所包括的全部成员变量或指定成员变量。Field提供了如下两组方法来读取或设置成员变量值:
1.getXxx(Object obj): 获取obj对象的该成员变量的值。此处的Xxx对应8种基本类型,如果该成员变量的类型是引用类型,则取消get后面的Xxx。
2.setXxx(Object obj , Xxx val):将obj对象的该成员变量设置成val值。此处的Xxx对应8种基本类型,如果该成员变量的类型是引用类型,则取消set后面的Xxx。
使用这两个方法可以随意地访问指定对象的所有成员变量,包括private修饰的成员变量。
在这里插入图片描述

使用反射生成JDK动态代理

Proxy提供了用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态地创建实例,也可以使用Proxy来创建动态代理实例。
在这里插入图片描述
实际上,即使采用第一个方法生成动态代理类之后,如果程序需要通过该代理类来创建对象,依然需要传入一个InvocationHandler 对象。也就是说,系统生成的每个代理对象都有一个与之关联的InvocationHandler对象。

动态代理就好比一段代码,我把他写成函数,在其他地方直接调用,如果不写函数,一个一个复制粘贴,那么修改的时候是个非常大的工程,而写成函数就只需要修改函数即可,这差不多就是动态代理的优势。
在这里插入图片描述

反射与泛型

使用Class< T >泛型可以避免强制类型转换
通过指定类对应的Class对象,可以获得该类里包含的所有成员变量,不管该成员变量是使用private修饰,还是使用public修饰。获得了成员变量对应的Field对象后,就可以很容易地获得该成员变量的数据类型,即使用如下代码即可获得指定成员变量的类型:Class<?> a= f.getType() ;
但这种方式只对普通类型的成员变量有效。如果该成员变量的类型是有泛型类型的类型,如Map<String , Integer>类型,则不能准确地得到该成员变量的泛型参数。为了获得指定成员变量的泛型类型,应先使用如下方法来获取该成员变量的泛型类型:Type gType = f.getGenericType() ;
然后将Type对象强制类型转换为ParameterizedType对象, ParameterizedType代表被参数化的类型,也就是增加了泛型限制的类型。ParameterizedType 类提供了如下两个方法:
1.getRawType(): 返回没有泛型信息的原始类型。
2.getActualTypeArguments(): 返回泛型参数的类型。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值