一文把Java反射说的明明白白,清清楚楚,记得点赞关注,距离架构师的小目标又进一步

简介: 今天有时间没加班回家来好好写一篇文章,反射是Java里比较高级的概念了,一般在书的后半部分。反射也是写框架的必备技能,反射很重要,现在仍然记得刚毕业的一两年一直没有搞懂反射是什么。今天就讲讲反射,希望这篇文章能帮有同样疑惑的你解开疑团,废话不多说,让我们开始吧。

今天有时间没加班回家来好好写一篇文章,反射是Java里比较高级的概念了,一般在书的后半部分。反射也是写框架的必备技能,反射很重要,现在仍然记得刚毕业的一两年一直没有搞懂反射是什么。今天就讲讲反射,希望这篇文章能帮有同样疑惑的你解开疑团,废话不多说,让我们开始吧。


1、反射的概念


1、概念


反射,指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对任意一个对象,都能调用它的任意一个方法。这种动态获取信息,以及动态调用对象方法的功能,叫做java语言的反射机制。反射很强大,有优点也有缺点。


优点:灵活性高。因为反射属于动态编译,即只有到运行时才动态创建 &获取对象实例。


缺点:执行效率低。

2、获取字节码文件对象的方式


2.1 元数据的概念


元数据(metadata):元数据是指用来描述类的数据,就是class的代码数据。所有的class文件加载到虚拟机之后都会被构建成class对象,class对象描述了一个类都有哪些东西,大家都知道的实现的接口,继承的抽象类,成员变量,类变量,成员方法,类方法,静态方法等,这个class对象就是元数据。


  • Class类:代表一个类。


  • Field类:代表类的成员变量(成员变量也称为类的属性)。


  • Method类:代表类的方法。


  • Constructor类:代表类的构造方法。

 

9a75742764a6480ab06013b57ce10eb5~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg

 

2.2 获取class对象的方式


  • 2.2.1 通过对象获得,因为任何对象都必须和class对象关联


  • 2.2.2 通过类对象直接获得


  • 2.2.3 通过类加载器获得,因为类加载器读取class文件会返回class对象


即将用来反射的对象(随便定义的一个对象,只是为了演示)


package org.pdool.reflect;
/**
* @author 香菜
*/
public class Npc {
  // 静态field
  public static int NPC_TYPE_1 = 1;
  // 私有成员变量
  private int npcType;
  // 共有成员变量
  public String name;
  // 无参构造函数
  public Npc() {
  }
  // 有参构造函数
  public Npc(int npcType, String name) {
      this.npcType = npcType;
      this.name = name;
  }
  public int getNpcType() {
      return npcType;
  }
  public void setNpcType(int npcType) {
      this.npcType = npcType;
  }
  public String getName() {
      return name;
  }
  public void setName(String name) {
      this.name = name;
  }
  // 静态方法
  public static void sayHello(String word){
      System.out.println("hello " + word);
  }
}
复制代码


  • 获取反射class的三种方式


package org.pdool.reflect;
/**
* @author 香菜
*/
public class ClazzTest {
   public static void main(String[] args) {
       //第一种方式获取Class对象
       Npc npc1 = new Npc();//这一new 产生一个Npc对象,一个Class对象。
       Class npcClazz1 = npc1.getClass();//获取Class对象
       System.out.println(npcClazz1.getName());
       //第二种方式获取Class对象
       Class npcClazz2 = Npc.class;
       System.out.println(npcClazz1 == npcClazz2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个
       //第三种方式获取Class对象
       try {
           Class npcClazz3 = Class.forName("org.pdool.reflect.Npc");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
           System.out.println(npcClazz3 == npcClazz2);//判断三种方式是否获取的是同一个Class对象
      } catch (ClassNotFoundException e) {
           e.printStackTrace();
      }
  }
}
复制代码



3、反射如何获取元数据并访问


1、访问权限


反射机制的默认行为受限于Java的访问控制,可通过 setAccessible 绕过控制。


// 设置对象数组可访问标志
static void setAccessible(AccessibleObject[] array, boolean flag)  
复制代码


2、获取方法

8d50891c0e7344008c59d37b19792b70~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg

2.1 访问静态方法


public static void main(String[] args) throws NoSuchMethodException,InvocationTargetException, IllegalAccessException {
       Npc npc = new Npc(1, "妖神·凰女");
       Class npcClazz = Npc.class;
       // 第一个参数是方法名,第二个参数是函数的参数class对象,因为存在重载的可能性,用参数类型区分
       Method sayHello = npcClazz.getMethod("sayHello", String.class);
       sayHello.invoke(npc, "world");
  }
复制代码


2.2 访问类方法


Npc npc = new Npc(1, "妖神·凰女");
      System.out.println(npc.getName());
      Class npcClazz = Npc.class;
      // 第一个参数是方法名,第二个参数是函数的参数class对象,因为存在重载的可能性,用参数类型区分
      Method sayHello = npcClazz.getMethod("setName", String.class);
      sayHello.invoke(npc, "world");
      System.out.println(npc.getName());
复制代码


3、获取字段,读取字段的值


08b0005d4ee84e959c8a9adcd8c13820~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


Npc npc = new Npc(1, "妖神·凰女");
       Class npcClazz = Npc.class;
       // 获取字段,并设置可访问
       Field field = npcClazz.getField("name");
       field.setAccessible(true);
       System.out.println( field.get(npc));
复制代码


4、获取实现的接口

60c2d4097b374cbe9fa2bcab2c9e7866~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg

5、获取构造函数,创建实例

 

a94c131f8b924894a1a4062ff5f8cb7b~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg

Class npcClazz = Npc.class;
               Constructor declaredConstructor = npcClazz.getDeclaredConstructor(int.class,String.class);
       Npc npc = (Npc) declaredConstructor.newInstance(1, "妖神");
       System.out.println(npc.getName());
复制代码


6、获取继承的父类


Class npcClazz = Npc.class;
      Class superclass = npcClazz.getSuperclass();
      System.out.println(superclass.getName());
复制代码

 

7、获取注解


Class npcClazz = Npc.class;
       Annotation[] annotations = npcClazz.getAnnotations();
// 运行时注解
       for (Annotation annotation : annotations) {
           System.out.println(annotation.getClass().getName());
      }
复制代码


4、反射实例


获取到元数据不是最终的目的,我们最终的目的是想在运行时去调用,访问类。说了太多,还是举个例子,大家都知道Spring的IOC,怎么实现的呐?


过程:


1、Spring 在项目启动的时间通过读取xml中配置的bean的路径,


2、然后通过Class.forName 读取class 到类加载器,


3、然后通过反射创建所有的bean实例并保存到容器中,启动容器之后,


4、在项目中可以直接获取bean对象。


我们来大概实现这一过程,因为xml的读取比较麻烦,直接用property来代替了。大家体会一下思想就可以了。


package org.pdool.reflect;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* @author 香菜
*/
public class ClazzTest {
  public static void main(String[] args){
      try {
          Map<String,Object> container = new HashMap<>();
          //1.读取配置
          InputStream in = ClazzTest.class.getResourceAsStream("/beans.properties");
          Properties property = new Properties();
          property.load(in);
          //2.反射创建对象
          Set<Object> keySet = property.keySet();
          for (Object key : keySet) {
              // 2.1 获取类的全路径
              String classStr = (String) property.get(key);
              // 2.2 加载class 到虚拟机
              Class<?> beanClazz = Class.forName(classStr);
              // 2.3 获取缺省的构造函数
              Constructor<?> declaredConstructor = beanClazz.getDeclaredConstructor();
              // 2.4 创建实例
              Object o = declaredConstructor.newInstance();
              container.put((String) key,o);
          }
          // 3.获取实例
          Npc npc = (Npc) container.get("npc");
          System.out.println(npc == null);
      } catch (Exception e) {
          e.printStackTrace();
      }
  }
}
复制代码


5、总结


在使用Java反射机制时,主要步骤包括:


  1. 获取 目标类型的Class对象


  1. 通过 Class 对象分别获取Constructor类对象、Method类对象 或者 Field 类对象


  1. 通过 Constructor类对象、Method类对象 & Field类对象分别获取类的构造函数、方法&属性的具体信息,并进行后续操作。
目录
相关文章
|
2月前
|
消息中间件 Java Kafka
Java 事件驱动架构设计实战与 Kafka 生态系统组件实操全流程指南
本指南详解Java事件驱动架构与Kafka生态实操,涵盖环境搭建、事件模型定义、生产者与消费者实现、事件测试及高级特性,助你快速构建高可扩展分布式系统。
183 7
|
13天前
|
机器学习/深度学习 数据可视化 网络架构
PINN训练新思路:把初始条件和边界约束嵌入网络架构,解决多目标优化难题
PINNs训练难因多目标优化易失衡。通过设计硬约束网络架构,将初始与边界条件内嵌于模型输出,可自动满足约束,仅需优化方程残差,简化训练过程,提升稳定性与精度,适用于气候、生物医学等高要求仿真场景。
105 4
PINN训练新思路:把初始条件和边界约束嵌入网络架构,解决多目标优化难题
|
5月前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
6月前
|
人工智能 安全 Java
智慧工地源码,Java语言开发,微服务架构,支持分布式和集群部署,多端覆盖
智慧工地是“互联网+建筑工地”的创新模式,基于物联网、移动互联网、BIM、大数据、人工智能等技术,实现对施工现场人员、设备、材料、安全等环节的智能化管理。其解决方案涵盖数据大屏、移动APP和PC管理端,采用高性能Java微服务架构,支持分布式与集群部署,结合Redis、消息队列等技术确保系统稳定高效。通过大数据驱动决策、物联网实时监测预警及AI智能视频监控,消除数据孤岛,提升项目可控性与安全性。智慧工地提供专家级远程管理服务,助力施工质量和安全管理升级,同时依托可扩展平台、多端应用和丰富设备接口,满足多样化需求,推动建筑行业数字化转型。
224 5
|
3月前
|
缓存 Java 数据库
Java 项目分层架构实操指南及长尾关键词优化方案
本指南详解基于Spring Boot与Spring Cloud的Java微服务分层架构,以用户管理系统为例,涵盖技术选型、核心代码实现、服务治理及部署实践,助力掌握现代化Java企业级开发方案。
160 2
|
4月前
|
算法 架构师 Java
Java 开发岗及 java 架构师百度校招历年经典面试题汇总
以下是百度校招Java岗位面试题精选摘要(150字): Java开发岗重点关注集合类、并发和系统设计。HashMap线程安全可通过Collections.synchronizedMap()或ConcurrentHashMap实现,后者采用分段锁提升并发性能。负载均衡算法包括轮询、加权轮询和最少连接数,一致性哈希可均匀分布请求。Redis持久化有RDB(快照恢复快)和AOF(日志更安全)两种方式。架构师岗涉及JMM内存模型、happens-before原则和无锁数据结构(基于CAS)。
125 5
|
3月前
|
缓存 Cloud Native Java
Java 面试微服务架构与云原生技术实操内容及核心考点梳理 Java 面试
本内容涵盖Java面试核心技术实操,包括微服务架构(Spring Cloud Alibaba)、响应式编程(WebFlux)、容器化(Docker+K8s)、函数式编程、多级缓存、分库分表、链路追踪(Skywalking)等大厂高频考点,助你系统提升面试能力。
161 0
|
5月前
|
存储 监控 算法
Java程序员必学:JVM架构完全解读
Java 虚拟机(JVM)是 Java 编程的核心,深入理解其架构对开发者意义重大。本文详细解读 JVM 架构,涵盖类加载器子系统、运行时数据区等核心组件,剖析类加载机制,包括加载阶段、双亲委派模型等内容。阐述内存管理原理,介绍垃圾回收算法与常见回收器,并结合案例讲解调优策略。还分享 JVM 性能瓶颈识别与调优方法,分析 Java 语言特性对性能的影响,给出数据结构选择、I/O 操作及并发同步处理的优化技巧,同时探讨 JVM 安全模型与错误处理机制,助力开发者提升编程能力与程序性能。
Java程序员必学:JVM架构完全解读