Java 基础 - 反射(1)

文章目录

  • 引入
    • 类加载过程
    • 1. 通过 new 创建对象
    • 2. 通过反射创建对象
      • 2.1 触发加载但不初始化
      • 2.2 按需触发初始化
      • 2.3 选择性初始化控制
  • 核心用法
  • 示例
    • 1. 通过无参构造函数创建实例对象
    • 2. 通过有参构造函数创建实例对象
    • 3. 反射通过私有构造函数创建对象, 破坏单例模式
    • 4. 通过反射获得类的public属性值, 演示getField与getDeclaredField两者的区别

引入

当我们刚接触java语言的时候, 我们最常写的代码应该就是初始化某个对象, 然后调用该 对象的方法。 如:

MyClass obj = new MyClass();
obj.doSth();

上面的这种用法的前提是, 我们在写代码的时候已经确定要去创建MyClass类的具体实例对象。
那如果我们想在代码运行的时候才去指定具体对象的类(比如根据传入的参数名称确定创建的类名),普通的硬编码方式将无法实现需求 ;反射登场了。
反射为什么可以实现呢, 这个就要先介绍一下类加载的过程了。

类加载过程

  1. 加载(Loading)
    JVM将类的字节码文件(.class)加载到内存,创建Class对象。
  2. 链接(Linking)
  • 验证:确保字节码符合规范。
  • 准备:为静态变量分配内存并赋予默认值(如int初始化为0)。
  • 解析:将符号引用转换为直接引用。
  1. 初始化(Initialization)
    执行类的静态代码块(static {})和静态变量显式赋值。

1. 通过 new 创建对象

new关键字会直接触发类的完整加载、链接和初始化过程:

  1. 若类未加载:
    - 立即执行加载、链接,完成后强制触发类的初始化(执行static代码块和初始化静态变量)。
  2. 初始化完成后:调用构造函数创建对象。
    示例:
// 第一次使用类时触发初始化
MyClass obj = new MyClass();

特点:

  • 类必须在编译时已知(硬编码依赖)。
  • 初始化在对象创建时必定发生。

2. 通过反射创建对象

通过反射(Class.newInstance()或Constructor.newInstance())创建对象时,允许分阶段控制类的加载过程:

2.1 触发加载但不初始化

使用ClassLoader.loadClass()可加载类但不初始化:

ClassLoader loader = MyClass.class.getClassLoader();
Class<?> clazz = loader.loadClass("MyClass"); // 仅加载和链接,不初始化

此时尚未执行静态代码块或静态变量显式赋值。

2.2 按需触发初始化

在首次需要初始化时才触发(如反射调用newInstance()):

Object obj = clazz.newInstance(); // 触发初始化 → 执行static代码块

2.3 选择性初始化控制

通过Class.forName可指定是否初始化:

public class Main {public static void main(String[] args) throws Exception {// 反射示例:ClassLoader loader = MyClass.class.getClassLoader();// 加载类但不初始化(第三个参数为类加载器)System.out.println("加载类但不初始化1...");Class<?> clazz2 = Class.forName("com.test.galaxy.MyClass", false, loader);// 加载类但不初始化System.out.println("加载类但不初始化2...");Class<?> clazz = loader.loadClass("com.test.galaxy.MyClass"); // 无输出// 触发初始化前,类的静态代码块仍未执行System.out.println("准备创建对象...");Object obj = clazz.newInstance(); // 输出:静态代码块执行!// 加载类同时触发初始化System.out.println("加载类同时触发初始化...");Class<?> clazz1 = Class.forName("com.test.galaxy.MyClass2");}
}
class MyClass {static {System.out.println("静态代码块执行!"); // 初始化触发}
}
class MyClass2 {static {System.out.println("静态代码块2执行!"); // 初始化触发}
}

特点:

  • 类的加载步骤可拆分(加载、链接、初始化分开触发)。
  • 初始化在需要时才发生(如通过newInstance())。
  • 灵活支持运行时动态加载类(例如插件化架构)。

核心用法

反射允许程序在运行时动态获取类的信息并操作类或对象。核心类是 Class,关键操作包括:

  • 动态创建对象(newInstance())
  • 调用方法(method.invoke())
  • 访问/修改字段(field.get()/set())

示例

1. 通过无参构造函数创建实例对象

import java.lang.reflect.Constructor;
public class ReflectionExample1 {public static void main(String[] args) {try {// 1. 获取Class对象(触发类加载,可能初始化)Class<?> clazz = Class.forName("com.test.galaxy.User");// 2. 获取无参构造方法(需处理异常)Constructor<?> constructor = clazz.getDeclaredConstructor();// 3. 调用newInstance()创建实例(无参数)Object instance = constructor.newInstance();System.out.println("实例创建成功:" + instance.getClass());} catch (Exception e) {e.printStackTrace();}}
}
class User {public User() {System.out.println("无参构造函数被调用!");}
}

关键说明

  • Class.forName():动态加载类,默认触发初始化。
  • getDeclaredConstructor():传入空参数类型列表表示获取无参构造方法。
  • 私有构造方法处理:若构造函数是私有(private),需调用 constructor.setAccessible(true) 解除访问限制。

2. 通过有参构造函数创建实例对象

import java.lang.reflect.Constructor;public class ReflectionExample2 {public static void main(String[] args) {try {// 1. 获取Class对象(注意使用全限定类名)Class<?> clazz = Class.forName("com.test.galaxy.User2");// 2. 指定参数类型列表,获取有参构造方法Class<?>[] paramTypes = {String.class, int.class}; // 参数类型顺序严格匹配Constructor<?> constructor = clazz.getDeclaredConstructor(paramTypes);// 3. 传递参数值实例化对象Object[] initArgs = {"张三", 25}; // 参数值顺序与类型列表一致Object instance = constructor.newInstance(initArgs);System.out.println("实例创建成功:" + instance.getClass());} catch (Exception e) {e.printStackTrace();}}
}
class User2 {private String name;private int age;public User2(String name, int age) {this.name = name;this.age = age;System.out.println("有参构造函数被调用!name=" + name + ", age=" + age);}
}

关键说明

  • 参数类型匹配:必须精确指定参数类型(如 int.class 不能写作 Integer.class)。
  • 参数值顺序:传入的参数值顺序需与声明时一致。
  • 可变长参数处理:若构造方法参数为可变长度(如 String…),类型写为 String[].class。

3. 反射通过私有构造函数创建对象, 破坏单例模式

import java.lang.reflect.Constructor;class Singleton {private static Singleton instance;private Singleton() {// 私有构造函数}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
public class ReflectionExample3 {public static void main(String[] args) {try {// 通过正常方式获取单例对象Singleton instance1 = Singleton.getInstance();System.out.println("正常实例:" + instance1);// 方式 1:通过反射创建新实例(直接访问构造函数)Class<Singleton> clazz = Singleton.class;Constructor<Singleton> constructor = clazz.getDeclaredConstructor();constructor.setAccessible(true); // 访问私有构造函数Singleton instance2 = constructor.newInstance();// 方式 2:通过反射多次创建实例(动态控制)for (int i = 0; i < 3; i++) {Constructor<Singleton> ctor = clazz.getDeclaredConstructor();ctor.setAccessible(true);Singleton instance = ctor.newInstance();System.out.println("反射实例 " + (i+1) + ": " + instance);}// 验证两个实例是否相同System.out.println("instance1 == instance2 ? " + (instance1 == instance2));} catch (Exception e) {e.printStackTrace();}}
}

4. 通过反射获得类的public属性值, 演示getField与getDeclaredField两者的区别

  1. getField() 的特点
    • 只能获取 当前类及继承链中声明为 public 的属性;
    • 无法获取非 public 属性;
    • 可以直接访问继承的父类 public 属性。
  2. getDeclaredField() 的特点
    • 能获取 当前类中声明的所有属性(包括 private/protected/public);
    • 无法获取父类声明的属性;
    • 访问非 public 属性需通过 setAccessible(true)。
import java.lang.reflect.Field;class Parent {public String parentPublicField = "Parent-Public";private String parentPrivateField = "Parent-Private";
}
class Child extends Parent {public String childPublicField = "Child-Public";private String childPrivateField = "Child-Private";
}
public class ReflectionExample4 {public static void main(String[] args) {Child child = new Child();Class<?> clazz = Child.class;try {// ======================= 使用 getField() ========================// 1. 获取子类的 public 属性(成功)Field childPublicField = clazz.getField("childPublicField");System.out.println("[getField] 子类 public 属性: " + childPublicField.get(child));// 2. 获取父类的 public 属性(成功)Field parentPublicField = clazz.getField("parentPublicField");System.out.println("[getField] 父类 public 属性: " + parentPublicField.get(child));// 3. 尝试获取子类的 private 属性(失败,触发异常)clazz.getField("childPrivateField");} catch (Exception e) {System.err.println("[getField 失败] " + e.getClass().getSimpleName() + ": " + e.getMessage());}try {// ================== 使用 getDeclaredField() ======================// 1. 获取子类的 public 属性(成功)Field childPublicDeclaredField = clazz.getDeclaredField("childPublicField");System.out.println("[getDeclaredField] 子类 public 属性: " + childPublicDeclaredField.get(child));// 2. 获取子类的 private 属性(需解除访问限制)Field childPrivateDeclaredField = clazz.getDeclaredField("childPrivateField");childPrivateDeclaredField.setAccessible(true);  // 强制访问私有属性System.out.println("[getDeclaredField] 子类 private 属性: " + childPrivateDeclaredField.get(child));// 3. 尝试获取父类的属性(失败,无论是否是 public)clazz.getDeclaredField("parentPublicField");} catch (Exception e) {System.err.println("[getDeclaredField 失败] " + e.getClass().getSimpleName() + ": " + e.getMessage());}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/75289.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在React中集成 PDF.js?构建支持打印下载的PDF阅读器详解

本文深入解析基于 React 和 PDF.js 构建 PDF 查看器的实现方案&#xff0c;该组件支持 PDF 渲染、图片打印和下载功能&#xff0c;并包含完整的加载状态与错误处理机制。 完整代码在最后 一个PDF 文件&#xff1a; https://mozilla.github.io/pdf.js/web/compressed.tracemo…

数据结构与算法-动态规划-线性动态规划,0-1背包,多重背包,完全背包,有依赖的背包,分组背包,背包计数,背包路径

动态规划原理 动态规划这玩意儿&#xff0c;就好比是在拓扑图上玩跳格子游戏。在图论中&#xff0c;咱们是从特定的节点跳到其他节点&#xff1b;而在动态规划里呢&#xff0c;我们是从一个状态 “嗖” 地转移到另一个状态。状态一般用数组来表示&#xff0c;就像 f [i][j]&am…

解决文件夹解压中文字符产生乱码的问题

太tm智能了&#xff0c;本来还想看看解压工具在哪里修改&#xff0c;智能的识别到乱码了。点赞 看到那个地球了吗&#xff0c;点击那个球&#xff0c;这个修改不是侵略性的&#xff0c;不会修改压缩文件本身所以需要在当前页面解压 参考 https://blog.csdn.net/QCSYSZQ/artic…

C++与C的区别

目录 前言 一、从字面上看 二、从编程思想上看 三、C 和 C++ 都有各自适合的领域和特性 四、划重点 前言 本文主要对 C 和 C++ 两种编程语言进行对比区分,便于大家理解 一、从字面上看 1.首先:两者第一个字符完全一致 说明:C++ 完全兼容 C ,凡是合法的 C 程序在 C…

水利水电安全员ABC适合哪些人考?

水利水电安全员证是水利工程建设领域的重要职业资格证书&#xff0c;主要涉及水利水电工程施工安全管理、风险防控和应急处理等工作。那么&#xff0c;哪些人适合考取&#xff1f; 哪些人适合考水利水电安全员&#xff1f; 1. 水利水电工程从业人员 ✅ 施工管理人员&#xf…

Linux中用gdb查看coredump文件

查看dump的命令&#xff1a; gdb 可执行文件 dump文件路径查看函数调用栈 (gdb)bt查看反汇编代码 (gdb)disassemble查看寄存器的值 (gdb)info all-registers如果通过上述简单命令无法排查&#xff0c;还是通过-g参数编译带符号表的可执行文件&#xff0c;再用gdb查看

【前端】【React】useCallback的作用与使用场景总结

一、useCallback 的作用与使用场景总结 useCallback 是 React 提供的一个 Hook&#xff0c;用于缓存函数的引用&#xff0c;避免因为组件重新渲染而导致函数地址发生变化。它返回一个记忆&#xff08;memoized&#xff09;后的回调函数&#xff0c;只有当依赖项发生变化时才会…

蓝桥杯备赛学习笔记:高频考点与真题预测(C++/Java/python版)

2025蓝桥杯备赛学习笔记 ——高频考点与真题预测 一、考察趋势分析 通过对第13-15届蓝桥杯真题的分析&#xff0c;可以发现题目主要围绕基础算法、数据结构、数学问题、字符串处理、编程语言基础展开&#xff0c;且近年逐渐增加动态规划、图论、贪心算法等较难题目。 1. 基…

20250410在荣品的PRO-RK3566开发板使用Rockchip原厂的buildroot系统时自动挂载eth0【直接编译进IMG】

【暂时没有找到第一次编译就可以修改的地方&#xff01;&#xff01;&#xff01;&#xff01;】 rootrootrootroot-X99-Turbo:~/RK3566_RK3568_Linux5.10_V1.2.0$ find . -name interfaces 【完整编译之后&#xff0c;基本确认修改这里有效。】 ./buildroot/output/rockchip_r…

c11新特性,继承构造函数

#include <iostream> #include <string>class Person { public:std::string name;int age;// 主构造函数Person(const std::string& name, int age) : name(name), age(age) {std::cout << "Person created with name: " << name <&l…

【TS学习】(24)什么是装饰器

在 TypeScript 中&#xff0c;装饰器&#xff08;Decorators&#xff09; 是一种特殊的声明&#xff0c;用于为类、类成员&#xff08;属性、方法、访问器&#xff09;、方法参数或整个类添加元数据或修改其行为。装饰器是 JavaScript 和 TypeScript 的实验性特性&#xff0c;广…

datagrip如何连接数据库

datagrip连接数据库的步骤 2025版本 想要链接数据库是需要一个jar包的&#xff0c;所以将上面进行删除之后&#xff0c;需要下载一个jar包 那么这个时候需要链接上传一个mysql链接的jar包 选择核心驱动类 上述操作完成之后&#xff0c;然后点击apply再点击ok即可 如下图说明my…

菊风RTC 2.0 开发者文档正式发布,解锁音视频新体验!

重磅发布&#xff01; 开发者们&#xff0c;菊风实时音视频2.0文档已正式发布上线&#xff0c;为您提供更清晰、更高效的开发支持&#xff01;让菊风实时音视频2.0为您的音视频应用加速~ 菊风实时音视频2.0聚焦性能升级、体验升级、录制服务升级&#xff0c;助力视频通话、语…

轻量级碎片化笔记memos本地NAS部署与跨平台跨网络同步笔记实战

文章目录 前言1. 使用Docker部署memos2. 注册账号与简单操作演示3. 安装cpolar内网穿透4. 创建公网地址5. 创建固定公网地址 推荐 ​ 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。 点击跳转到网站 前言…

【Vue #2】脚手架 指令

一、脚手架 脚手架&#xff1a;一个保证各项工作顺利开展的平台&#xff0c;方便我们 拿来就用&#xff0c;零配置 1. Vue 代码开发方式 相比直接 script 引入 vue 源码&#xff0c;有没有更好的方式编写vue代码呢? ① 传统开发模式&#xff1a; 基于html文件开发Vue&…

ArkTS语言入门之接口、泛型、空安全、特殊运算符等

前言 臭宝们&#xff0c;今天我们来学习ArkTS中最后的一些内容。 实现接口 包含implements子句的类必须实现列出的接口中定义的所有方法&#xff0c;但使用默认实现定义的方法除外。 interface DateInterface {now(): string; } class MyDate implements DateInterface {no…

Maven超级详细安装部署

1.到底什么是Maven&#xff1f;搞清楚这个 Maven 是一个项目管理工具&#xff0c;主要用于 Java 项目的构建、依赖管理和文档生成。 它基于项目对象模型&#xff08;POM&#xff09;&#xff0c;通过 pom.xml 文件定义项目的配置。 &#xff08;简单说破&#xff1a;就是工程…

高并发内存池(三):PageCache(页缓存)的实现

前言&#xff1a; 在前两期内容中&#xff0c;我们深入探讨了内存管理机制中在 ThreadCache 和 CentralCache两个层级进行内存申请的具体实现。这两层缓存作为高效的内存分配策略&#xff0c;能够快速响应线程的内存需求&#xff0c;减少锁竞争&#xff0c;提升程序性能。 本期…

机器学习 | 强化学习方法分类汇总 | 概念向

文章目录 📚Model-Free RL vs Model-Based RL🐇核心定义🐇核心区别📚Policy-Based RL vs Value-Based RL🐇核心定义🐇 核心区别📚Monte-Carlo update vs Temporal-Difference update🐇核心定义🐇核心区别📚On-Policy vs Off-Policy🐇核心定义🐇核心区别…

GSO-YOLO:基于全局稳定性优化的建筑工地目标检测算法解析

论文地址:https://arxiv.org/pdf/2407.00906 1. 论文概述 《GSO-YOLO: Global Stability Optimization YOLO for Construction Site Detection》提出了一种针对建筑工地复杂场景优化的目标检测模型。通过融合全局优化模块(GOM)​、稳定捕捉模块(SCM)​和创新的AIoU损失函…