做阿里巴巴网站费用吗/网站推广途径和推广要点
前言
Java 中的反射大家都不陌生,有很多很好的文章都进行了讲解,但是很难找到一篇文章能完全解释反射的所有用法,特别是反射获取这个对象的注解的信息和泛型信息,往往都停留在了获取类的函数、方法,构造上。所以这篇文章将全面讲解 Java 反射中的所有用法,特别是对于类中的注解的获取上。另外,本文章不会对反射的原理进行讲解,看完本文你应该只能学会怎么用反射,至于其原理,本文不会涉及。
首先我们自定义两个类和一个注解,用于后续我们来演示反射的用法。简单来说,就是 Student
类继承自 Person
类,并在 Student
类上使用了 Work
注解。Person
类比较简单,我就不贴出来了,另外两个 Java 文件如下,我们后续将通过反射获取这个类对象的所有信息。
Work.java
:
package lic.reflect;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Documented()
@Inherited()
public @interface Work {String value();int category() default 0;Class<?> workClass();
}
Student.java
:
package lic.reflect;@Work(value = "college", workClass = Person.class)
public class Student extends Person implements Cloneable {private volatile int level;private Student(String name) {super(name);}public Student(String name, int age) {super(name, age);}@Work(value = "math", workClass = HashMap.class)public List<String> study(Map<String, Integer> map) {System.out.println(this + " study:\n");List<String> list = new ArrayList();for (Entry<String, Integer> entry : map.entrySet()) {System.out.println(entry.getKey() + " : " + entry.getValue());list.add(entry.getKey());}return list;}@Overridepublic void printOccupation() {System.out.println("occupation student.");}
}
获取 Class 对象的四种方式
java.lang.Class
类是反射机制的基础,存放着对应类型对象的运行时信息。在 Java 程序运行时,Java虚拟机为所有类型维护一个 java.lang.Class
对象,即每种类型的 Class
对象只有一个。
在使用反射做任何事情之前,我们必须获得 Class
对象,可以说,Class
对象是我们进行反射操作的入口。那么怎么获取这个 Class
对象呢?主要有四种方式:
//方式一:Object.getClass()
Class studentClass = student.getClass();
//方式二:T.class
Class studentClass = Student.class;
//方式三:static Class<?> Class.forName
try {Class studentClass_3 = Class.forName("lic.reflect.Student");System.out.println("Class.forName = " + studentClass);
} catch (ClassNotFoundException exception) { //此方式会抛出 ClassNotFoundException 异常exception.printStackTrace(System.err);
}
//方式四:通过 ClassLoader
try { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Class<?> c = classLoader.loadClass("java.lang.String");
} catch (ClassNotFoundException e) {e.printStackTrace();
}
无论通过哪种方式,其获取的 Class
对象都是一样的。
在 Class
对象中,我们一般会获取的有个三类信息:构造方法 Constructor
、成员方法 Method
、成员变量 Field
。而后续作用域对象的操作都必须先过去到其对应的上述三类信息,下面我们一一道来。
构造方法 Constructor
Class
类中用于获取构造方法的方法:
Constructor<?>[] getConstructors()
返回所有公共构造方法对象的数组(只能获取 public 的构造方法)Constructor<?>[] getDeclaredConstructors()
返回所有构造方法对象的数组(所有构造方法,包含 private)Constructor<T> getConsturctor(Class<?>... parameterTypes)
返回单个指定的公共构造方法Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
返回单个指定的构造方法
注意这上面,带 declared
的方法和不带 declared
的方法的区别:
- 不带
Declared
的方法支持取出包括继承、公有(Public) 但不包括有(Private)的构造函数 - 带
Declared
的方法是支持取出包括公共(Public)、保护(Protected)、默认(包)访问和私有(Private)的构造方法,但不包括继承的构造函数
这个规则不仅适用于获取 Constructor
、还适用于获取 Method
和 Field
对象。
获取到 Constructor
对象之后,即可用其创建对象,相关方法如下:
T newInstance(Object... initargs)
根据指定的构造方法创建对象setAccessible(boolean flag)
设置是否取消访问检查,设置为 true 表示取消访问检查,可以提高反射效率
下面我们试一下上面的几个方式获取构造器:
Constructor[] constructors = studentClass.getConstructors();
for(Constructor constructor: constructors) {System.out.println(constructor);
}
//输出:
//public lic.reflect.Student(java.lang.String,int)constructors = studentClass.getDeclaredConstructors();
for(Constructor constructor: constructors) {System.out.println(constructor);
}
//输出:
//private lic.reflect.Student(java.lang.String)
//public lic.reflect.Student(java.lang.String,int)try {Constructor constructor = studentClass.getDeclaredConstructor(String.class, Integer.TYPE);System.out.println(constructor);
} catch (NoSuchMethodException|SecurityException e) {e.printStackTrace();
}
//输出:
//public lic.reflect.Student(java.lang.String,int)
当获取了 Constructor
之后,即可以使用这个对象来创建 Student
类对象:
constructor.setAccessible(true); //取消访问检查
Student student = (Student)constructor.newInstance("Alan", 30); //创建对象
System.out.println(student);
成员变量 Field
Class 类中用于获取成员变量的方法:
Field[] getFields()
返回此类和父类中所有public的成员变量Field[] getDeclaredFields()
返回此类中所有的成员变量,不包括父类FIeld getField(String name)
从此类和父类中查找对应的public成员变量并返回Field getDeclaredField(String name)
在此类中查找指定的成员变量并返回
Field 类中用于设置和获取成员变量的方法:
void set(Object obj, Object value)
设置 obj 对象中的此成员变量设置为 valueObject get(Object obj)
获取 obj 对象中的此成员变量String getName()
获取此成员变量的名称Class<?> getType()
获取此成员变量的类型int getModifiers()
获取此成员变量的修饰符(即private
volatile
之类的),通过 Modifier 类方便查询
下面是 Field 这个类的使用示例:
Field[] fields = studentClass.getFields();
for(Field field: fields) System.out.println(field);
//输出:父类的 public name 成员变量
//public java.lang.String lic.reflect.Person.nameField[] fields = studentClass.getDeclaredFields();
for(Field field: fields)
System.out.println(field);
//输出:本类的 private level 成员变量
//private int lic.reflect.Student.leveltry {Field field = studentClass.getDeclaredField("level");String fieldName = field.getName();System.out.println(fieldName); //输出 levelClass fieldType = field.getType();System.out.println(fieldType); //输出 intint modifier = field.getModifiers();System.out.println(Modifier.isPrivate(modifier)); //输出 trueSystem.out.println(Modifier.isVolatile(modifier)); //输出 true
} catch (NoSuchFieldException | SecurityException exception) {exception.printStackTrace();
}
成员方法 Method
Class 类中用于获取成员变量的方法:
Method[] getMethdos()
返回所有 public 成员方法的对象,包括继承的Method[] getDeclaredMethods()
返回此类中所有成员方法对象的数组Method getMethod(String name, Class<?>... parameterTypes)
查找此类及父类中指定的 public 成员方法Method getDeclaredMethod(String name, Class<?>... parameterTypes)
查找并返回单个成员方法
Method 类中的常用方法:
Object invoke(Object obj, Object... args)
调用 obj 对象上的这个方法Class getReturnType()
返回这个方法的返回值类型Type getGenericReturnType()
返回这个方法的泛型返回值类型Class[] getParameterTypes()
返回这个方法的参数类型Type[] getGenericParameterTypes()
返回这个方法的参数的泛型信息Annotation[] getAnnotations()
返回这个方法上的注解
下面是 Method 类的使用示例:
Method[] methods = studentClass.getMethods();
for(Method method: methods)System.out.println(method);
//输出:可见大部分都是从 Object 类继承而来的方法
//public void lic.reflect.Student.study(java.util.Map)
//public void lic.reflect.Student.printOccupation()
//public java.lang.String lic.reflect.Person.getName()
//public void lic.reflect.Person.setName(java.lang.String)
//public int lic.reflect.Person.getAge()
//public void lic.reflect.Person.setAge(int)
//public static void lic.reflect.Person.staticFunc()
//public boolean java.lang.Object.equals(java.lang.Object)
//public java.lang.String java.lang.Object.toString()
//public native int java.lang.Object.hashCode()
//public final native java.lang.Class java.lang.Object.getClass()
//public final native void java.lang.Object.notify()
//public final native void java.lang.Object.notifyAll()
//public final void java.lang.Object.wait(long) throws java.lang.InterruptedException
//public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
//public final void java.lang.Object.wait() throws java.lang.InterruptedExceptionMethod[] methods = studentClass.getDeclaredMethods();
for(Method method: methods)System.out.println(method);
//输出:
//public void lic.reflect.Student.study(java.util.Map)
//public void lic.reflect.Student.printOccupation()try {Method method = studentClass.getMethod("study", Map.class);System.out.println(method);Map<String, Integer> methodArgs = new HashMap<String, Integer>();methodArgs.put("math", 10);method.invoke(student, methodArgs); //study 方法被正常调用
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException e) {e.printStackTrace();
}Class returnType = method.getReturnType();
//interface java.util.List
Type type = method.getGenericReturnType();
//java.util.List<java.lang.String>Class[] parameterClass = method.getParameterTypes();
//interface java.util.Map
Type[] types = method.getGenericParameterTypes();
//java.util.Map<java.lang.String, java.lang.Integer>
Annotation[] annotations = method.getAnnotations();
//@lic.reflect.Work(category=0, value="math", workClass=java.util.HashMap.class)
对注解进行解析
上面的代码中,我们通过 method.getAnnotations()
方法获取了 study
方法上的注解,下面将讲解如何解析注解的一些信息。注解是由 Annotation
这个接口来表示,为了获取注解上的信息,我们首先就要获取这个注解的 Class 对象:
Class<? extends Annotation> annotationType()
获取这个注解的 Class 对象
有了这个 Class
对象再来看注解的解析。实际上跟上述获取类的成员方法并调用是一样的:
Method[] methods = annoClass.getDeclaredMethods();
//输出
//public abstract java.lang.String lic.reflect.Work.value()
//public abstract int lic.reflect.Work.category()
//public abstract java.lang.Class lic.reflect.Work.workClass()
如果我们要获取定义在 study()
方法上的定义的注解 @Work(value = "math", workClass = HashMap.class)
,只需要进行如下的调用即可:
String value = (String) methods[0].invoke(annotation);
System.out.println(value); //math
int category = (int) methods[1].invoke(annotation);
System.out.println(category); //0
Class workClass = (Class) methods[2].invoke(annotation);
System.out.println(workClass); //class java.util.HashMap
与反射相关的三个类
通过上面的解读,我们肯定对反射有了基本的认识和运用,在使用反射中,我们往往会碰到三个类或接口,其中另一个就是上面说的 Class
。而另外两个,一个是 Type
接口,一个是 AnnotatedType
接口。下面就对这两个接口简单解释一下。
Type 接口
Type
是一个顶层接口,表示 Java 语言中的所有类型,它的几个子接口和实现类如下:
Class<?>
:表示类或接口类型ParameterizedType
:表示参数化类型(例如,List<String>
)GenericArrayType
:表示泛型数组类型(例如,T[]
)TypeVariable<D extends GenericDeclaration>
:表示类型变量(例如,T
)WildcardType
:表示通配符类型(例如,? extends Number
)
Type 接口主要用来在反射中表示和处理各种类型。通过这个接口,可以获取类型的详细信息并执行相应的操作。一般来说,当我们获取泛型类型的参数、确定数组类型时,会用到这个接口。例如:
public class Example {private List<String> list;public static void main(String[] args) throws NoSuchFieldException {Field field = Example.class.getDeclaredField("list");Type type = field.getGenericType();System.out.println("Type: " + type); // 输出:Type: java.util.List<java.lang.String>}
}
AnnotatedType 接口
AnnotatedType
接口是 AnnotatedElement
的一个子接口,表示带有注解的类型,允许访问和操作类型上的注解。但我们需要获取注解信息时,就必须获取使用这个接口。例如:
@Retention(RetentionPolicy.RUNTIME)
@interface ExampleAnnotation {
}public class Example {private @ExampleAnnotation List<String> list;public static void main(String[] args) throws NoSuchFieldException {Field field = Example.class.getDeclaredField("list"); AnnotatedType annotatedType = field.getAnnotatedType(); System.out.println("Annotations: " + annotatedType.getAnnotations().length); // 输出:Annotations: 1 }
}
Class 类
Class
对象是 Java 反射机制中的核心概念之一。它是 Type 接口的一个实现类,代表了运行时的类或接口。Class
对象不仅仅是类型信息的载体,还提供了大量的方法来检查类的结构、创建实例、访问成员等。
这个类上面说了一大堆的内容,这里就不赘述了。
总结
Java 反射机制是 Java 语言的重要特性之一,如果你开发框架或库时,掌握反射的使用是必须的。例如大名鼎鼎的 Retrofit、Gson、Room 等,都大量使用了反射。所以作为 Android 开发者,这个反射咱们还真是得好好掌握。
相关文章:

Java Reflection 反射使用 完全指南
前言 Java 中的反射大家都不陌生,有很多很好的文章都进行了讲解,但是很难找到一篇文章能完全解释反射的所有用法,特别是反射获取这个对象的注解的信息和泛型信息,往往都停留在了获取类的函数、方法,构造上。所以这篇文…...

2024年适合学生写作业的台灯推荐:五款公认好用的护眼台灯
儿童的视力健康是每一个家庭都极为关注的问题。目前中国近视率居高不下,且呈现出年轻化、低龄化的趋势。儿童近视的问题愈发严重,如何才能让孩子在学习的同时,眼睛也能得到充分的保护?答案就是护眼台灯。护眼台灯通常拥有柔和的光…...

电商平台API电商平台数据传输(商品订单店铺数据采集)
API(Application Programming Interface)是一种用于应用程序之间相互通信和交互的接口。它定义了一组规范和协议,允许软件系统之间传递数据和请求服务。 API的基本概念包括: 1. 接口:API作为接口,提供了一…...

【LeetCode每日一题】3067. 在带权树网络中统计可连接服务器对数目-DFS和图
Hey我的编程小伙伴们👋,今天我要和大家分享一道我在LeetCode上遇到的超有趣的题目——编号3067的在带权树网络中统计可连接服务器对数目。这是一道非常适合练习DFS和图的题目哦!🤓💻 邻接图是什么? 在我们…...

java中的时间相关类
LocalDate: 用于表示日期。 public final class LocalDate {private final int year;private final int month;private final int day;}LocalTime: 用于表示时间。 public final class LocalTime {private final byte hour;private final byte minute;private final byte se…...

大模型的现状与未来:探索腾讯元宝APP及其他AIGC产品
前言 随着近日腾讯元宝APP的正式上线,国内大模型产品又添一员。近年来,随着人工智能技术的快速发展,AIGC(AI生成内容)产品逐渐成为技术与商业应用的热点。各大互联网厂商纷纷推出自己的大模型产品,以期在这…...

记录一个apisix修改后台接口超时时间的方法
垃圾程序猿搞了个数据导入,解析校验比较复杂,1000条就要70秒。apisix默认60s超时,导致提交导入功能总是失败。 非要先调整超时时间。这里记录一下 到服务器配置yaml如下: apiVersion: apisix.apache.org/v2 kind: ApisixUpstrea…...

地产样板间vr全景云展平台降低售房压力
在数字化浪潮的推动下,传统的实体展厅正面临着巨大的转型压力。高昂的搭建、物流、安保成本,以及展览的周期性和资源浪费,都成为了展商们不得不面对的难题。然而,现在有了商品3D线上展台搭建编辑器,这些问题都迎刃而解…...

性能测试2【搬代码】
1.性能测试脚本完善以及增强 2.jmeter插件安装以及监控使用 3.性能压测场景设置(基准、负载、压力、稳定性) 4. 无界面压测场景详解 一、性能测试脚本完善以及增强 使用控制器的目的是使我们的脚本更加接近真实的场景 1.逻辑控制器: 【事务控制器】&…...

Chromium源码阅读:深入理解Mojo框架的设计思想,并掌握其基本用法(1)
Mojo简介 Mojo 是一个运行时库的集合,提供与平台无关的通用 IPC 原语抽象、消息 IDL 格式以及具有针对多种目标语言的代码生成的绑定库,以便于跨任意进程间和进程内边界传递消息。 Mojo 分为清晰分离的层,子组件的基本层次结构如下ÿ…...

通用大模型VS垂直大模型对比
通用大模型和垂直大模型的区分主要在于它们的设计目的、应用范围、训练数据、优化目标和使用场景。以下是一些关键点,用以区分这两种模型: 设计目的: 通用大模型:设计用于处理多种类型的任务,不特定于某一领域。垂直大…...

时尚解决方案来袭:几分钟即可生成高清商拍大片
在时尚行业,视觉展示的重要性不可小觑。商品图片不仅代表品牌的风格调性,而且直接影响消费者的购买行为。可以说,视觉营销在服装行业中的地位至关重要。 尽管如此,视觉营销的传统产出渠道——商业摄影,因其高成本、复杂…...

【每日一练】day1
✨✨谢谢大家捧场,祝屏幕前的小伙伴们每天都有好运相伴左右,一定要天天开心哦!✨✨ 🎈🎈作者主页: 🎈丠丠64-CSDN博客🎈 ✨✨ 帅哥美女们,我们共同加油!一起…...

GA/T 1400 (非标)视图库网关
GA/T 1400 (非标)视图库网关 应用概述: GAT1400视图库网关产品是公司“分布式综合安防管理平台”下的子系统 针对以下遇到应用场景定制开发、优化后形成的网关产品,具备兼容性高、可扩展、可功能定制、可OEM等优点。 视图库网关…...

QT安装及项目创建
一、QT安装 1、安装qt_creater 方法一: 镜像文件:在2024-6-12:版本已经更新到了6.7 下载地址:https://download.qt.io/archive/qt/ 方法二: 百度网盘:链接:https://pan.baidu.com/s/1D0EmH…...

15. STUN协议和ICE工作原理
NET介绍 NAT是一种地址转换技术,它可以将IP数据报文头中的IP地址转换为另一个IP地址,并通过转换端口号达到地址重用的目的。 在大多数网络环境中,我们都需要通过 NAT 来访问 Internet。 NAT作为一种缓解IPv4公网地址枯竭的过渡技术ÿ…...

JVM (一)内存模型
一。内存结构 1,JVM内存结构 堆内存:是JVM中最大的一块,由新生代和老年代组成。默认情况下新生代按照8:1:1的比例来分配; 方法区:存储类信息、常量、静态变量等数据,是线程共享的区域; 栈&#…...

Web前端职业描述:编织数字世界的绚丽画卷
Web前端职业描述:编织数字世界的绚丽画卷 在数字化浪潮席卷而来的今天,Web前端职业日益成为技术领域的璀璨明星。他们不仅是数字世界的建筑师,更是用户体验的缔造者。那么,Web前端职业究竟是怎样的呢?接下来ÿ…...

负氧离子监测站:打造健康生态的守护者
TH-FZ5随着人们对生活质量和健康水平的要求日益提高,空气质量成为了公众关注的焦点。其中,负氧离子作为空气中的一种重要成分,对人体健康有着显著的影响。负氧离子监测站作为监测空气中负氧离子浓度的专业设备,在现代环境监测和生…...

在调用接口上map与forEach的区别
在场景:一个表格数据需要上传,每行表格需要上传图片->这就需要在提交时对数据也就是数组进行处理(先将每个元素图片上传拿到图片id 这种情况我刚开始就用的map处理,然后问题来了,提交的接口调用了,但是…...

最短路:spfa算法
最短路:spfa算法 题目描述参考代码 题目描述 参考代码 输入示例 3 3 1 2 5 2 3 -3 1 3 4输出示例 2#include <iostream> #include <cstring> #inc…...

算法笔记 图论和优先级队列的笔记
图论 DFS stack O(h) 不具有最短性 BFS queue O(2^h) 最短路 迪杰斯特拉算法 初始化: 将起始节点 A 的距离设为 0。将其他所有节点的距离设为无穷大。创建一个优先队列,并将起始节点 A 加入优先队列。 处理队列: …...

6.每日LeetCode-数组类,找到所有数组中消失的数字
题目 448找到所有数组中消失的数字.go 给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。 示例 1: 输入:nums [4,3,2,7,8,2,…...

【Three.js】知识梳理十:Three.js纹理贴图
1. 纹理贴图 在Three.js中,纹理贴图是一种将二维图像贴到三维物体表面的技术,以增强物体的视觉表现。纹理贴图可以使物体表面更加真实、细腻,为场景增色不少。 在Three.js中,纹理贴图的加载主要通过THREE.TextureLoader类实现。…...

mysql order by后跟case when
在SQL中,ORDER BY子句用于对查询结果进行排序。当在ORDER BY后面使用CASE语句时,它的原理是:根据CASE语句中定义的条件和结果,为查询结果集中的每一行生成一个临时的排序值。然后,根据这些排序值对结果集进行排序。 具…...

数字孪生赋能的智慧园区物联网云平台建设方案(97页PPT)
方案介绍: 本方案通过数字孪生技术赋能智慧园区物联网云平台,实现了园区的智能化管理、优化资源配置、提高运营效率等目标。同时提升园区的安全性、环保性和可持续性。最后,该方案还充分考虑了系统的可扩展性、安全性和可靠性,为…...

TikTok小店运营策略
TikTok,作为一款全球知名的短视频社交平台,其用户基数庞大且日活跃用户持续增长,为商家提供了巨大的商机。欧洲作为TikTok的重要市场之一,其小店功能为商家提供了一个展示和销售产品的新渠道。本文将探讨如何有效地运营TikTok小店…...

Docker面试整理-如何查看和管理Docker容器的日志?
管理和查看 Docker 容器的日志是 Docker 容器管理的重要部分,有助于监控应用的行为和诊断问题。Docker 提供了几种方法来查看和管理容器日志。 查看容器日志 要查看 Docker 容器的日志,你可以使用 docker logs 命令。这个命令会打印容器的 STDOUT 和 STDERR 输出,这是大多数…...

Java从放弃到继续放弃
并发编程 为什么需要多线程? 由于硬件的发展,CPU的核数增多,如果仍然使用单线程对CPU资源会造成浪费。同时,单线程也会出现阻塞的问题。所以,选择向多线程转变。 多线程的使用使得程序能够并行计算,提高计…...

上传文件生成聊天机器人,实现客服、办公自动化智能体 | Chatopera
从谈论聊天机器人,到谈论智能体,是目前人工智能最炙手可热的话题,这两年最大的变化是大语言模型的应用。聊天机器人曾经很难定制,往往局限于个别行业,同时也只有行业内的领导者、头部企业能定制。比如银行、金融证券、…...