理解Java反射机制

article/2025/10/30 16:06:04

理解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方法,会在虚拟机层面拦截直接抛出异常。


http://chatgpt.dhexx.cn/article/UZ0PdIf4.shtml

相关文章

Java 反射机制快速入门及常见方法全归纳。

目录 一、反射机制 1、基本介绍 2、原理示意图 3、反射基本代码实现 4、反射性能 二、Class 类 1、基本介绍 2、获取 Class类对象的方式 3、有 Class对象的类 三、类加载 1、基本介绍 2、连接阶段 四、常见方法取类的结构信息 1、常用类的方法 2、通过反射创建对…

利用java反射机制修改属性值

利用java反射机制修改属性值 本文将介绍如何利用java反射机制修改属性值,废话不多说,直接上代码 1、新建一个实体类,添加属性test以及获取属性值的方法getTest() public class ReflectTest {//测试修改属性String test "修改前的值&q…

Java反射机制的基本认识

关于Java类加载 编译:java文件编译后生成class字节码文件类加载机制:JVM把class文件加载到内存,并对数据进行校验、准备、解析、初始化,最终形成JVM可以直接使用的Java类型的过程。 关于Java反射 Java反射机制是在运行状态中&…

Java基础篇:反射机制详解

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

Java 反射机制与动态代理

1.什么是反射机制? Java反射机制是在运行状态中,对任意一个类(class文件)都能知道这个类的属性和方法、对于任意一个对象都能调用它的属性和方法,这种动态获取的信息以及调用对象的方法的功能称为Java语言的反射机制。…

java反射机制面试_java反射机制面试题及答案整理,java反射面试题

大家对于java反射机制应该都是很了解的吧,那么下面要给大家分享的就是一组和java反射机制有关的java面试题,下面一起来看看java反射面试题都有哪些吧! java反射机制面试题: 1、java反射机制的作用是什么? 答案:在运行的时候构造任意一个类的…

Java反射机制(简单易懂)

文章目录 前言一、反射机制二、反射的用途三、反射相关的类(重要)3.1 Class类(反射机制的起源 )3.2 通过反射调用构造方法3.3 通过反射调用普通方法3.4 通过反射调用类属性 四、反射的优缺点总结 前言 博主个人社区:开发与算法学习社区 博主个…

Java反射机制详解

文章目录 1.反射1.1 反射的概述为什么需要反射? 1.2 获取Class类对象的三种方式1.2.1 代码示例 1.3 反射获取构造方法并应用1.3.1 Class类获取构造方法对象的方法1.3.2 代码示例1.3.3 Constructor类用于创建对象的方法1.3.4 代码示例1.3.5 小结 1.4 反射获取成员变量…

Java反射机制

Java反射机制 前言一、反射的概述反射的定义反射的作用 反射的应用场合 二、Java反射机制反射机制原理示意图Java Reflection反射使用步骤 三、 Class类基本介绍常用方法获取Class类对象对象实例化的方式通过反射获取类的相关信息 四、类加载基本说明类加载时机类加载过程图类加…

JAVA反射机制及其原理实现

9.1 概念 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;public、protected、private。 OO(面向对象)&#xf…

day17-基础加强(类加载器和反射)

1.类加载器 1.1类加载器【理解】 作用 负责将.class文件(存储的物理文件)加载在到内存中 1.2类加载的过程【理解】 类加载时机 创建类的实例(对象)调用类的类方法访问类或者接口的类变量,或者为该类变量赋值使用反射…

Java-反射机制(超详解)

Java反射机制概述 前言一、Java反射机制概述1. Java Reflection2. 动态语言 vs 静态语言 二、 Class类的理解1. 类的加载过程1.1 初步了解1.2 类的加载过程图解1.3 了解:什么时候会发生类初始化?1.4 类加载器的作用1.5 JVM中不同类型的类的加载器1.6 代码…

Java--反射机制原理、几种Class获取方式及应用场景

目录 📢学习背景🎹一、Java反射机制是什么?🎸1.1 反射原理📣1.2 反射例子 🎵二、Java反射机制中获取Class的三种方式及区别?📀2.1 Class的几种获取方式🔊2.2 代码演示几种…

关于线程和进程的区别

进程 : 一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程 线程: 进程中的一个执行任务(控制单元)&…

从内核角度看Linux 线程和进程的区别

多数人都会讲说线程和进程在内核中是相同的,没有严格地做区分。这样讲是没错了,但对于应用开发者来说,这样讲是有点笼统。本文将从内核角度,分析线程和进程之间的区别,希望能对这一块感兴趣的人提供借鉴意义。 1 数据…

java线程与进程的区别是什么?

关于进程与线程的文章早已是非常多了,本文是对我个人过往学习,理解及应用进程与线程的一个总结。此文内容涉及进程线程的区别,什么是进程,什么是线程?希望对大家有所帮助。 java线程与进程的区别是什么? 进程:是并…

Java面试--线程和进程的区别

面试题:线程和进程的区别是什么(招银网络科技、阿里巴巴面试题) 一、线程和进程的区别是什么? 1、进程是一段正在执行的程序,是资源分配的基本单元,而线程是CPU调度的基本单元。 2、进程间相互独立进程&a…

对线程与进程的区别以及对多线程并发的理解

一、线程与进程的区别 先简单说说线程与进程的概念: (1)进程是指一个内存中运行的应用程序,比如在Windows系统中,一个运行的exe就是一个进程。 (2)线程是指进程中的一个执行流程。 区别&…

线程与进程的区别和处理器的调度

(1)进程的概念(Dijkstra) 进程是可并发执行的程序在某个数据集合上的一次计算活动,也是操作系统进行资源分配和调度的基本单位。 (2)进程与程序的联系与区别 ① 程序是指令的有序集合&#x…

多线程(一)线程和进程的区别

目录 🍓 一,进程线和程的概念🍌二,为什么要有线程(1)首先并发编程成为需求(2)虽然进程也可以并编程,但是线程更轻量(3)那么是不是线程创建越多越好…