java.lang.reflect.Field 解读

news2025/1/23 4:12:32

java.lang.reflect.Field

Java 中 Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类字段或实例字段。Field 是成员变量的意思。Field 也是一个类,该类位于 java.lang.reflect 包下。

https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Field.html

在这里插入图片描述

  1. 获取变量的类型。

    • Field.getType():返回这个变量的类型。

    • Field.getGenericType():如果当前属性有签名属性类型就返回,否则就返回 Field.getType()。

    • isEnumConstant() : 判断这个属性是否是枚举类。

  2. 获取成员变量的修饰符。

    • Field.getModifiers() : 以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符。
  3. 获取和修改成员变量的值。

    • getName() : 获取属性的名字。

    • get(Object obj) : 返回指定对象obj上此 Field 表示的字段的值。

    • set(Object obj, Object value) : 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。

1. 获取field的类型

有两种方式可以获取到field的属性,Field.getType()Field.getGenericType(),其中 getGenericType 可以获取到泛型的标识符,如果这个field是泛型,则返回泛型的标识,如果不是泛型,这会转而调用 getType 获取到真正的类型,也就是 Object

这里可以提一下,Java 里的泛型是假泛型,从字节码到可以执行文件的时候,已经把泛型擦除了,变成真正的类型,但是 getType() 调用时,并没有真正的类型代入,所以会返回所有的类的父类 Object

我们举个例子:

public class FieldSpy<T> {
    public boolean[][] b = {{ false, false }, { true, true } };
    public String name  = "Alice";
    public List<Integer> list;
    public T val;

    public static void main(String[] args) {
        try {
            Class<?> c = Class.forName(args[0]);
            Field f = c.getField(args[1]);
            System.out.format("Type: %s%n", f.getType());
            System.out.format("GenericType: %s%n", f.getGenericType());
        } catch (ClassNotFoundException x) {
            x.printStackTrace();
        } catch (NoSuchFieldException x) {
            x.printStackTrace();
        }
    }
}

执行命令以及执行结果:

$ java FieldSpy FieldSpy b
Type: class [[Z
GenericType: class [[Z
$ java FieldSpy FieldSpy name
Type: class java.lang.String
GenericType: class java.lang.String
$ java FieldSpy FieldSpy list
Type: interface java.util.List
GenericType: java.util.List<java.lang.Integer>
$ java FieldSpy FieldSpy val
Type: class java.lang.Object
GenericType: T

2. 检索和解析 Field 的修饰符

Field 的修饰符可以通过 public int getModifiers() 方法获取,这个方法返回的是int型,代表意义可以参见修饰符,如果要判断一个 field 是否具有某个修饰符,可以通过 位运算符& 判断,比如判断一个 field 的修饰符是否有 public 属性:

Field f = OneClass.getField("field");
int modify = f.getModifiers();
return modify&Modifier.PUBLIC == Modifier.PUBLIC

可以看一个官方的例子:

enum Spy { BLACK , WHITE }

public class FieldModifierSpy {
    volatile int share;
    int instance;
    class Inner {}

    public static void main(String... args) {
    	try {
    	    Class<?> c = Class.forName(args[0]);
    	    int searchMods = 0x0;
    	    for (int i = 1; i < args.length; i++) {
    	    	searchMods |= modifierFromString(args[i]);
    	    }
	
    	    Field[] flds = c.getDeclaredFields();
    	    out.format("Fields in Class '%s' containing modifiers:  %s%n",
    	         c.getName(),
    	         Modifier.toString(searchMods));
    	    boolean found = false;
    	    for (Field f : flds) {
    	    	int foundMods = f.getModifiers();
    	    	// Require all of the requested modifiers to be present
    	   		if ((foundMods & searchMods) == searchMods) {
    	         	out.format("%-8s [ synthetic=%-5b enum_constant=%-5b ]%n",
    	         		f.getName(), f.isSynthetic(), f.isEnumConstant());
    	        	found = true;
    	    	}
    	    }
	
    	    if (!found) {
    	    	out.format("No matching fields%n");
    	    }
	
    	    // production code should handle this exception more gracefully
    	} catch (ClassNotFoundException x) {
    	    x.printStackTrace();
    	}
    }

    private static int modifierFromString(String s) {
    	int m = 0x0;
    	if ("public".equals(s))           m |= Modifier.PUBLIC;
    	else if ("protected".equals(s))   m |= Modifier.PROTECTED;
    	else if ("private".equals(s))     m |= Modifier.PRIVATE;
    	else if ("static".equals(s))      m |= Modifier.STATIC;
    	else if ("final".equals(s))       m |= Modifier.FINAL;
    	else if ("transient".equals(s))   m |= Modifier.TRANSIENT;
    	else if ("volatile".equals(s))    m |= Modifier.VOLATILE;
    	return m;
    }
}

这个例子的大致意思是查找输入类名是否具有输入的修饰符的成员变量,并把成员变量名,并且输出其是否是编译器生成的和是否输入枚举变量。

输入输出:

$ java FieldModifierSpy FieldModifierSpy volatile
Fields in Class 'FieldModifierSpy' containing modifiers:  volatile
share    [ synthetic=false enum_constant=false ]

$ java FieldModifierSpy Spy public
Fields in Class 'Spy' containing modifiers:  public
BLACK    [ synthetic=false enum_constant=true  ]
WHITE    [ synthetic=false enum_constant=true  ]

$ java FieldModifierSpy FieldModifierSpy\$Inner final
Fields in Class 'FieldModifierSpy$Inner' containing modifiers:  final
this$0   [ synthetic=true  enum_constant=false ]

$ java FieldModifierSpy Spy private static final
Fields in Class 'Spy' containing modifiers:  private static final
$VALUES  [ synthetic=true  enum_constant=false ]
  • 是否是编译器生成可以通过方法 field.isSynthetic() 判断。
  • 是否是枚举变量可以通过方法 field.isEnumConstant() 判断。
  • 是否是编译器我想很多人都明白,什么是编译器生成的成员变量呢?
    • 比如枚举类型,每个枚举类型都有一个 VALUES 成员变量,这个变量我们并没有显式定义,但是可以通过它获取这个枚举类对应的所有没有常量,VALUES 就是编译器生成的。

2.1 Java 中冷门的 synthetic 关键字原理解读

看 JAVA 的反射时,看到有个 synthetic ,还有一个方法 isSynthetic() 很好奇,就了解了一下:

Any constructs introduced by a Java compiler that do not have a corresponding construct in the source code must be marked as synthetic, except for default constructors, the class initialization method, and the values and valueOf methods of the Enum class.

大意为:由 java 编译器生成的(除了像默认构造函数这一类的)方法,或者类

2.1.1 例子

既然知道 synthetic 方法synthetic类 是由编译器生成的,那到底编译器会怎么生成这些东西,又在什么情况下会生成这些东西呢?

先看一段代码:

import static java.lang.System.out;

public final class DemonstrateSyntheticMethods
{
   public static void main(final String[] arguments)
   {
      DemonstrateSyntheticMethods.NestedClass nested = new DemonstrateSyntheticMethods.NestedClass();
      out.println("String: " + nested.highlyConfidential);
   }

   private static final class NestedClass
   {
      private String highlyConfidential = "Don't tell anyone about me";
      private int highlyConfidentialInt = 42;
      private Calendar highlyConfidentialCalendar = Calendar.getInstance();
      private boolean highlyConfidentialBoolean = true;
   }
}

编译之后,可以看到三个文件:

在这里插入图片描述

其中,最下面的这个类文件很好解释,就是我们的主class,中间的文件,是我们的内部类,上面的文件,后面再讲,我们先看一下中间这个内部类

2.1.1.1 内部类的反编译结果

javap 反编译 DemonstrateSyntheticMethods$NestedClass.class ,得到如下结果:

javap DemonstrateSyntheticMethods$NestedClass.class
Compiled from "DemonstrateSyntheticMethods.java"
final class DemonstrateSyntheticMethods$NestedClass {
	DemonstrateSyntheticMethods$NestedClass(DemonstrateSyntheticMethods$1);

	static java.lang.String access$100(DemonstrateSyntheticMethods$NestedClass);
}

先把构造函数放一边,我们来看这个标黑的方法 access$100 这个是怎么回事呢?我们的源文件里找不到这个 access方法 啊?

2.1.1.2 synthetic方法

这个方法就是编译器生成的 synthetic方法,读者不信的话,可以用 method.isSynthetic() 去验证一下。

为何要生成这样一个方法呢?

可以看到,我们的 NestedClass类 中,highConfidential 是一个私有属性,而我们在外部类 DemonstrateSyntheticMethods 中,直接引用了这个属性。作为一个内部类,NestedClass 的属性被外部类引用,在语义上毫无问题,但是这却苦了编译器。

为了能让一个 private 的变量被引用到,编译器生成了一个 package scopeaccess 方法,这个方法就是一个 get 方法,在外部类使用 highConfidential 这个属性时,实际是使用了这个 access 方法。

javap 中可以看到直接的证据:

在这里插入图片描述

图中红框的位置,可以很清楚的看到 main 方法实际上调用了 access$100 这个方法。

所以,结论很清楚了,编译器为了方便内部类的私有成员被外部类引用,生成了一个 get 方法,这可以被理解为一个 trick ,绕开了 private 成员变量的限制。

2.1.1.3 synthetic类

定义已经提到,编译器不仅仅会生成方法,也会生成 synthetic类

我们回过头来看 2.1 提到的最后一个类 DemonstrateSyntheticMethods$1.class

这个类是一个完全的空类,反编译后是这个样子:

// $FF: synthetic class
class DemonstrateSyntheticMethods$1 {
}

这个类只出场了一次,作为内部类 NestedClasspackage scope 的构造函数,如图所示:

在这里插入图片描述

那么,这个类的作用呢?笔者查了很多资料,都没有明确的说明这个类的用途,只能根据代码做推测如下:

NestedClass作为一个 private 类,其默认构造函数也是 private 的。那么,事实上,作为外部类的 DemonstrateSyntheticMethods类 ,没有办法new这个内部类的对象,而这和我们需要的语义相违背。

那么,为了实现语义,编译器又用了一个 trick,悄悄的生成了一个构造函数 NestedClass(DemonstrateSyntheticMethods$1 obj),这个构造函数是包可见的。

3. 检索Field的注解

获取所有的注解可以用 field.getDeclaredAnnotations() 方式。

获取单个的可以用:

  • getAnnotatedType()
  • getAnnotation(Class annotationClass)
  • getAnnotationsByType(Class annotationClass)

实际上这几个方法都是从 class java.lang.reflect.AccessibleObject 继承而来的,这里就不做详细介绍了。

4. 设置和获取Field的值

set(Object obj, Object value) 来设置 Field ,除了这个方式还有多种确定Field类型的方式,比如 void setDouble(Object obj, double d)

Object get(Object obj) 来获取 Field 的值,和 set方法 一直,get方法也有多种确定 Field类型 的方式,比如 double getDouble(Object obj)

以上方法都可能抛出 NoSuchFieldExceptionIllegalAccessException 异常。

官方文档上有一句话是这样说的:因为这种访问通常违反了该类的设计意图,因此应尽可能谨慎的使用它。前面就讲过,反射是破坏封装性的,违反的类的设计原则,所以能少用就少用。这里要提一下 setXXXX() 内部如果是基础类型时要小心,这个方法不会进行装箱和拆箱操作,因为装箱和拆箱操作是编译器做的,运行时,JVM 并不能做这个事情。比如下面的例子就会抛出异常。

public class FieldTrouble {
    public Integer val;

    public static void main(String... args) {
        FieldTrouble ft = new FieldTrouble();
        try {
            Class<?> c = ft.getClass();
            Field f = c.getDeclaredField("val");
            f.setInt(ft, 42);               // IllegalArgumentException
        } catch (NoSuchFieldException x) {
            x.printStackTrace();
        } catch (IllegalAccessException x) {
            x.printStackTrace();
        }
    }
}

执行结果:

Exception in thread "main" java.lang.IllegalArgumentException: Can not set java.lang.Integer field reflect.FieldTrouble.val to (int)42
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:191)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.setInt(UnsafeObjectFieldAccessorImpl.java:114)
    at java.lang.reflect.Field.setInt(Field.java:949)
    at reflect.FieldTrouble.main(FieldTrouble.java:14)

做set方式之前可以通过 isAssignableFrom 方法来进行检测,检测之后再进行处理:

Integer.class.isAssignableFrom(int.class) == false;
int.class.isAssignableFrom(Integer.class) == false

另外,final 标识的成员变量是不能用set方法重新设置其值的,会抛出 IllegalAccessException 异常。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/40427.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每天五分钟机器学习:支持向量机损失函数和互熵损失函数

本文重点 本节课程我们讲学习两个常见的损失函数,一个是支持向量机损失,也叫做hinge loss,另外一个损失函数是互熵损失函数,它常常应用于softmax分类器中。 hinge loss 单样本的hinge loss可以为: 这个意思是说样本分类错误的分数-样本分类正确的分数小于阈值▲,则损…

Spring Boot 2.7.6 正式版发布, SpringBoot 2.7.6来了

一、发布说明 11 月 25 日官方发布了 Spring Boot 2.7.6 版本&#xff0c;此版本包括 44 个错误修复、文档改进和依赖项升级。 二、更新内容 2.1 bug 修复 即使未启用基于注释的计划&#xff0c;ScheduledBeanLazyInitializationExcludeFilter 也会自动配置使用 ContextHi…

第16章-Spring AOP中的基础API

文章目录一、概述二、切点&#xff08;Pointcut&#xff09;三、通知&#xff08;Advice&#xff09;1. 环绕通知2. 前置通知3. 异常通知4. 后置通知四、通知者&#xff08;Advisor&#xff09;五、附录1. 常用接口2. 示例代码前面我们讲了基于 XML 和注解两种方式配置 AOP&…

SpringBoot SpringBoot 原理篇 2 自定义starter 2.3 定时任务报表开发

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 原理篇 文章目录SpringBootSpringBoot 原理篇2 自定义starter2.3 定时任务报表开发2.3.1 直接开干2.3.2 小结2 自定义start…

JavaScript函数进阶:闭包

变量作用域 变量根据作用域的不同分为两种&#xff1a;全局变量和局部变量。 1. 函数内部可以使用全局变量。 2. 函数外部不可以使用局部变量。 3. 当函数执行完毕&#xff0c;本作用域内的局部变量会销毁 什么是闭包 闭包&#xff08;closure&#xff09;指有权访问另一…

Day13--商品列表-请求并渲染商品列表的数据

1.定义请求参数对象 接口部分&#xff1a; 文档部分&#xff1a; 我的操作&#xff1a; 1》在goods_list.vue中&#xff1a; 1>初步操作&#xff1a; 其效果图&#xff1a; 2>进一步操作&#xff1a; 在goods_list.vue中&#xff1a; 情况①&#xff1a; 情况②&…

python高级在线题目训练-第二套·主观题

1、《Walden》 是美国作家梭罗独居瓦尔登湖畔的记录,描绘了他两年多时间里的所见、所闻和所思。该书崇尚简朴生活&#xff0c;热爱大自然的风光&#xff0c;内容丰厚&#xff0c;意义深远&#xff0c;语言生动。 请用Python统计小说《Walden》 中各单词出现的频次&#xff0c;…

Metabase学习教程:视图-8

漏斗图 使用漏斗图显示步骤的进度。 图1。我们将用示例数据库构建一个漏斗图。 漏斗图用一系列台阶显示了指标。通常&#xff0c;它们用于显示有多少人通过特定的序列&#xff08;如网站上的结帐流程&#xff09;完成。第一步是多少人访问你的网站。然后有多少人浏览了一个产品…

【笔记】ABAQUS弹塑性分析

1. 弹塑性分析的主要问题 1.1 elastic-plastic deform behavior abaqus 默认的塑性表现行为是金属材料经典塑性理论&#xff0c;采用mises屈服面定义各向同性屈服。 一般金属材料都是各向同性材料&#xff0c;弹塑性行为&#xff1a; 小应变时&#xff0c;材料表现为线弹性&…

【5G MAC】随机接入流程中的 Msg2 (RAR)

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

FL Studio水果2023版本更新下载汉化教程

Image-Line宣布针对Win和Mac版本的数字音频工作站FL Studio的21版本更新。FL Studio2023是一个完整的软件音乐制作环境或数字音频工作站&#xff08;DAW&#xff09;。代表超过 23年的创新发展&#xff0c;它包含了您在一个包装中编排&#xff0c;编排&#xff0c;录制&#xf…

cocos creator实现浏览星球的功能,附源码

预览效果&#xff1a; 技术要点&#xff1a; 主摄像机的视场轴需要设置为水平。在场景下创建一个空节点用于挂载控制器脚本图片已进行各概念的说明 在“collisionNodeArray”属性下&#xff0c;放置需要点击的星球节点&#xff0c;系统会自己绑定碰撞器。 也可自己提前绑定。 布…

基于SSM的学籍证明打印系统设计与实现。

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

QT下TCP协议实现数据网络传输

QT开发框架以其跨平台的优势&#xff0c;在全世界IT界如雷贯耳。其封装了功能齐全的各种类&#xff0c;大大的提高了开发者的效率。本篇内容将介绍如何使用QT 6.4.1框架开发服务器和客户端程序&#xff0c;让两端能够首发消息&#xff0c;服务端往客户端发送文件&#xff08;客…

Spark在Yarn集群的两种提交模式

目录 一.Yarn Client(yarn的客户端模式) 二.Yarn Cluster(yarn的集群节点模式) 三.两者的差异 一.Yarn Client(yarn的客户端模式) 第一步&#xff1a;Driver端会在提交的本地机上运行 第二步&#xff1a;Driver端启动后会跟ResourceManager(RM)进行通信,申请启动一个Applic…

Linux安装Samba服务,基于Fedora

Linux安装Samba服务&#xff0c;基于Fedora1 安装samba服务2 启动samba服务3 更改配置信息4 使用windows系统进行连接5 其他说明1 安装samba服务 1 关闭防火墙及关闭防火墙开机自启 [whs02fedora ~]$ &#xff1a;sudo systemctl stop firewalld.service [whs02fedora ~]$ &a…

splay树:hdu4453 Looploop

题目链接如下&#xff1a; Problem - 4453 主要是要对区间操作和这种splay树的性质比较清楚。 关于区间我们设立两个额外节点&#xff0c;用来设立最开始的左右区间。 性质方面&#xff0c;其实就是二叉搜索树的性质&#xff0c;这里的体现就是中序遍历就是顺时针访问输入数…

《统计学习方法》 第十四章 聚类方法

聚类方法 1.聚类是针对给定的样本&#xff0c;依据它们属性的相似度或距离&#xff0c;将其归并到若干个“类”或“簇”的数据分析问题。一个类是样本的一个子集。直观上&#xff0c;相似的样本聚集在同类&#xff0c;不相似的样本分散在不同类。 2.距离或相似度度量在聚类中…

压力传感器

压力传感器 压力传感器是最常用的一种传感器&#xff0c;其应用范围有各种工业互通环境&#xff0c;涉及航空&#xff0c;航天&#xff0c;军工&#xff0c;石化&#xff0c;电力等。按照不同的测试&#xff0c;压力类型可分表压传感器&#xff0c;差压传感器&#xff0c;绝压…

现代密码学导论-19-基于伪随机函数的CPA安全

目录 3.5.2 基于伪随机函数的CPA安全 基于伪随机函数的加密示意图 CONSTRUCTION 3.28 构造基于伪随机函数的CPA安全的加密方案 THEOREM 3.29 方案3.28是CPA安全的 THEOREM 3.29 的证明 3.5.2 基于伪随机函数的CPA安全 基于伪随机函数的加密示意图 CONSTRUCTION 3.28 构造…