"talk is cheap, show me the code"。接下来将从实际应用的角度,说明如何使用反射。关于反射相关的概念,可以参考Java反射概述博文。 使用反射的基本步骤如下:
(1) 获取类型的Class对象;
(2) 基于Class对象获取类型元数据:Constructor类型、Field类型、Method类型;
(3) 基于Constructor类型、Field类型、Method类型访问成员。

获取Class对象

Class类是由class关键字修饰的一个特殊的类。Class实例可以看成是Object及其子类的元数据引用。一个Java类均对应一个Class实例。该Class实例引用可以从getClass方法获取。使用Class对象可以获取对应类型的元数据(metadata)信息,如构造器、字段、方法等。
获取Class对象的方法有很多种。这里介绍下常用的几种方法:
(1) 使用Class类的forName静态方法;
(2) 使用Ojbect根类的class静态字段;
(3) 使用类型实例的getClass()方法;
(4) 使用ClassLoader实例的loadClass方法。
在介绍获取Class对象的方法前,先预定义测试类,方便后面统一使用。

// package io.github.courage007.reflect;@Getter
@Setter
public class Apple {private String color;private String size;public Apple() {this.color = "red";this.size = "medium";}public Apple(String color, String size) {this.color = color;this.size = size;}public void changeByTime() {this.color = "gray";this.size = "small";}private void changeColor() {this.color = "gray";}
}

使用Class类的forName静态方法

Class类提供forName静态方法用于获取Class实例。相关源码片段如下:

public final class Class<T> implements java.io.Serializable,GenericDeclaration,Type,AnnotatedElement {// ...@CallerSensitivepublic static Class<?> forName(String className)throws ClassNotFoundException {Class<?> caller = Reflection.getCallerClass();return forName0(className, true, ClassLoader.getClassLoader(caller), caller);}@CallerSensitivepublic static Class<?> forName(String name, boolean initialize,ClassLoader loader)throws ClassNotFoundException{Class<?> caller = null;SecurityManager sm = System.getSecurityManager();if (sm != null) {// Reflective call to get caller class is only needed if a security manager// is present.  Avoid the overhead of making this call otherwise.caller = Reflection.getCallerClass();if (loader == null) {ClassLoader ccl = ClassLoader.getClassLoader(caller);if (ccl != null) {sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);}}}return forName0(name, initialize, loader, caller);}// since java 9@CallerSensitivepublic static Class<?> forName(Module module, String name) {Objects.requireNonNull(module);Objects.requireNonNull(name);ClassLoader cl;SecurityManager sm = System.getSecurityManager();if (sm != null) {Class<?> caller = Reflection.getCallerClass();if (caller != null && caller.getModule() != module) {// if caller is null, Class.forName is the last java frame on the stack.// java.base has all permissionssm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);}PrivilegedAction<ClassLoader> pa = module::getClassLoader;cl = AccessController.doPrivileged(pa);} else {cl = module.getClassLoader();}if (cl != null) {return cl.loadClass(module, name);} else {return BootLoader.loadClass(module, name);}}/** Called after security check for system loader access checks have been made. */private static native Class<?> forName0(String name, boolean initialize,ClassLoader loader,Class<?> caller)throws ClassNotFoundException;
}

可以看到,forName静态方法有三个重载方法。其使用示例如下:

public void getClassRefByForName() {try {Class clazz1 = Class.forName("io.github.courage007.reflect.Apple");// Returns the class of the caller of the method calling this method// 获取调用当前方法的方法的调用者Class<?> caller = Reflection.getCallerClass();Class clazz = Class.forName("io.github.courage007.reflect.Apple", false, caller.getClassLoader());} catch (ClassNotFoundException ex) {throw new RuntimeException("get class failed");}// java 9新增基于Module获取Class实例,这里不再讨论,有兴趣的同学可以自行学习
}

基于类的全路径名获取 Class 对象的方式,其优势是不需要事先引入依赖,适用于运行时调用的场景。需要说明的是,由于代码里硬编码了类的全路径名,不能很好的适应类的全路径名变化场景,生产上可以这部分数据放置在文件或数据库等存储介质中。

使用Ojbect根类的静态class字段

Ojbect根类提供静态class字段,可以直接获取Class实例。示例代码如下:

public void getClassRefByStaticField() {Class clazz = Apple.class;
}

使用这种方法,需要引入类对应的包。该方式主要应用于调用方。

使用类型实例的getClass()方法

Ojbect根类提供getClass方法,用于获取实例的Class实例。关键代码如下:

public class Object {// .../*** Returns the runtime class of this {@code Object}. The returned* {@code Class} object is the object that is locked by {@code* static synchronized} methods of the represented class.*/@HotSpotIntrinsicCandidatepublic final native Class<?> getClass();
}

所以,可以使用getClass方法获取Class实例。示例代码如下:

public void getClassRefByGetClassMethod() {Apple apple = new Apple();Class clazz = apple.getClass();
}

需要说明的是,使用这种方法,需要引入实例所属类的包。这种方式的调用多出现于调用方。对于自身来说,因为已经有了类的实例,无需再通过Class实例去构造实例并访问字段或方法。

使用ClassLoader实例的loadClass方法

ClassLoader实例提供loadClass方法来实现指定类的全路径名来获取Class实例。示例代码如下:

public void getClassRefByClassLoader() {try {ClassLoader classLoader = this.getClass().getClassLoader();Class clazz = classLoader.loadClass("io.github.courage007.reflect.Apple");System.out.println(clazz.toString());} catch (ClassNotFoundException ex) {throw new RuntimeException("get class failed");}
}

基于ClassLoader获取Class的方式与基于Class类的forName静态方法获取Class的方式一样,都是基于类的全路径名获取 Class 对象。其优缺点不再赘述。

基于反射构造实例

获取Class对象后,就可基于Class对象实现构造方法、字段、方法的调用。这里介绍如何基于Class对象调用构造方法以实现实例创建。
按照访问类型、参数个数,可将构造函数分为如下四类:公有无参构造函数公有带参构造函数私有无参构造函数私有带参构造函数

公有无参构造函数

Class类提供newInstance方法,用于创建类的实例。但是,该方法会返回空实例,在Java 9之后已经弃用,推荐先基于Class获取Constructor实例,然后基于Constructor的newInstance方法去创建类型实例。需要说明的是Constructor的newInstance方法也可创建公有带参构造函数,示例代码如下:

public void getInstanceWithoutParam() {try {Class clazz = Class.forName("io.github.courage007.reflect.Apple");Apple apple = (Apple) clazz.getConstructor(null).newInstance(null);apple.getColor();} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ex) {throw new RuntimeException("get class constructor failed");}
}

已弃用的Class类的newInstance方法代码片段如下:

public final class Class<T> implements java.io.Serializable,GenericDeclaration,Type,AnnotatedElement {// .../*** 创建基于Class实例的类的实例。** @deprecated 这个方法会传递空实例,已被弃用。推荐使用* java.lang.reflect.Constructor的newInstance(java.lang.Object...)方法*/@CallerSensitive@Deprecated(since="9")public T newInstance() throws InstantiationException, IllegalAccessException {// ...try {// 创建无参return tmpConstructor.newInstance((Object[])null);} catch (InvocationTargetException e) {Unsafe.getUnsafe().throwException(e.getTargetException());// Not reachedreturn null;}}
}

公有带参构造函数

Constructor的newInstance方法支持创建公有带参构造函数,示例代码如下:

public void getInstanceWithParam() {try {Class clazz = Class.forName("io.github.courage007.reflect.Apple");Apple apple = (Apple) clazz.getConstructor(String.class, String.class).newInstance("green", "small");apple.getColor();} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ex) {throw new RuntimeException("get class constructor failed");}
}

私有构造函数

Class实例的getConstructor方法只能获取包含父类的某个public的构造方法,对于某个非public访问权限的构造方法,则需使用
getDeclaredConstructor方法。需要说明的是,在使用非public的Constructor时,必须先执行setAccessible(true)方法,设置允许访问。
经过对公有构造函数调用可以发现,基于Constructor调用公有无参构造函数和公有待参构造函数,其差异性仅体现在传参。私有无参构造函数和私有带参构造函数有类似处理机制。这里仅以私有带参构造函数为例,私有无参构造函数处理类似。示例代码如下:

public void getInstanceWithPrivateConstructor() {try {Class clazz = Class.forName("io.github.courage007.reflect.Apple");Constructor currentConstructor = clazz.getDeclaredConstructor(String.class);currentConstructor.setAccessible(true);Apple apple = (Apple)currentConstructor.newInstance("yellow");apple.getColor();} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ex) {throw new RuntimeException("get class constructor failed");}
}

基于反射获取字段

与基于反射构造实例先通过Class对象获取Constructor对象,然后再基于Constructor对象调用构造函数类似,基于反射获取字段先通过Class对象获取Field对象,然后再基于Field对象访问字段。Field提供一系列方法,用于操作字段,常用的方法有:

getName():返回字段名称
getType():返回字段类型,也是一个Class实例,如String.class
getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的访问权限
setAccessible():设置变量为public
set():设置字段值
get():获取字段值

这里以访问私有字段为例,介绍下如何基于反射访问字段。示例代码如下:

public void getFieldWithPrivatePermission() {try {Class clazz = Class.forName("io.github.courage007.reflect.Apple");// 获取private字段"grade":Field colorField = clazz.getDeclaredField("color");colorField.setAccessible(true);// 获取字段名colorField.getName();// 获取字段类型colorField.getType();// 获取字段的访问权限colorField.getModifiers();Apple apple = (Apple) clazz.getConstructor(String.class, String.class).newInstance("green", "small");apple.getColor();// 设置字段colorField.set(apple, "red");// 获取字段值colorField.get(apple);} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException |  InstantiationException | InvocationTargetException | NoSuchMethodException ex) {throw new RuntimeException("get class field failed");}
}

基于反射获取方法

与基于反射构造实例和基于反射获取字段类似,基于反射获取方法先通过Class对象获取Method对象,然后再基于Method对象访问方法。Method提供一系列方法,用于访问方法,常用的方法有:

getName():返回方法名称
getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class
getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class}
getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义
setAccessible():设置private函数为public属性
invoke(object,new Object[]{}) 调用执行方法

这里以访问私有方法为例,介绍下如何基于反射调用方法。示例代码如下:

public void invokeMethodWithPrivatePermission() {try {Class clazz = Class.forName("io.github.courage007.reflect.Apple");Method changeColorMethod = clazz.getDeclaredMethod("changeColor");changeColorMethod.setAccessible(true);// 获取方法名changeColorMethod.getName();// 获取参数类型 Class[] 数组changeColorMethod.getParameterTypes();// 返回方法返回值类型 Class 实例changeColorMethod.getReturnType();// 获取方法的访问权限changeColorMethod.getModifiers();Apple apple = (Apple) clazz.getConstructor(String.class, String.class).newInstance("green", "small");// 调用方法changeColorMethod.invoke(apple, null);} catch (ClassNotFoundException | IllegalAccessException |  InstantiationException | InvocationTargetException | NoSuchMethodException ex) {throw new RuntimeException("get class method failed");}
}

基于反射获取父类成员

通过Class实例的getXxx类型方法可以获取包含父类的某个public的构造方法、字段、方法。对于非public构造方法、字段、方法,可以先通过获取getSuperclass获取父类对应的Class对象,然后通过Class对象访问非public构造方法、字段、方法。这里不再给出示例,有兴趣的同学,可以自行学习。

总结

Java提供Class类型、Constructor类型、Field类型、Method类型,帮助实现运行时访问类型实例上的成员。在获取成员时,根据成员的访问权限、声明位置,需要选用不同的方法,具体可以分为两类:
getXxx 获取包含父类的某个public的构造方法、字段、方法。
getDeclaredXxx 获取当前类的包含private访问权限的所有构造方法、字段、方法。

参考

https://www.anquanke.com/post/id/245458 Java安全之反射

Java反射--实战篇相关推荐

  1. java反射一篇搞定

    反射机制; 将类的各个组成部分封装成其他对象; 优点: 可以在程序运行过程中操作对象; 可以解耦,提高程序的可扩展性; JAVA代码在计算机中经历的三个阶段; Person类经过javac编译,成为了 ...

  2. JAVA SE 实战篇 C7 基于CSFramework的聊天室 (下) 客户端APP

    文章目录 P1 聊天室用户端APP功能分析 P2 进入聊天室前的准备 1 连接 ChatRoomConnectToServer 2 登录 ChatRoomLoginView 3 用户信息 UserIn ...

  3. 容我喝一杯 java_花一杯茶的时间,学会Java反射(实用篇)

    前言 之前,我写了一篇关于java反射的使用--花一杯茶的时间,学会Java反射(基础篇),今天就拿一些实例和应用给大家看看如何在项目中用反射. 实例 反射可以提高代码的扩展性,低耦合,高内聚.就拿工 ...

  4. 「实战篇」开源项目docker化运维部署-后端java部署(七)

    原创文章,欢迎转载.转载请注明:转载自IT人故事会,谢谢! 原文链接地址:「实战篇」开源项目docker化运维部署-后端java部署(七) 本节主要说说后端的部署需要注意的点,本身renren-fas ...

  5. Python和Java结合的项目实战_[项目实战] Python高级教程项目实战篇 Python和Java结合的项目实战 视频教程 [...

    资源介绍 课程简介:xa0xa0 Python高级教程项目实战篇 Python和Java结合的项目实战 视频教程 教学视频 ----------------------课程目录 Python项目实战篇 ...

  6. Java面向对象编程篇6——注解与反射

    Java面向对象编程篇6--注解与反射 1.注解概述 Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制 Java 语言中的类.方法.变量.参数和包等都可 ...

  7. Java—反射机制实战及动态代理总结

    关注微信公众号:CodingTechWork,一起学习进步. 引言   反射在Java技术应用中是老生常谈的事了,我们每次都是知道个皮毛,这个反射可以动态获取类的信息,比如类的属性和方法,但是在平时疯 ...

  8. 安琪拉教鲁班玩Java反射-业务场景篇

    安琪拉教鲁班玩Java反射-业务场景篇 Java 语言中有很多特性,其中有一项很重要的特性就是反射,我们在很多框架类的代码中可能会看到反射的身影,那实际在业务逻辑层是否可以用反射特性做些事情呢? 安琪 ...

  9. java o2o_Java项目实战篇之校园O2O商铺平台-开发准备一

    校园O2O商铺平台 一. 项目介绍 校园o2o平台是一个新兴的电子商务平台,主要是为了广大学生群体建立一个实用性,广泛性,快捷性的消费平台.就大学生的消费的状况,市场份额相当巨大,消费潜力也是非常可观 ...

  10. java反射案例实战

    本次主要场景为:excel导出功能,当对象Person有五个字段,依次为name,age,mobile,idCard,sex, 但是导出的Excel功能中,按照顺序导出name,mobile,sex等 ...

最新文章

  1. python中字符串的布尔值_Python基础之字符串,布尔值,整数,列表,元组,字典,集合...
  2. Python中sys模块详解
  3. 【c++】30.为什么可以通过指针或引用实现多态,而不可以通过对象呢?
  4. 关于Netty的入门使用
  5. Cesium之初始化视图
  6. HttpSession 和 HttpSession
  7. 【飞控理论】从零开始学习Kalman Filters之一:Kalman Filters的常见用途、什么是状态观测器?
  8. html5 ie10支持,ie10支不支持html5|Internet Explorer10完美支持HTML5_好特教程
  9. java 装饰流_java装饰流的使用【转】
  10. Python基础-高级变量类型
  11. 电脑公司特别版常用软件盘
  12. ubuntu下非常好用的PDF阅读器
  13. 雷达篇(四)雷达工作波段
  14. PCB板检测机(PCB板外观缺陷视觉检测设备)
  15. Python 深拷贝和浅拷贝详解
  16. 【Educoder作业】※ 数值信息——除二取余
  17. 计算机制造属于轻工业吗,哪些行业属于轻工业?
  18. drop、delete、truncate比较
  19. SQLite 数据库操作
  20. C++ Opencv中Mat的操作

热门文章

  1. iis6扩展php_教你IIS6的PHP最佳配置方法
  2. 算法:回溯七 Permutation Sequence数组全排列定位
  3. hadoop 如何连beeline_Hadoop家族新成员Hbase重磅来袭
  4. MAB多臂老虎机/赌博机
  5. git diff与git status
  6. 117.填充同一层的兄弟节点II
  7. Python 中Python 为什么要继承 object 类
  8. 【POJ 1113】Wall【凸包+一点思维】
  9. 【ccpc网络赛】Tree and Permutation【1009】【树上+组合数学】
  10. LaTeX在数学环境中使用直立体