Java基础 反射详解

目录

简介     

反射的基本使用

获取 Class 对象的四种方式

基本使用示例

常用方法

生产中的常用方式

获取注解

SpringIoc容器的制作

反射 + 抽象工厂模式

双亲委派

反射缺点


前言-与正文无关

        生活远不止眼前的苦劳与奔波,它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中,我们往往容易陷入工作的漩涡,忘记了停下脚步,感受周围的世界。让我们一起提醒自己,要适时放慢脚步,欣赏生活中的每一道风景,享受与家人朋友的温馨时光,发现那些平凡日子里隐藏的幸福时刻。因为,这些点点滴滴汇聚起来的,才是构成我们丰富多彩生活的本质。希望每个人都能在繁忙的生活中找到自己的快乐之源,不仅仅为了生存而工作,更为了更好的生活而生活。

        送你张美图!希望你开心!

简介     

        像咱们平时大部分时候都是在写业务代码(码农),很少会接触到直接使用反射机制的场景,起码我是很少接触。但是呢首先这个是个面试重点,其实这是个很厉害的东西,正是因为反射,你才能这么轻松地使用各种框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制。   

        反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。反射能够根据类的字节码文件, 反射类的基本信息,类的成员(字段,方法,构造方法)等内容, 动态的创建对象,调用方法的技术 。

反射的基本使用

Java 反射的主要组成部分有4个:

  • Class:任何运行在内存中的所有类都是该 Class 类的实例对象,每个 Class 类对象内部都包含了本来的所有信息
  • Field:描述一个类的属性,内部包含了该属性的所有信息,例如数据类型,属性名,访问修饰符······
  • Constructor:描述一个类的构造方法,内部包含了构造方法的所有信息,例如参数类型,参数名字,访问修饰符······
  • Method:描述一个类的所有方法(包括抽象方法),内部包含了该方法的所有信息,与Constructor类似,不同之处是 Method 拥有返回值类型信息,因为构造方法是没有返回值的。

      

获取 Class 对象的四种方式

如果我们动态获取到这些信息,我们需要依靠 Class 对象。Class 类对象将一个类的方法、变量等信息告诉运行的程序。Java 提供了四种方式获取 Class 对象:

1. 知道具体类的情况下可以使用:

Class alunbarClass = TargetObject.class;

但是我们一般是不知道具体类的,基本都是通过遍历包下面的类来获取 Class 对象,通过此方式获取 Class 对象不会进行初始化

2. 通过 Class.forName()传入类的全路径获取:

Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject");

3. 通过对象实例instance.getClass()获取:

TargetObject o = new TargetObject();
Class alunbarClass2 = o.getClass();

4. 通过类加载器xxxClassLoader.loadClass()传入类路径获取:

通过类加载器获取 Class 对象不会进行初始化,意味着不进行包括初始化等一系列步骤,静态代码块和静态对象不会得到执行

ClassLoader.getSystemClassLoader().loadClass("cn.javaguide.TargetObject");

基本使用示例

创建一个我们要使用反射操作的类 TargetObject

package cn.javaguide;public class TargetObject {private String value;public TargetObject() {value = "JavaGuide";}public void publicMethod(String s) {System.out.println("I love " + s);}private void privateMethod() {System.out.println("value is " + value);}
}

使用反射操作这个类的方法以及参数 

package cn.javaguide;import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class Main {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {/*** 获取 TargetObject 类的 Class 对象并且创建 TargetObject 类实例*/Class<?> targetClass = Class.forName("cn.javaguide.TargetObject");TargetObject targetObject = (TargetObject) targetClass.newInstance();/*** 获取 TargetObject 类中定义的所有方法*/Method[] methods = targetClass.getDeclaredMethods();for (Method method : methods) {System.out.println(method.getName());}/*** 获取指定方法并调用*/Method publicMethod = targetClass.getDeclaredMethod("publicMethod",String.class);publicMethod.invoke(targetObject, "JavaGuide");/*** 获取指定参数并对参数进行修改*/Field field = targetClass.getDeclaredField("value");//为了对类中的参数进行修改我们取消安全检查field.setAccessible(true);field.set(targetObject, "JavaGuide");/*** 调用 private 方法*/Method privateMethod = targetClass.getDeclaredMethod("privateMethod");//为了调用private方法我们取消安全检查privateMethod.setAccessible(true);privateMethod.invoke(targetObject);}
}

输出内容: 

publicMethod
privateMethod
I love JavaGuide
value is JavaGuide

常用方法

Declared修饰的方法:可以获取该类内部包含的所有变量、方法和构造器,但是无法获取继承下来的信息

Declared修饰的方法:可以获取该类中public修饰的变量、方法和构造器,可获取继承下来的信息

获取类中的变量(Field)

  • Field[] getFields():获取类中所有被public修饰的所有变量
  • Field getField(String name):根据变量名获取类中的一个变量,该变量必须被public修饰
  • Field[] getDeclaredFields():获取类中所有的变量,但无法获取继承下来的变量
  • Field getDeclaredField(String name):根据姓名获取类中的某个变量,无法获取继承下来的变量

获取类中的方法(Method)

  • Method[] getMethods():获取类中被public修饰的所有方法

  • Method getMethod(String name, Class...<?> paramTypes):根据名字和参数类型获取对应方法,该方法必须被public修饰

  • Method[] getDeclaredMethods():获取所有方法,但无法获取继承下来的方法

  • Method getDeclaredMethod(String name, Class...<?> paramTypes):根据名字和参数类型获取对应方法,无法获取继承下来的方法

获取类的构造器(Constructor)

  • Constuctor[] getConstructors():获取类中所有被public修饰的构造器
  • Constructor getConstructor(Class...<?> paramTypes):根据参数类型获取类中某个构造器,该构造器必须被public修饰
  • Constructor[] getDeclaredConstructors():获取类中所有构造器
  • Constructor getDeclaredConstructor(class...<?> paramTypes):根据参数类型获取对应的构造器

new 和newInstance() 区别

newInstance():弱类型,效率低,只能调用无参构造

new():强类型,高效率,能调用任何public构造器

生产中的常用方式

举几个我们都知道例子说明他的使用

获取注解

一般你看生产项目内你自定义个注解这个可能用的比较多!

获取注解单独拧了出来,因为它并不是专属于 Class 对象的一种信息,每个变量,方法和构造器都可以被注解修饰,所以在反射中,Field,Constructor 和 Method 类对象都可以调用下面这些方法获取标注在它们之上的注解。

  • Annotation[] getAnnotations():获取该对象上的所有注解
  • Annotation getAnnotation(Class annotaionClass):传入注解类型,获取该对象上的特定一个注解
  • Annotation[] getDeclaredAnnotations():获取该对象上的显式标注的所有注解,无法获取继承下来的注解
  • Annotation getDeclaredAnnotation(Class annotationClass):根据注解类型,获取该对象上的特定一个注解,无法获取继承下来的注解

当然也是有条件的,只有注解的@Retension标注为RUNTIME时,才能够通过反射获取到该注解,@Retension 有3种保存策略:

  • SOURCE:只在**源文件(.java)**中保存,即该注解只会保留在源文件中,编译时编译器会忽略该注解,例如 @Override 注解
  • CLASS:保存在字节码文件(.class)中,注解会随着编译跟随字节码文件中,但是运行时不会对该注解进行解析
  • RUNTIME:一直保存到运行时用得最多的一种保存策略,在运行时可以获取到该注解的所有信息

SpringIoc容器的制作

在 Spring 中,经常会编写一个上下文配置文件applicationContext.xml,里面就是关于bean的配置,程序启动时会读取该 xml 文件,解析出所有的 <bean>标签,并实例化对象放入IOC容器中。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="smallpineapple" class="com.bean.SmallPineapple"><constructor-arg type="java.lang.String" value="小菠萝"/><constructor-arg type="int" value="21"/></bean>
</beans>

当然springboot也是一样的原理,只不过他不用xml而是yuml文件罢了。

在定义好上面的文件后,通过ClassPathXmlApplicationContext加载该配置文件,程序启动时,Spring 会将该配置文件中的所有bean都实例化,放入 IOC 容器中,IOC 容器本质上就是一个工厂,通过该工厂传入 <bean> 标签的id属性获取到对应的实例。

public class Main {public static void main(String[] args) {ApplicationContext ac =new ClassPathXmlApplicationContext("applicationContext.xml");SmallPineapple smallPineapple = (SmallPineapple) ac.getBean("smallpineapple");smallPineapple.getInfo(); // [小菠萝的年龄是:21]}
}

Spring 在实例化对象的过程经过简化之后,可以理解为反射实例化对象的步骤:

  • 获取Class对象的构造器
  • 通过构造器调用 newInstance() 实例化对象

当然 Spring 在实例化对象时,做了非常多额外的操作,才能够让现在的开发足够的便捷且稳定

反射 + 抽象工厂模式

传统的工厂模式,如果需要生产新的子类,需要修改工厂类,在工厂类中增加新的分支

public class MapFactory {public Map<Object, object> produceMap(String name) {if ("HashMap".equals(name)) {return new HashMap<>();} else if ("TreeMap".equals(name)) {return new TreeMap<>();} // ···}
}

反射 + 抽象工厂的核心思想是:

  • 在运行时通过参数传入不同子类的全限定名获取到不同的 Class 对象,调用 newInstance() 方法返回不同的子类。细心的读者会发现提到了子类这个概念,所以反射 + 抽象工厂模式,一般会用于有继承或者接口实现关系。

        例如,在运行时才确定使用哪一种 Map 结构,我们可以利用反射传入某个具体 Map 的全限定名,实例化一个特定的子类。

className 可以指定为 java.util.HashMap,或者 java.util.TreeMap 等等,根据业务场景来定。

public class MapFactory {/*** @param className 类的全限定名*/public Map<Object, Object> produceMap(String className) {Class clazz = Class.forName(className);Map<Object, Object> map = clazz.newInstance();return map;}
}

双亲委派

反射中,还有一个非常重要的类就是 ClassLoader 类,类装载是Class类装载进JVM的,ClassLoader 使用的是双亲委托模型来搜索加载类的,这个模型也就是双亲委派模型。

反射缺点

  • 破坏类的封装性:可以强制访问 private 修饰的信息,所谓的权限在它面前啥也不是一览无余。
  • 性能损耗:反射相比直接实例化对象、调用方法、访问变量,中间需要非常多的检查步骤和解析步骤,JVM无法对它们优化。

        虽然反射具有性能损耗的特点,但是我们不能一概而论,产生了使用反射就会性能下降的思想,反射的慢,需要同时调用上100W次才可能体现出来,在几次、几十次的调用,并不能体现反射的性能低下。所以不要一味地戴有色眼镜看反射,在单次调用反射的过程中,性能损耗可以忽略不计。如果程序的性能要求很高,那么尽量不要使用反射。

------------------------------------------与正文内容无关------------------------------------
 如果觉的文章写对各位读者老爷们有帮助的话,麻烦点赞加关注呗!作者在这拜谢了!

混口饭吃了!如果你需要Java 、Python毕设、商务合作、技术交流、就业指导、技术支持度过试用期。请在关注私信我,本人看到一定马上回复!

这是我全部文章所在目录,看看是否有你需要的,如果遇到觉得不对地方请留言,看到后我会查阅进行改正。

A乐神-CSDN博客

关注在文章左上角,作者信息处。

 

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

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

相关文章

Vim工具使用全攻略:从入门到精通

引言 在软件开发的世界里&#xff0c;Vim不仅仅是一个文本编辑器&#xff0c;它是一个让你的编程效率倍增的神器。然而&#xff0c;对于新手来说&#xff0c;Vim的学习曲线似乎有些陡峭。本文将手把手教你如何从Vim的新手逐渐变为高手&#xff0c;深入理解Vim的操作模式&#…

【C++】拷贝构造函数和赋值运算符重载详解

目录 拷贝构造函数 概念 特征 赋值运算符重载 运算符重载 赋值运算符重载 ​编辑前置和后置重载 ⭐拷贝构造函数 ⭐概念 拷贝构造函数&#xff1a;只有单个形参&#xff0c;该形参是对本类类型对象的引用(一般常用const修饰)&#xff0c;在用已存 在的类类型对象创建新…

不同核函数高斯过程回归算法与不同因子输入情况下对长江流域蒸散发量应用研究_杨梓涵_2023

不同核函数高斯过程回归算法与不同因子输入情况下对长江流域蒸散发量应用研究_杨梓涵_2023 摘要关键词 0 引言1 材料与方法1.1 数据资料1.2 参考作物腾发量( ET0 ) 计算方法1.2.1 FAO&#xff0d;56 Penman&#xff0d;Monteith 模型1.2.2 Hargreaves&#xff0d;Samani 模型1.…

ctfshow web-76

开启环境: c?><?php $anew DirectoryIterator("glob:///*"); foreach($a as $f) {echo($f->__toString(). );} exit(0); ?> cinclude("/flagc.txt");exit(); c?><?php $anew DirectoryIterator("glob:///*"); foreach($a…

WebAssembly002 FFmpegWasmLocalServer项目

项目介绍 https://github.com/incubated-geek-cc/FFmpegWasmLocalServer.git可将音频或视频文件转换为其他可选的多媒体格式&#xff0c;并导出转码的结果 $ bash run.sh FFmpeg App is listening on port 3000!运行效果 相关依赖 Error: Cannot find module ‘express’ …

政安晨:示例演绎Python的函数与获取帮助的方法

调用函数和定义我们自己的函数&#xff0c;并使用Python内置的文档&#xff0c;是成为一位Pythoner的开始。 通过我的上篇文章&#xff0c;相信您已经看过并使用了print和abs等函数。但是Python还有许多其他函数&#xff0c;并且定义自己的函数是Python编程的重要部分。 在本…

花瓣网美女图片爬取

爬虫基础案例01 花瓣网美女图片 网站url&#xff1a;https://huaban.com 图片爬取 import requests import json import os res requests.get(url "https://api.huaban.com/search/file?text%E7%BE%8E%E5%A5%B3&sortall&limit40&page1&positionsear…

FreeRTOS动态 / 静态创建和删除任务

本篇文章记录我学习FreeRTOS的动态 / 静态创建和删除任务的知识。希望我的分享能给你带来不一样的收获&#xff01;文中涉及FreeRTOS创建和删除任务的API函数&#xff0c;建议读者参考以下文章&#xff1a; FreeRTOS任务相关的API函数-CSDN博客 目录 ​编辑 一、FreeRTOS动态创…

离线数仓-数据治理

目录 一、前言 1.1 数据治理概念 1.2 数据治理目标 1.3 数据治理要解决的问题 1.3.1 合规性 元数据合规性 数据质量合规性 数据安全合规性 1.3.2 成本 存储资源成本 计算资源成本 二、数据仓库发展阶段 2.1 初始期 2.2 扩张期 2.3 缓慢发展期 2.4 变革期 三、…

前后端数据校验

前端校验内容 前端开发中的必要校验&#xff0c;可以保证用户输入的数据的准确性、合法性和安全性。同时&#xff0c;这些校验也有助于提供良好的用户体验和防止不必要的错误提交到后端。 1、必填字段校验&#xff1a; 对于必填的字段&#xff0c;需确保用户输入了有效的数据…

Node.js版本管理工具之_Volta

Node.js包管理工具之_Volta 文章目录 Node.js包管理工具之_Volta1. 官网1. 官网介绍2. 特点1. 快( Fast)2. 可靠(Reliable)3. 普遍( Universal) 2. 下载与安装1. 下载2. 安装3. 查看 3. 使用1. 查看已安装的工具包2. 安装指定的node版本3.切换项目中使用的版本 1. 官网 1. 官网…

02-Java抽象工厂模式 ( Abstract Factory Pattern )

抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是围绕一个超级工厂创建其他工厂 该超级工厂又称为其他工厂的工厂 在抽象工厂模式中&#xff0c;接口是负责创建一个相关对象的工厂&#xff0c;不需要显式指定它们的类 每个生成的工厂都能按照工厂模式提供对象 …