目录

什么是反射?

获取.class字节码文件对象

获取该.class字节码文件对象的详细信息

通过反射机制执行函数

反射链


反射机制是java的一个非常重要的机制,一些著名的应用框架都使用了此机制,如struts、spring、hibernate、android app界面等等。

java.lang.Class它是java语法的一个基础类,用于描述一个class对象。在文件系统中,class以文件的形式存在。在运行的JVM中,*.class文件被加载到内存中成为一个对象,该对象的类型就是java.lang.Class。

什么是反射?

在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取信息以及动态调用对象的方法的功能就称为java语言的反射机制。也就是说,虽然我们获取不到该类的源代码,但是通过该类的.class文件能反射(Reflect)出这些信息。

通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。

想要使用反射机制,就必须要先获取到该类的字节码文件对象 .class,java.lang.Class类表示 .class 字节码文件对象。通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法、属性、类名、父类名、实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。

获取.class字节码文件对象

获取字节码文件对象的三种方式,有了字节码文件对象才能获得类中所有的信息,我们在使用反射获取信息时,也要考虑使用下面哪种方式获取字节码对象合理,视不同情况而定。

  1. //方法一
  2. Class clazz1 = Class.forName("my.Student");//通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。包名为 my,类名为 Student
  3. //方法二
  4. Class clazz2 = Student.class; //当类被加载成.class文件时,此时Student.java类变成了Student.class,该类处于字节码阶段
  5. //方法三
  6. Student s=new Student(); //实例化Student对象
  7. Class clazz3 = s.getClass(); //通过该类的实例获取该类的字节码文件对象,该类处于创建对象阶段

获取该.class字节码文件对象的详细信息

当我们得到一个.class字节码文件对象,我们可以得到以下信息:

  • 类名 (含package路径)
  • 函数 (名称,参数类型,返回值)
  • 域 (名称,类型)
  • 实现的接口 (interfaces)

使用 java.lang.reflect.*下的类来实现

  1. import java.lang.reflect.Constructor;
  2. import java.lang.reflect.Method;
  3. import java.text.DateFormat.Field;
  4. public class main
  5. public static void main(String[] args) throws Exception{
  6. Object obj=new Student(); //Student实例
  7. Class cls=obj.getClass(); //得到Student字节码对象
  8. //通过函数名和函数参数,得到函数
  9. String methodName="setId"; //函数名字
  10. Class[] par={int.class,String.class}; //函数参数
  11. Method m=cls.getMethod(methodName, par); //返回函数
  12. //调用method
  13. Object[] paramters={123,"haha"}; //传递参数
  14. m.invoke(obj,paramters); //执行函数
  15. cls.getPackage(); //获取包信息
  16. String str=cls.getSimpleName(); //Student ,获得该类的名字,不包含包名
  17. String str2=cls.getName(); //my.Student,获得该类的名字,包含包名
  18. Object obj2=cls.newInstance(); //创建一个实例,要求有一个不带参数的构造函数
  19. int modified=cls.getModifiers(); //获取cls对象的类修饰符
  20. Class[] interfaces=cls.getInterfaces(); //获取实现的接口集合
  21. Constructor[] con=cls.getConstructors(); //获取构造函数的集合
  22. Method[] methods=cls.getMethods(); //获取函数列表
  23. cls.getDeclaredAnnotations(); //获取私有函数(protected和private修饰的)
  24. java.lang.reflect.Field[] fields=cls.getFields(); //获取成员变量列表
  25. cls.getDeclaredField(str2); //获取私有变量(protected和private修饰的)
  26. }
  27. }

通过反射机制执行函数

如下,下面的代码中我们利用JAVA 的反射机制来调用计算器。我们利用了Java的反射机制把我们的代码意图都利用字符串的形式进行体现,使得原本应该是字符串的属性,变成了代码执行的逻辑。

  1. import java.lang.reflect.InvocationTargetException;
  2. public class main3 {
  3. public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException {
  4. Object runtime=Class.forName("java.lang.Runtime").getMethod("getRuntime",new Class[]{}).invoke(null); //得到Runtime.getRuntime()函数
  5. Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(runtime,"calc.exe"); //执行函数
  6. }
  7. }

运行代码,可以看到,执行了 calc.exe 的命令

反射链

什么是反射链呢?

我们来查看 org.apache.commons.collections.functors.InvokerTransformer.java 的源代码,这个接口实现了反射链,可以通过Java的反射机制来执行任意命令

  1. public class InvokerTransformer implements Transformer, Serializable {
  2. /*
  3. Input参数为要进行反射的对象,
  4. iMethodName,iParamTypes为调用的方法名称以及该方法的参数类型
  5. iArgs为对应方法的参数
  6. 在invokeTransformer这个类的构造函数中我们可以发现,这三个参数均为可控参数
  7. */
  8. private static final long serialVersionUID = -8653385846894047688L;
  9. private final String iMethodName;
  10. private final Class[] iParamTypes;
  11. private final Object[] iArgs;
  12. public static Transformer getInstance(String methodName) {
  13. if (methodName == null) {
  14. throw new IllegalArgumentException("The method to invoke must not be null");
  15. }
  16. return new InvokerTransformer(methodName);
  17. }
  18. public static Transformer getInstance(String methodName, Class[] paramTypes, Object[] args) {
  19. if (methodName == null) {
  20. throw new IllegalArgumentException("The method to invoke must not be null");
  21. }
  22. if (((paramTypes == null) && (args != null))
  23. || ((paramTypes != null) && (args == null))
  24. || ((paramTypes != null) && (args != null) && (paramTypes.length != args.length))) {
  25. throw new IllegalArgumentException("The parameter types must match the arguments");
  26. }
  27. if (paramTypes == null || paramTypes.length == 0) {
  28. return new InvokerTransformer(methodName);
  29. } else {
  30. paramTypes = (Class[]) paramTypes.clone();
  31. args = (Object[]) args.clone();
  32. return new InvokerTransformer(methodName, paramTypes, args);
  33. }
  34. }
  35. private InvokerTransformer(String methodName) {
  36. super();
  37. iMethodName = methodName;
  38. iParamTypes = null;
  39. iArgs = null;
  40. }
  41. public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
  42. super();
  43. iMethodName = methodName;
  44. iParamTypes = paramTypes;
  45. iArgs = args;
  46. }
  47. public Object transform(Object input) {
  48. if (input == null) {
  49. return null;
  50. }
  51. try {
  52. Class cls = input.getClass();
  53. Method method = cls.getMethod(iMethodName, iParamTypes);
  54. return method.invoke(input, iArgs);
  55. } catch (NoSuchMethodException ex) {
  56. throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
  57. } catch (IllegalAccessException ex) {
  58. throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
  59. } catch (InvocationTargetException ex) {
  60. throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
  61. }
  62. }
  63. private void writeObject(ObjectOutputStream os) throws IOException {
  64. FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class);
  65. os.defaultWriteObject();
  66. }
  67. private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException {
  68. FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class);
  69. is.defaultReadObject();
  70. }
  71. }

然后,我们去实现这个接口,只需要传入方法名、参数类型和参数,即可调用任意函数。在这里,我们可以看到,先用ConstantTransformer() 获取了 Runtime 类,接着通过反射调用 getRuntime 函数,再调用 getRuntime 的 exec() 函数,执行命令。依次调用关系为: Runtime --> getRuntime --> exec()

  1. import org.apache.commons.collections.Transformer;
  2. import org.apache.commons.collections.functors.ChainedTransformer;
  3. import org.apache.commons.collections.functors.ConstantTransformer;
  4. import org.apache.commons.collections.functors.InvokerTransformer;
  5. public class main2 {
  6. public static void main(String[] args) {
  7. Transformer[] transformers=new Transformer[]{
  8. new ConstantTransformer(Runtime.class), //获得Runtime.class字节码文件
  9. //通过反射,返回一个对象
  10. new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]}),
  11. new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),
  12. new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"})
  13. };
  14. //ChainedTransformer为链式的Transformer,会挨个执行我们定义Transformer
  15. Transformer transformedChain=new ChainedTransformer(transformers);
  16. Map innerMap=new HashMap();
  17. innerMap.put("value","value");
  18. Map outerMap = TransformedMap.decorate(innerMap, null, transformedChain);
  19. Map.Entry entry=(Entry)outerMap.entrySet().iterator().next();
  20. entry.setValue("test"); //修改值,触发执行
  21. }
  22. }

可以看到,当Map中的Value值被修改了,触发执行了Transformer的transform()方法,通过反射链调用了Runtime.getRuntime.exec("calc.exe") 命令

Java中的反射机制Reflection的更多相关文章

  1. 浅说Java中的反射机制(一)

    在学习传智播客李勇老师的JDBC系列时,会出现反射的概念,由于又是第一次见,不免感到陌生.所以再次在博客园找到一篇文章,先记录如下: 引用自java中的反射机制,作者bingoideas.(()为我手 ...

  2. java 中利用反射机制获取和设置实体类的属性值

    摘要: 在java编程中,我们经常不知道传入自己方法中的实体类中到底有哪些方法,或者,我们需要根据用户传入的不同的属性来给对象设置不同的属性值,那么,java自带的反射机制可以很方便的达到这种目的,同 ...

  3. java中的反射机制在Android开发中的用处

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反 ...

  4. 浅说Java中的反射机制(二)

    写过一篇Java中的反射机制,不算是写,应该是抄了,因为那是别人写的,这一篇也是别人写的,摘抄如下: 引自于Java基础--反射机制的知识点梳理,作者醉眼识朦胧.(()为我手记) 什么是反射? 正常编 ...

  5. 【Java基础】java中的反射机制与动态代理

    一.java中的反射机制 java反射的官方定义:在运行状态下,可以获取任意一个类的所有属性和方法,并且可通过某类任意一对象实例调用该类的所有方法.这种动态获取类的信息及动态调用类中方法的功能称为ja ...

  6. Java中的反射机制(一)

    基本概念 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法? 答案是肯定的. 这种动态获取类的信息以及动态调用对象的方法的功能来自于J ...

  7. Java 中的反射机制

    JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为ja ...

  8. 深入理解Java中的反射机制

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...

  9. Java中的反射机制和动态代理

    一.反射概述 反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法:对于任意一个对象 ...

随机推荐

  1. Java程序员必备后台前端框架--Layui【从入门到实战】(二)

    layui使用 导航菜单.选项卡 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] [编程工具:IDEA] 导航菜单 在layui官网中示例中,我们可以找到关于导航的例子: 我们点击查 ...

  2. C++图论算法——图的储存方式

    使用二维数组邻接矩阵储存图 无向图: 图G 定义图G[101][101],G[i][j]的值表示从结点vi到vj是否有边或弧,若有,取值为1或权值,若无,则取值为0或∞.以下是图G用邻接矩阵表示的列表 ...

  3. 掌握HTTP原理

    URI和URL URI的全程为Uniform Resource identifier,即统一资源标志符,URL的全称 Universal Resource Locator 即统一资源定位符 在目前的互 ...

  4. centos /bin /sbin /usr/bin /usr/sbin 目录的说明

    在linux下我们经常用到的四个应用程序的目录是/bin./sbin./usr/bin./usr/sbin .而四者存放的文件一般如下:    bin目录:  bin为binary的简写主要放置一些系 ...

  5. Java8的新特性--Optional

    目录 Optional 一.Optional类是什么? 二.Optional类常用的方法 1. 创建Optional实例 1.1 Optional.of(T) 1.2 Optional.empty() ...

  6. python3 多线程爬虫模板

    原文:https://www.jianshu.com/p/06ae2373f560 1 import threading # 多线程模块 2 import queue # 队列模块 3 import ...

  7. 半监督学习方法(Semi-supervised Learning)的分类

    根据模型的训练策略划分: 直推式学习(Transductive Semi-supervised Learning) 无标记数据就是最终要用来测试的数据,学习的目的就是在这些数据上取得最佳泛化能力. 归 ...

  8. python那些需要知道的事儿——逻辑运算与比大小

    一.逻辑运算 逻辑运算符: and   or   not,结果为布尔值(True和False) 1.基本逻辑运算符介绍 not :将后面的逻辑运算结果取反 >>> not 1 < ...

  9. maven 打包和构建的Linux命令(mvn)

    maven 打包构建相关命令 命令 mvn clean package 依次执行clean.resources.compile.testResources.testCompile.test.jar(打 ...

  10. CVPR2021| 继SE,CBAM后的一种新的注意力机制Coordinate Attention

    前言: 最近几年,注意力机制用来提升模型性能有比较好的表现,大家都用得很舒服.本文将介绍一种新提出的坐标注意力机制,这种机制解决了SE,CBAM上存在的一些问题,产生了更好的效果,而使用与SE,CBA ...