解析Java中的Field类和Method类

Field类
Field类中定义了一些方法,可以用来查询字段的类型以及设置或读取字段的值。将这些方法与继承而来的member方法结合在一起.就可以使我们能够找出有关字段声明的全部信息,并且能够操纵某个特定对象或类的字段。

  getGenericType方法返回表示字段的声明类型的Type实例。对于像String或int这样的平凡类型,该方法将返回与其相关联的Class对象,例如String.class和int.classo对于像List < Stri ng>这样的参数化类型,该方法将返回Parameterizedrype的实例,例如,对像T这样的类型,该方法将返回Typevariable实例。

  遗留下来的getType方法将返回字段的类型的Class对象。对于平凡类型,该方法的行为与getGenericType方法的相同。如果字段的声明类型是参数化类型,那么getType方法将返回参数化类型的擦除所对应的Class对象,即原始类型的Class对象。例如,对于声明为List < Stri ng>的对象,getType将返回Li St. class的。如果字段的声明类型是类型变量,那么getType方法将返回类型变量的擦除所对应的class对象。例如,假设有一个类FOO<丁>,对于其声明为T类型的字段,get丁ype将返回object.

  class对象。如果FOO被声明为FOo<下extends Number >,那么get下ype将返回 Number.class.

  我们可以使用isEnumConstant方法查询一个字段是否是枚举常量,也可以使用get和set方法来获取和设置字段的值。这些接受object引元并返回Obj ect值的方法都有一种通用形式,以及一些可以直接处理基本类型的更加特化的形式。所有这些方法都要接受一个引元,用来指定所要操作的对象。对于静态字段,将忽略这个对象引元,所以此时也可以将其设

  置为null。下面的方法将打印一个对象的short型字段的值:

  public static void printShortField(Object o, String name)

  throws NoSuchFieldException,IllegalAccessException

  {

  Field field=o.getClass().getField(name);

  short value=(Short) field.get(o);

  System.out.println(value);

  get方法的返回值可以是这个字段所引用的任何对象,如果该字段是基本类型,那么该方法将返回恰当类型的包装器类对象。对于我们的”hort型字段,get方法将返回包含该字段值的short类型的对象,而在将它赋值给本地变量value时,该对象值会自动进行拆箱转换。

  set方法的使用也是类似的。将short型字段设置为所提供的值的方法看起来可能像下面这样:

  public static voi

  setShortField(Object o,String name,short nv)

  throws NoSuchFieldException,IllegalAccessException

  Field field=0.getClass().getField(name);

  field .set(o .nv);

  虽然set接受的是Object类型的参数,但是我们可以直接传递一个short型的值,并用包装转换将其包装为short类型的对象。

  在上面的方法中,如果指定对象的域是不可访问的,并且这种访问权限控制是强制执行的,那么就会抛出IllegalACcessException异常;如果传递的对象与该域的类型不同,就会抛出illegalArgumentException异常;如果该域是非静态的且传递的对象引用是null,就会抛出NullPointerException异常;访问静态域可能会要求对类进行初始化,所以该方法也会抛出ExceptionInInitializerError异常。

  Field类还有特定的用来获取和设置基本类型的方法,例如,我们可以在Field对象上调用getPrimitive7ype和set Primitive7ype,其中Primitive7ype是(首字母大写的)基本类型名。get方法可用于下面的语句:

  short value=field.getshort(o);

而set方法可用于下面的语句:

  field.setshort(o, nv);

  用以上两种方式声明的语句中可以避免使用包装器类对象。

  Field类实现了AnnotatedElement接口,所以我们也可以像16.2节那样查询应用于域

  上的注解。

  凭借上面介绍的方法,我们可以将Field对象用作操纵任意值的一种方式,但是我们应该尽量避免使用它。因为Java语言会在程序的编译期尽可能多地捕获编程错误,所以在我们编写代码时,使用的诸如「ield对象这样的间接方法越少,那么在将它们编译成代码之前,就可以防止更多的错误。而且,我们可以看到,在前面的代码中,要想知道到底会发生什么,与在普通的语法中直接使用域名的情况相比,我们花费在阅读代码上的精力显然大了许多。

  Final字段

  在通常情况下,对声明为final的字段进行设置将会导致抛出IllegalACcessException
异常,这是我们所能预期的,因为final字段的值是永远不会改变的。但是有些特殊情况—例如在定制的反序列化(见20.8.4节)中,改变final字段的值就是有意义的,我们只有在实例字段上才能通过反射实现这一点,并且前提是在该Field对象上已经调用过了setAccessible(true)。注意,可以成功调用setAccessible(true)是不够的,必须确实调用过它。

  这种能力是为高度特化的上下文提供的,并非用于通用目的,我们介绍它仅仅是为了保持内容的完整性。如果脱离了特定的上下文,例如定制的反序列化,那么改变final字段的值可能会导致意外的甚至是灾难性的后果。在这些上下文之外,不能保证对final字段的改变是可见的。即便是在这样的上下文中,在使用这项技术编码时也必须保证安全机制不会阻碍代码的执行。改变值为常量变量(见2.2.3节)的final字段将会导致此改变不可见,除非通过使用反射来实现这种修改。

Method类
method类和它从member类继承而来的方法使得我们可以获得方法声明的完整信息:

  " public Type getGenericReturnTypeO:该方法返回的是目标方法的返回类型的Type对象。如果目标方法被声明为返回void,则该方法返回void.classo

  " public Type[] getGenericParameterTypes():该方法返回目标方法所有参数类型的Type对象数组,这些Type对象将按照参数的声明顺序存储于在数组中。如果目标方法没有任何参数,则该方法返回一个空数组。

  .publ i c Type [] getGeneri caccepti onTypes Q:该方法返回在throws子句中列出的所有异常类型的Type对象数组,这些Type对象将按照异常的声明顺序存储在数组中。

  如果目标方法没有声明任何异常,则该方法返回一个空数组。

  Java还提供了getReturnType,getParameterTypes和getExceptionTypes方法,用来返回Cl as”对象而不是Type对象。就像在使用Field.getType时,参数化类型和类型变量是由它们的擦除所对应的Class对象表示的。

  method类实现了AnnotatedElement,并且我们可以像16.2节所讨论的那样去查询应用于方法上的注解。另外,Method类还提供了getParameterAnnotations,用来提供对应用于方法参数上的注解进行访问。getParameterAnnotations方法可以返回Annotation数组,其中最外层数组的每一个元素都与方法的参数相对应;如果某个参数没有任何注解,则该方法为这个参数返回一个长度为0的Annotation数组。如果method对象所表示的方法自身就是一个注解元素,那么getDefaultvalue方法将返回一个表示该元素默认值的Object对象;如果method对象本身不是注解元素或者它没有默认值,则该方法将返回null.Method类也实现了GenericDeclaration,因此定义了getTypeParameters方法,该方法将返回一个Typevariable对象数组。如果给定的method对象表示的不是泛型方法,该方法将返回一个空数组。

  我们可以使用isvarArgs方法来检查某个method对象是否是一个可变引元方法,而i sBridge方法可以用来检查它是否是一个桥接方法

  Method对象最有趣的用法就是反射地调用它自己:

  .public object invoke(object onThis,object…args)throws IllegalACcessException,IllegalArgumentException,工nvocation下argetException:该方法在onThis对象上调用method对象定义的方法,并用args的值来设置被调用方法的参数。对于非静态方法,onThis的实际类型就确定了将要调用方法的哪种实现,而对于静态方法,onThis会被忽略,并且通常会设置为null. args值的数量必须和被调用方法的实际参数数量相同,并且这些值的类型必须全部都可赋值给那些被调用方法的参数;否则,我们将会得到工llegalArgumentException异常。请注意,可变引元方法的最后一个参数是一个数组,所以我们必须用实际想要传递的“可变”引元来填充该数组。如果我们想调用我们没有访问权限的方法,该方法就会抛出IllegalACcessException异常。如果被调用方法不是on下his对象的方法,该方法会抛出工llegalArgumentExcepti on异常。如果onThis为null并且是非静态的,该方法就会抛出NO 1PointerException异常。如果这个 method对象表示的是静态方法,并且声明这个静态方法的类仍处于待初始化状态,该方法就会抛出ExceptionIn工nitializerError异常。如果被调用法出异幂,谈万法就会抛出InvocationTargetException异常。

  当我们使用invoke方法时,可以直接传递基本类型,也可以使用合适的包装器类。包装器类表示的类型必须可赋值给方法所声明的参数类型。我们可以使用Long,Float或Double来包装double类型的引元,但是不能用Double来包装long或float类型的引元,因为double不是可赋值给long或们oat的。对invoke方法返回的object的处理方法和Field.get一样,都是返回对应于它们的包装器类的基本类型。如果方法声明为void, invoke方法将返回null,

  简单地说,就是我们在用invoke来调用方法时,只能使用在Java语言中合法的与其参数

  具有相同类型和值的引元。例如,下面的调用

  return str.indexof(".”,8);

  可以用反射写成如下形式:

  Throwable fa们ure;

  try{

  Method indexM=String.class.

  getMethod("index0f",String.class,int.class);

  return (Integer) indexM.invoke(str,”,”,8);

  }catch (NoSuchMethodException e){

  failure=e;

  }catch (InvocationTargetException e){

  fa们ure=e .getCause();

  }catch (IllegalAccessException e){

  failure=e;

  }

  throw fa们ure;

  虽然编译器对于直接调用所做的安全性检查,在使用反射的情况下,只能在运行时使用invoke时进行,但是基于反射的代码确实拥有与直接调用的代码在语义上等效的安全性检查。访问权限检查可能会以略为不同的方式执行—安全管理器可能会拒绝访问我们的包中的某个方法,即使我们可以直接调用该方法。

  当我们可以使用这种形式的调用时,我们有充分的理由去避免它。但是如果我们在编写调试器或其他需要将用户输入解释为对对象操作的泛型应用时使用invoke或get/set方法,就会显得很合理。method对象在某种程度上可以当作类似其他语言中的方法指针来使用,但是我们有更好的工具,尤其是接口、抽象类和嵌套类,可以用来处理那些通常在其他语言中用方法指针解决的问题。

(0)

相关推荐

  • 详解Java中Method的Invoke方法

    在写代码的时候,发现从父类class通过getDeclaredMethod获取的Method可以调用子类的对象,而子类改写了这个方法,从子类class通过getDeclaredMethod也能获取到Method,这时去调用父类的对象也会报错.虽然这是很符合多态的现象,也符合java的动态绑定规范,但还是想弄懂java是如何实现的,就学习了下Method的源代码.  Method的invoke方法 1.先检查 AccessibleObject的override属性是否为true. Accessib

  • java.lang.AbstractMethodError: org.apache.xerces.dom.DocumentImpl.setXmlVersion问题解决方法

    读取本地的xml文件,通过DOM进行解析,DOM解析的特点就是把整个xml文件装载入内存中,形成一颗DOM树形结构,树结构是方便遍历和和操纵. DOM解析的特性就是读取xml文件转换为 dom树形结构,通过节点进行遍历. 这是W3c关于节点的概念 如果xml中包含有大量的数据,由于dom一次性把xml装入内存中的特性,所以dom不适合于包含大量数据的xml解析.当包含有大量xml的时候,用SAX进行解析比较节省内存. 下面是一个运用DOM进行解析xml文件的例子: xml文件结构如下: <?xm

  • Java反射机制及Method.invoke详解

    JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类:在运行时构造任意一个类的对象:在运行时判断任意一个类所具有的成员变量和方法:在运行时调用任意一个对象的方法:生成动态代理. 1. 得到某个对象的属性 复制代码 代码如下: public Object get

  • java.lang.NoSuchMethodException: com.sun.proxy.$Proxy58.list错误解决办法

    java.lang.NoSuchMethodException: com.sun.proxy.$Proxy58.list错误解决办法 玩web的SSH总会有些令你意想不到的exception,这里其中有很多事自己不小心,或者马虎所造成.因此,解决的方案会各有不同,别人出现的异常解决方案对你的可能无效,就像上面的我报的异常一样,百度了很多很多次,给我的答案无非就是在aop上加上一句,但是非常抱歉,我加上去无效!所以还是那句话,对于自己的异常,还是要自己解决. 首先说明一下,我这次的练习的ssh结构

  • 解析Java中的Field类和Method类

    Field类 Field类中定义了一些方法,可以用来查询字段的类型以及设置或读取字段的值.将这些方法与继承而来的member方法结合在一起.就可以使我们能够找出有关字段声明的全部信息,并且能够操纵某个特定对象或类的字段. getGenericType方法返回表示字段的声明类型的Type实例.对于像String或int这样的平凡类型,该方法将返回与其相关联的Class对象,例如String.class和int.classo对于像List < Stri ng>这样的参数化类型,该方法将返回Para

  • 深入解析Java中反射中的invoke()方法

    先讲一下java中的反射: 反射就是将类别的各个组成部分进行剖析,可以得到每个组成部分,就可以对每一部分进行操作 反射机制应用场景:逆向代码.动态生成类框架等,使用反射机制能够大大的增强程序的扩展性. 反射的基本步骤:首先获得Class对象,然后实例化对象,获得类的属性.方法或者构造函数,最后访问属性.调用方法.调用构造函数创建对象.而invoke()方法就是用来执行指定对象的方法. 在比较复杂的程序或框架中来使用反射技术,可以简化代码提高程序的复用性. 讲的是Method类的invoke()方

  • 解析java中的condition

    一.condition 介绍及demo Condition是在java 1.5中才出现的,它用来替代传统的Object的wait().notify()实现线程间的协作,相比使用Object的wait().notify(),使用Condition的await().signal()这种方式实现线程间协作更加安全和高效.因此通常来说比较推荐使用Condition,阻塞队列实际上是使用了Condition来模拟线程间协作. Condition是个接口,基本的方法就是await()和signal()方法:

  • 实例解析Java中的构造器初始化

    1.初始化顺序 当Java创建一个对象时,系统先为该对象的所有实例属性分配内存(前提是该类已经被加载过了),接着程序开始对这些实例属性执行初始化,其初始化顺序是:先执行初始化块或声明属性时制定的初始值,再执行构造器里制定的初始值. 在类的内部,变量定义的先后顺序决定了初始化的顺序,即时变量散布于方法定义之间,它们仍就会在任何方法(包括构造器)被调用之前得到初始化. class Window { Window(int maker) { System.out.println("Window(&quo

  • 解析Java中的static关键字

    一.static关键字使用场景 static关键字主要有以下5个使用场景: 1.1.静态变量 把一个变量声明为静态变量通常基于以下三个目的: 作为共享变量使用 减少对象的创建 保留唯一副本 第一种比较容易理解,由于static变量在内存中只会存在一个副本,所以其可以作为共享变量使用,比如要定义一个全局配置.进行全局计数.如: public class CarConstants { // 全局配置,一般全局配置会和final一起配合使用, 作为共享变量 public static final in

  • 一文解析Java中的方法重写

    目录 1.含义 2.为什么要使用方法重写 3.如何使用方法重写 3.1 基本语法 3.2 具体分析 3.3 方法重写的一些小技巧 1.含义 子类继承父类后,可以在子类中书写一个与父类同名同参的方法,从而实现对父类中同名同参数的方法的覆盖,我们把这一过程叫做方法的重写(override) 2.为什么要使用方法重写 2.1 当父类的方法满足不了子类的需求的时候,需要在子类中对该方法进行重写 2.2 题目与分析 例如存在一个父类Peple,子类Chinese,父类中有一个say()方法,输出人在说话,

  • 详解Java中String JSONObject JSONArray List<实体类>转换

    JSON使用阿里的fastJson为依赖包 gradle依赖管理如下: compile group: 'com.alibaba', name: 'fastjson', version:'1.2.41' 1.String转JSONObject 前言:String 是JSONObject格式的字符串 eg: JSONObject jSONObject = JSONObject.parseObject(String); 2.String转JSONArray 前言:String 是JSONArray格式

  • 详解Java中CountDownLatch异步转同步工具类

    使用场景 由于公司业务需求,需要对接socket.MQTT等消息队列. 众所周知 socket 是双向通信,socket的回复是人为定义的,客户端推送消息给服务端,服务端的回复是两条线.无法像http请求有回复. 下发指令给硬件时,需要校验此次数据下发是否成功. 用户体验而言,点击按钮就要知道此次的下发成功或失败. 如上图模型, 第一种方案使用Tread.sleep 优点:占用资源小,放弃当前cpu资源 缺点: 回复速度快,休眠时间过长,仍然需要等待休眠结束才能返回,响应速度是固定的,无法及时响

  • 全面解析Java中的HashMap类

    HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实现类.虽然 HashMap 和 HashSet 实现的接口规范不同,但它们底层的 Hash 存储机制完全一样,甚至 HashSet 本身就采用 HashMap 来实现的. 实际上,HashSet 和 HashMap 之间有很多相似之处,对于 HashSet 而言,系统采用 Hash 算法决定集合元素

  • 深入解析Java中ThreadLocal线程类的作用和用法

    ThreadLocal与线程成员变量还有区别,ThreadLocal该类提供了线程局部变量.这个局部变量与一般的成员变量不一样,ThreadLocal的变量在被多个线程使用时候,每个线程只能拿到该变量的一个副本,这是Java API中的描述,通过阅读API源码,发现并非副本,副本什么概念?克隆品? 或者是别的样子,太模糊.   准确的说,应该是ThreadLocal类型的变量内部的注册表(Map<Thread,T>)发生了变化,但ThreadLocal类型的变量本身的确是一个,这才是本质!  

随机推荐