理解Java反射机制

  • 1. 概述
  • 2. 反射原理
  • 3. 反射的优缺点
  • 4. 反射的用途
  • 5. 反射相关的类
    • 5.1 Constructor
    • 5.2 Field
    • 5.3 Method
    • 5.4 Class类的原理
  • 6. 反射实例
    • 6.1 创建对象
    • 6.2 获取/修改属性
    • 6.3 调用方法
    • 6.4 调用内部类
  • 7. 小节

对于Java使用者来说,反射机制可以说是不得不了解的重要技能之一

1. 概述

JAVA反射机制,可在运行态直接操作任意类或对象的所有属性和方法,主要有以下几个功能:

  • 在运行时获取任意对象所属的类
  • 在运行时构造类的实例对象
  • 在运行时获取或修改类/成员的属性
  • 在运行时调用某个类/对象的方法
  • 另外还可获取类的其他信息,比如描述修饰符、父类信息等

针对动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。反射机制在运行时只能调用methods或改变fields内容,却无法修改程序结构或变量类型。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。

什么是反射?

  1. Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

  2. Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁

2. 反射原理

下图是类的正常加载过程、反射原理与class对象:

Class对象的由来是将.class文件读入内存,并为之创建一个Class对象。

java类的执行需要经历以下过程,

  • 编译:.java文件编译后生成.class字节码文件

  • 加载:类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的java.lang.Class对象实例

  • 连接:细分三步

    • 验证:格式(class文件规范) 语义(final类是否有子类) 操作
    • 准备:静态变量赋初值和内存空间,final修饰的内存空间直接赋原值,此处不是用户指定的初值。
    • 解析:符号引用转化为直接引用,分配地址
  • 初始化:有父类先初始化父类,然后初始化自己;将static修饰代码执行一遍,如果是静态变量,则用用户指定值覆盖原有初值;如果是代码块,则执行一遍操作。

Java的反射就是利用上面第二步加载到jvm中的.class文件来进行操作的。.class文件中包含java类的所有信息,当你不知道某个类具体信息时,可以使用反射获取class,然后进行各种操作。

Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。总结说:反射就是把java类中的各种成分映射成一个个的Java对象,并且可以进行操作。

3. 反射的优缺点

  • 优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

  • 缺点:

    (1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;

    (2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

4. 反射的用途

  • 反编译:.class–>.java

  • 通过反射机制访问java对象的属性,方法,构造方法等

  • 当我们在使用IDE,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。

  • 反射最重要的用途就是开发各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。

  • 比如,加载数据库驱动的,用到的也是反射。

    Class.forName("com.mysql.jdbc.Driver"); // 动态加载mysql驱动
    

5. 反射相关的类

用于操作反射相关的主要有以下5个类:

  • java.lang.Class: 代表类
  • java.lang.reflect.Constructor: 代表类的构造方法
  • java.lang.reflect.Field: 代表类的属性
  • java.lang.reflect.Method: 代表类的方法
  • java.lang.reflect.Modifier:代表类、方法、属性的描述修饰符。

Constructor、Field、Method这三个类都继承AccessibleObject,该对象有一个非常重要的方法setAccessible(boolean flag),用于保证反射可调用非Public的属性与方法。Modifier是指描述修饰符,包含如下范围: public, protected, private, abstract, static, final, transient, volatile, synchronized, native, strictfp, interface。

5.1 Constructor

通过java.lang.reflect.Constructor来操作类的构造方法

方法 含义
getConstructors() 获得类的所有public构造方法
getDeclaredConstructors() 获得类的所有构造方法
getConstructor(Class[] parameterTypes) 获得类的特定public构造方法
getDeclaredConstructor(Class[] params) 获取类的特定构造方法

5.2 Field

通过java.lang.reflect.Field来获取和修改成员属性,其中getField和getDeclaredField的核心区别就是是否指定类型为public

方法 含义
getFields() 获得类的所有public属性
getDeclaredFields() 获得类的所有属性
getField(String name) 获得类的特定public属性
getDeclaredField(String name) 获取类的特定属性

5.3 Method

通过java.lang.reflect.Method来执行成员方法

方法 含义
getMethods() 获得类的所有public成员方法
getDeclaredMethods() 获得类的所有成员方法
getMethod(String name, Class[] parameterTypes) 获得类的特定public成员方法
getDeclaredMethod(String name, Class[] parameterTypes) 获取类的特定成员方法

5.4 Class类的原理

  Java所有的类都是继承于类Object,其内声明了多个应该被所有Java类覆写的方法:hashCode()、equals()、clone()、toString()、notify()、wait()、getClass()等,其中getClass返回的便是一个Class类的对象。Class类也同样是继承Object类,拥有相应的方法。

  Java程序在运行时,运行时系统对每一个对象都有一项类型标识,用于记录对象所属的类。虚拟机使用运行时类型来选择相应方法去执行,保存所有对象类型信息的类便是Class类。Class类没有公共构造方法,Class对象是在加载类时由 Java 虚拟机以及通过调用ClassLoader的defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

  虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类型都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。

  基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。 每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。

6. 反射实例

对于正常方式来调用方法,往往只需要一行到两行代码,即可完成相应工作。而反射则显得比较繁琐,之所以繁琐仍会才用反射方式,是因为反射能干很多正常实例化对象的方式所无法做到的事。比如操作那些private的类、方法、属性,以及@hide标记过的类、方法、属性。

为了到达即能有反射的功效,同时调用方法简单易用,建议大家自己封装一个反射工具类ReflectUtils。(注:以下实例为了代码精简,忽略Exception以及异常处理逻辑。)

6.1 创建对象

//根据类名来获取类
Class clazz = Class.forName("java.lang.String");
//根据对象来获取类
Class clazz = object.getClass();
//根据类来实例化对象
Object obj = clazz.newInstance();//获取无参的构造函数
Constructor c = clazz.getConstructor(null);
//获取参数为String,int的构造函数
Constructor c = clazz.getConstructor(String.class, int.class);
//用于调用私有构造方法
c.setAccessible(true);
Object obj = c.newInstance("amlogic", 2021);

6.2 获取/修改属性

  1. 获取对象的属性:

    public static Object getField(Object object, String fieldName) {Class clazz = object.getClass();Field field = clazz.getDeclaredField(fieldName);field.setAccessible(true);return field.get(object);
    }
    
  2. 修改对象的属性:

    public static boolean setField(Object object, String fieldName, Object fieldValue) {Class clazz = object.getClass();Field field = clazz.getDeclaredField(fieldName);field.setAccessible(true);return field.set(object, fieldValue);
    }
    
  3. 获取类的静态属性:

    public static Object getField(Class clazz, String fieldName) {Field field = clazz.getDeclaredField(fieldName);field.setAccessible(true);return field.get(null);
    }
    
  4. 修改类的静态属性:

    public static boolean setField(Class clazz, String fieldName, Object fieldValue) {Field field = clazz.getDeclaredField(fieldName);field.setAccessible(true);return field.set(null, fieldValue);
    }
    

6.3 调用方法

  1. 调用对象方法

    public static Object invokeMethod(Object object, String methodName, Class[] argsType, Object... args) {Class clazz = object.getClass();Method method = clazz.getDeclaredMethod(methodName, argsType);return method.invoke(object, args);
    }
    
  2. 调用类的静态方法

    public static Object invokeMethod(Class clazz, String methodName, Class[] argsType, Object... args) {Method method = clazz.getDeclaredMethod(methodName, argsType);method.setAccessible(true);  return method.invoke(null, args);
    }
    

6.4 调用内部类

  假设com.reflect.Outer类,有一个内部类inner和静态内部类StaticInner。 那么静态内部类的构造函数为Outer$StaticInner();而普通内部类的构造函数为Outer$Inner(Outer outer),多了一个final的Outer类型属性,即Outer$Inner.this$0,用于存储外部类的属性值,也就是说非static内部类保持了外部类的引用。

直接实例化内部类方法如下:

// 静态内部类
Outer.StaticInner sInner = new Outer.StaticInner();
// 非静态内部类
Outer.Inner inner = new Outer().new Inner();

内部类的类名使用采用$符号,来连接外部类与内部类,格式为outer$Inner

    String className = "com.reflect.Outer$Inner";Class.forName(className);

除了格式了差异,关于内部类的属性和方法操作基本相似,下面以调用该静态类的静态方法为例

public static Object invokeMethod(String methodName, Class[] argsType, Object... args) {Class clazz = Class.forName(“com.reflect.Outer$StaticInner");Method method = clazz.getDeclaredMethod(methodName, argsType);method.setAccessible(true);  return method.invoke(null, args);
}

7. 小节

  反射机制为解耦合提供了保障机制,也为在运行时动态修改属性和调用方法提供的可能性。在Android的源码中,我们会发现有很多被”@hide”标记的类,它的作用是使这个类或方法在生成SDK时不可见。那么应用程序便不可以直接调用。而反射机制可调用@hide标记的类或方法,如入无人之地,畅通无阻。不过从Android P开始就不允许调用@hide方法,会在虚拟机层面拦截直接抛出异常。

理解Java反射机制相关推荐

  1. 秒懂系列,深入理解Java反射机制

    文章目录 深入理解Java反射机制 一.Java反射概述 二.Java创建对象思想传递 三.类对象 3.1 类对象和类的对象 3.2 三种获取类对象的方法 四.Class对象的常用方法 4.1 获取成 ...

  2. 深入理解java反射机制

    一,java的核心机制 java有两种核心机制:java虚拟机(JavaVirtual Machine)与垃圾收集机制(Garbage collection): Java虚拟机:是运行所有Java程序 ...

  3. 【重铸Java根基】理解Java反射机制

    最近带应届新员工,教然后知不足,发现自己把很多基础知识已经还给了大学老师,因此开贴,温故而知新! 从最基础的Java知识开始由浅入深,在某个知识点中遇到有疑惑的点会额外多写几句或者单独开帖子展开. 本 ...

  4. java反射机制+继承设计技巧

    [0]README 0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java反射机制 :最后还顺带提出了 继承设计的技巧: [1]反射相关 1)反射定义:能够分析 ...

  5. Java反射机制的理解和在项目中的应用场景

    Java反射机制的理解 Java程序中的对象在运行时可以表现为两种类型,即编译时类型和运行时类型.例如 Person p = new Student(); ,这行代码将会生成一个p变量,该变量的编译时 ...

  6. 利用java反射机制 读取配置文件 实现动态类载入以及动态类型转换

    作者:54dabang 在spring的学习过程之中,我们能够看出通过配置文件来动态管理bean对象的优点(松耦合 能够让零散部分组成一个总体,而这些总体并不在意之间彼此的细节,从而达到了真正的物理上 ...

  7. 工作中用到的java反射机制_(转)JAVA-反射机制的使用

    Java反射机制的实现原理 反射机制:所谓的反射机制就是java语言在运行时拥有一项自观的能力.通过这种能力可以彻底的了解自身的情况为下一步的动作做准备.下面具体介绍一下java的反射机制.这里你将颠 ...

  8. (转)个例子让你了解Java反射机制

    个例子让你了解Java反射机制 原文地址:http://blog.csdn.net/ljphhj/article/details/12858767 JAVA反射机制:   通俗地说,反射机制就是可以把 ...

  9. 关于java反射机制(基础篇)

    关于java的反射机制,有一些初步的理解,但是依然存在几点困惑.先将自己理解的部分分享其下,并提出困惑,之后完善. 1. java反射机制是什么? 从两个概念切入:反射机制和java反射机制 ①反射机 ...

最新文章

  1. 性能测试八:jmeter进阶之beanshell
  2. KVM(1)- 安装
  3. Maven 常见问题
  4. java容器类3:set/HastSet/MapSet深入解读
  5. 配置Mysql实现主从复制与读写分离
  6. wget 地址 连接超时_HttoPool连接池 和Hystrix 服务容错机制
  7. Xtrabackup备份与恢复
  8. Qt:Qt实现飞秋拦截助手—介绍
  9. nasa 开源_NASA公开数据,白宫新员工以及更多开源新闻
  10. 中国恒大拟出售恒大汽车2.66%股权 配股金额约106亿港元
  11. linux交换区使用过多导致的性能问题
  12. 海域网域名解析服务器,域名系统dns实现的映射是
  13. 安全培训教程之------利用IIS写权限漏洞
  14. html实现“设为首页”加入收藏”代码
  15. Django模型层(2)
  16. 俄罗斯方块游戏51单片机实现
  17. 不是python中文件操作的相关函数是_以下选项中,不是Python中文件操作的相关函数是:...
  18. 用webBrowser打开网页出现脚本错误怎么办?
  19. python怎样计算增长率_Python令人难以置信的增长
  20. 基于Matlab的数字水印设计实现

热门文章

  1. 断点调试(Debug)
  2. 使用ffmpeg解决网页端 视频不能边加载边播放的问题
  3. 游戏多开的分析与实现
  4. 图像处理21-计算轮廓周长或曲线长度
  5. 数据结构基本概念——图的存储结构和遍历算法
  6. 《现代交通报·信息周刊》面向全国招商
  7. Linux命令之创建新目录mkdir
  8. matplotlib matplotlib中axvline()和axhline()函数
  9. Excel VBA自动生成本年度节假日及补假
  10. 智慧树源码_智慧树平台系统开发