Java高级: 反射

news2024/11/25 13:46:38

目录

  • 反射
    • 反射概述
    • 反射获取类的字节码
    • 反射获取类的构造器
    • 反射获取构造器的作用
    • 反射获取成员变量&使用
    • 反射获取成员方法
    • 反射获取成员方法的作用
  • 反射的应用案例

接下来我们学习的反射、动态代理、注解等知识点,在以后开发中极少用到,这些技术都是以后学习框架、或者做框架的底层源码。讲这些技术的目的,是为了以后我们理解框架、或者自己开发框架给别人用作铺垫的。同时由于这些技术非常抽象,所以我们都会采用先带着大家充分的认识它们,然后再了解其作用和应用场景。

请添加图片描述

反射

反射概述

其实API文档中对反射有详细的说明,我们去了解一下。在java.lang.reflect包中对反射的解释如下图所示

请添加图片描述

翻译成人话就是:反射技术,指的是加载类的字节码到内存,并以编程的方法解刨出类中的各个成分(成员变量、方法、构造器等)。

反射有啥用呢?其实反射是用来写框架用的,但是现阶段同学们对框架还没有太多感觉。为了方便理解,我给同学们看一个我们见过的例子:平时我们用IDEA开发程序时,用对象调用方法,IDEA会有代码提示,idea会将这个对象能调用的方法都给你列举出来,供你选择,如果下图所示。问题是IDEA怎么知道这个对象有这些方法可以调用呢? 原因是对象能调用的方法全都来自于类,IDEA通过反射技术就可以获取到类中有哪些方法,并且把方法的名称以提示框的形式显示出来,所以你能看到这些提示了。

请添加图片描述

因为反射获取的是类的信息,那么反射的第一步首先获取到类才行。由于Java的设计原则是万物皆对象,获取到的类其实也是以对象的形式体现的,叫字节码对象,用Class类来表示。获取到字节码对象之后,再通过字节码对象就可以获取到类的组成成分了,这些组成成分其实也是对象,其中每一个成员变量用Field类的对象来表示、每一个成员方法用Method类的对象来表示,每一个构造器用Constructor类的对象来表示。

请添加图片描述


反射获取类的字节码

反射的第一步:是将字节码加载到内存,我们需要获取到的字节码对象。

请添加图片描述

比如有一个Student类,获取Student类的字节码代码有三种写法。不管用哪一种方式,获取到的字节码对象其实是同一个。

public class Test1Class{
    public static void main(String[] args){
        Class c1 = Student.class;
        System.out.println(c1.getName()); //获取全类名 com.tx.www.Student
        System.out.println(c1.getSimpleName()); //获取简单类名 Student
        
        Class c2 = Class.forName("com.tx.www.Student");
        System.out.println(c1 == c2); //true
        
        Student s = new Student();
        Class c3 = s.getClass();
        System.out.println(c2 == c3); //true
    }
}

反射获取类的构造器

获取构造器,需要用到Class类提供的几个方法,如下图所示:

请添加图片描述

速记:

get:获取
Declared: 有这个单词表示可以获取任意一个,没有这个单词表示只能获取一个public修饰的
Constructor: 构造方法的意思
后缀s: 表示可以获取多个,没有后缀s只能获取一个

假设现在有一个Cat类,里面有几个构造方法,代码如下

public class Cat{
    private String name;
    private int age;
    
    public Cat(){
        
    }
    
    private Cat(String name, int age){
        
    }
}

接下来,我们写一个测试方法,来测试获取类中所有的构造器

public class Test2Constructor{
    @Test
    public void testGetConstructors(){
        //1、反射第一步:必须先得到这个类的Class对象
        Class c = Cat.class;
        
        //2、获取类的全部构造器
        Constructor[] constructors = c.getDeclaredConstructors();
        //3、遍历数组中的每一个构造器对象。
        for(Constructor constructor: constructors){
            System.out.println(constructor.getName()+"---> 参数个数:"+constructor.getParameterCount());
        }
    }
}

运行测试方法打印结果如下

请添加图片描述

接下来,我们演示获取单个构造器试一试

public class Test2Constructor {
    @Test
    public void testGetConstructor() throws NoSuchMethodException {
        //1、反射第一步:必须先得到这个类的Class对象
        Class c = Cat.class;

        //2、获取类public修饰的空参数构造器
        Constructor constructor1 = c.getConstructor();
        System.out.println(constructor1.getName()+"---> 参数个数:"+constructor1.getParameterCount());

        //3、获取private修饰的有两个参数的构造器,第一个参数String类型,第二个参数int类型
        Constructor constructor2 = c.getDeclaredConstructor(String.class,int.class);

        System.out.println(constructor2.getName()+"---> 参数个数:"+constructor2.getParameterCount());

    }
}

结果如下:

请添加图片描述


反射获取构造器的作用

获取到构造器后,有什么作用呢?
构造器的作用:初始化对象并返回。
这里我们需要用到如下的两个方法,注意:这两个方法时属于Constructor的,需要用Constructor对象来调用。

请添加图片描述

如下图所示,constructor1和constructor2分别表示Cat类中的两个构造器。现在我要把这两个构造器执行起来

请添加图片描述

由于构造器是private修饰的,先需要调用setAccessible(true) 表示禁止检查访问控制,然后再调用newInstance(实参列表) 就可以执行构造器,完成对象的初始化了。
代码如下:为了看到构造器真的执行, 故意在两个构造器中分别加了两个打印语句

请添加图片描述


反射获取成员变量&使用

再学习获取类的成员变量,并使用。其实套路是一样的,在Class类中提供了获取成员变量的方法,如下图所示。

请添加图片描述

速记:

get:获取
Declared: 有这个单词表示可以获取任意一个,没有这个单词表示只能获取一个public修饰的
Field: 成员变量的意思
后缀s: 表示可以获取多个,没有后缀s只能获取一个

假设有一个Cat类它有若干个成员变量,用Class类提供 的方法将成员变量的对象获取出来。

请添加图片描述

行完上面的代码之后,我们可以看到控制台上打印输出了,每一个成员变量的名称和它的类型。

请添加图片描述

获取到成员变量的对象之后该如何使用呢?在Filed类中提供给给成员变量赋值和获取值的方法,如下图所示。

请添加图片描述

强调一下设置值、获取值的方法时Filed类的需要用Filed类的对象来调用,而且不管是设置值、还是获取值,都需要依赖于该变量所属的对象。代码如下

请添加图片描述

执行代码:

请添加图片描述


反射获取成员方法

还剩下最后一个就是反射获取成员方法并使用了。在Java中反射包中,每一个成员方法用Method对象来表示,通过Class类提供的方法可以获取类中的成员方法对象。如下下图所示

请添加图片描述

用代码演示一下:假设有一个Cat类,在Cat类中红有若干个成员方法

public class Cat{
    private String name;
    private int age;
    
    public Cat(){
        System.out.println("空参数构造方法执行了");
    }
    
    private Cat(String name, int age){
        System.out.println("有参数构造方法执行了");
        this.name=name;
        this.age=age;
    }
    
    private void run(){
        System.out.println("(>^ω^<)喵跑得贼快~~");
    }
    
    public void eat(){
        System.out.println("(>^ω^<)喵爱吃猫粮~");
    }
    
    private String eat(String name){
        return "(>^ω^<)喵爱吃:"+name;
    }
    
    public void setName(String name){
        this.name=name;
    }
    public String getName(){
        return name;
    }
    public void setAge(int age){
        this.age=age;
    }
     public int getAge(){
        return age;
    }
}

接下来,通过反射获取Cat类中所有的成员方法,每一个成员方法都是一个Method对象

public class Test3Method{
    public static void main(String[] args){
        //1、反射第一步:先获取到Class对象
        Class c = Cat.class;
        
        //2、获取类中的全部成员方法
        Method[] methods = c.getDeclaredMethods();
        
        //3、遍历这个数组中的每一个方法对象
        for(Method method : methods){
            System.out.println(method.getName()+"-->"+method.getParameterCount()+"-->"+method.getReturnType());
        }
    }
}

执行上面的代码,运行结果如下图所示:打印输出每一个成员方法的名称、参数格式、返回值类型

请添加图片描述

也能获取单个指定的成员方法,如下图所示

请添加图片描述


反射获取成员方法的作用

获取到成员方法之后,有什么作用呢?在Method类中提供了方法,可以将方法自己执行起来。

请添加图片描述

下面我们演示一下,把run()方法和eat(String name)方法执行起来。看分割线之下的代码:

public class Test3Method{
    public static void main(String[] args) throws Exception{
	   	//1、反射第一步:先获取到Class对象
	    Class c = Cat.class;
	
	    //2、获取类中的全部成员方法
	    Method[] methods = c.getDeclaredMethods();
	
	    //3、遍历这个数组中的每一个方法对象
	    for(Method method : methods){
	        System.out.println(method.getName()+"-->"+method.getParameterCount()+"-->"+method.getReturnType());
	    }
	
	    System.out.println("-----------------------");
	    //4、获取private修饰的run方法,得到Method对象
	    Method run = c.getDeclaredMethod("run");
	    //执行run方法,在执行前需要取消权限检查
	    Cat cat = new Cat();
	    run.setAccessible(true);
	    Object rs1 = run.invoke(cat);
	    System.out.println(rs1);// null
	
	    //5、获取private 修饰的eat(String name)方法,得到Method对象
	    Method eat = c.getDeclaredMethod("eat",String.class);
	    eat.setAccessible(true);
	    Object rs2 = eat.invoke(cat,"鱼儿");
	    System.out.println(rs2);//(>^ω^<)喵爱吃:鱼儿
	}
}

打印结果如下图所示:run()方法执行后打印猫跑得贼快~~,返回null; eat()方法执行完,返回猫最爱吃:鱼儿

-----------------------
空参数构造方法执行了
(>^ω^<)喵跑得贼快~~
null
(>^ω^<)喵爱吃:鱼儿



反射的应用案例

我们已经充分认识了什么是反射,以及反射的核心作用是用来获取类的各个组成部分并执行他们。但是由于经验有限,对于反射的具体应用场景还是很难感受到的(这个目前没有太好的办法,只能慢慢积累,等经验积累到一定程度,就会豁然开朗了)。
我们一直说反射使用来写框架的,接下来,我们就写一个简易的框架,简单窥探一下反射的应用。反射其实是非常强大的,这个案例也仅仅是小试牛刀。
需求是让我们写一个框架,能够将任意一个对象的属性名和属性值写到文件中去。不管这个对象有多少个属性,也不管这个对象的属性名是否相同。

请添加图片描述

分析一下该怎么做

1.先写好两个类,一个Student类和Teacher类
2.写一个ObjectFrame类代表框本架
	在ObjectFrame类中定义一个saveObject(Object obj)方法,用于将任意对象存到文件中去
	参数:Object obj: 就表示要存入文件中的对象
3.编写方法内部的代码,往文件中存储对象的属性名和属性值
	(1)每收到一个对象后,使用反射获取该对象的Class对象,然后获取全部的成员变量。
	(2)遍历成员变量,然后提取成员变量在该对象中的具体值。
	(3)把成员变量名、和其值,写出到文件中去即可。

写一个ObjectFrame表示自己设计的框架,代码如下所示

public class ObjectFrame{
    public static void saveObject(Object obj) throws Exception{
        PrintStream ps =
                new PrintStream(new FileOutputStream("src\\data.txt",true));//字节打印流
        //1)每收到一个对象后,使用反射获取该对象的Class对象,然后获取全部的成员变量。
        //2)遍历成员变量,然后提取成员变量在该对象中的具体值。
        Class c = obj.getClass(); //获取字节码
        ps.println("---------"+c.getSimpleName()+"---------");

        Field[] fields = c.getDeclaredFields(); //获取所有成员变量
        //3)把变量名和变量值写到文件中去
        for(Field field : fields){
            String name = field.getName();
            field.setAccessible(true);//禁止访问检查
            String value = field.get(obj)+"";
            ps.println(name +  "=" + value);
        }
        ps.close();
    }
}

使用自己设计的框架,往文件中写入Student对象的信息和Teacher对象的信息。
先准备好Student类和Teacher类

public class Student{
    private String name;
    private int age;
    private char sex;
    private double height;
    private String hobby;
}
public class Teacher{
    private String name;
    private double salary;
}

创建一个测试类,在测试中类创建一个Student对象,创建一个Teacher对象,用ObjectFrame的方法把这两个对象所有的属性名和属性值写到文件中去。

public class Test5Frame{
    @Test
    public void save() throws Exception{
        Student s1 = new Student("吴彦祖",45, '男', 185.3, "篮球,冰球,阅读");
        Teacher s2 = new Teacher("播妞",999.9);

        ObjectFrame.saveObject(s1);
        ObjectFrame.saveObject(s2);
    }
}

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

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

相关文章

李佳琦掉粉,国货品牌却从“商战大剧”走向“情景喜剧”

李佳琦直播间带货怼网友&#xff0c;“哪里贵了&#xff0c;国货很难的”“这么多年工资没涨&#xff0c;有没有认真工作&#xff1f;”本人事后垂泪道歉仍掉粉百万&#xff0c;但是闻风而来的国货品牌却迎来了一场流量盛宴。 从蜂花蹲点“捡”粉丝&#xff0c;上架三款79元洗…

多元函数的偏导数

目录 偏导数的定义 二元函数偏导数的几何意义 高阶偏导数 全微分 偏导数的定义 偏导数是一种特殊的数学概念&#xff0c;它是针对一个多变量的函数在某个自变量上的导数。 具体来说&#xff0c;对于一个有多个自变量的函数yf(x0, x1, xj, ..., xn)&#xff0c;在自变量xk固…

TCP特性的滑动窗口,流量控制

目录 一、TCP特性滑动窗口 二、TCP特性流量控制&#xff08;作为滑动窗口的补充&#xff09; 一、TCP特性滑动窗口 提高传输效率&#xff08;更准确的说&#xff0c;让TCP在可靠传输的前提下&#xff0c;效率不太拉跨&#xff09;&#x1f49b; 当然你要是想让TCP媲美UDP&…

清水模板是什么材质?

清水模板是建筑施工中常用的一种模板&#xff0c;用于浇筑混凝土结构的形成和支撑。它是指没有进行任何装饰和涂层处理的模板&#xff0c;通常由木材制成&#xff0c;如胶合板、钢模板等。下面是关于清水模板的详细介绍。 清水模板的材质多样&#xff0c;其中最常见的是胶合板。…

ASEMI二极管1N4148(T4)的用途和使用建议

编辑-Z 二极管是一种常见的电子元件&#xff0c;其中1N4148&#xff08;T4&#xff09;是一款广泛使用的快恢复二极管。它具有快速的开关特性和高反向阻挡能力&#xff0c;适用于多种电子应用。本文将介绍1N4148&#xff08;T4&#xff09;的特点、用途和如何正确使用该二极管…

《PostgreSQL中的JSON处理:技巧与应用》

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f405;&#x1f43e;猫头虎建议程序员必备技术栈一览表&#x1f4d6;&#xff1a; &#x1f6e0;️ 全栈技术 Full Stack: &#x1f4da…

centos7更新podman到最新版

实验环境&#xff1a;centos7.7.1908 1.安装podman并查看版本 $ yum install podman -y $ podman -v [rootd7cb4574cd89 /]# podman -v podman version 1.6.4 centos7默认安装的podman版本是1.6.4&#xff0c;现在我们要把podman升级到最新版。 2.删除现有podman $ yum remo…

C语言编程题(三)整型和浮点型混合运算

C语言——整型和浮点型混合运算_int与float的混合计算__好好学习的博客-CSDN博客 请写出165.25(10进制)使用float型存储在计算机中的形式。 在计算机中&#xff0c;浮点数使用IEEE 754标准来表示。根据IEEE 754标准&#xff0c;32位的单精度浮点数&#xff08;float类型&#…

软件流程图怎么画?详细画法看这里

软件流程图怎么画&#xff1f;软件流程图是软件开发过程中必不可少的一环&#xff0c;可以帮助开发人员更好地理解和规划软件开发的流程。在制作软件流程图的时候&#xff0c;我们可以使用一些制作工具。下面就给大家介绍一款好用的绘制工具。 我们可以使用【迅捷画图】来进行流…

28.Xaml ContexMenu控件---->右键菜单

1.运行效果 2.运行源码 a.Xaml源码 <Window x:Class="testView.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.mic…

anaconda,cuda,torch,lightning的安装

本博客仅作为初学者参考使用&#xff0c;汇总了多位大牛的博客&#xff0c;如有侵权请联系我删除 anaconda,cuda,torch,lightning的安装 1、Anaconda2、cuda3、pytorch4、lightning5、解决pip执行后导致C盘空间变小问题 1、Anaconda 作用&#xff1a; 1、可创建python包的虚拟…

RabbitMQ —— 初窥门径

前言 RabbitMQ作为当下主流的消息中间件之一&#xff0c;无疑是我们Java后端开发技术成长路线的重要一环&#xff0c;在这篇文章中荔枝将会梳理入门RabbitMQ的知识&#xff0c;文章涉及RabbitMQ的基本概念及其环境配置&#xff0c;荔枝的RabbitMQ是在Docker上部署的&#xff0c…

基于Gradio/Stable Diffusion/Midjourney的AIGC自动图像绘画生成软件 - Fooocus

0.参考 本项目&#xff1a;GitHub - lllyasviel/Fooocus: Focus on prompting and generating 作者&#xff1a;Lvmin Zhang lllyasviel 另一杰作 ContorlNet https://github.com/lllyasviel/ControlNet 模型&#xff1a;https://huggingface.co/stabilityai/stable-diffus…

基于人体呼出气体的电子鼻系统的设计与实现

基于人体呼出气体的电子鼻系统的设计与实现 摘要 电子鼻技术是通过模式识别技术对传感器采集的人体呼出气体进行分类训练的方法。本文研究实现的电子鼻系统包括下面几个部分:首先搭建以Arduino为控制核心的气路采集装置&#xff0c;包括MOS传感器和双阀储气袋构建的传感器阵列和…

探索数据结构:从基础到高级

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 数据结构是计算机科学和…

MiniMeters for Mac - 独立音频计量软件,创意音乐的最佳伙伴

MiniMeters for Mac是一款专为Mac用户设计的音频计量软件&#xff0c;它提供了一套功能强大、直观易用的工具&#xff0c;帮助你更好地理解和处理音频。这款软件不仅具备高度的专业性&#xff0c;同时也极具创新性&#xff0c;它的出现将彻底改变你对音频处理的认知。 .安装&a…

macOS 中 聚焦搜索 的使用教程

macOS中的聚焦搜索是一个强大的工具&#xff0c;它可以帮助你快速找到文件、应用程序、联系人、电子邮件、互联网搜索结果等。 下面是macOS中聚焦搜索的使用教程&#xff1a; 1.打开聚焦搜索&#xff1a; 使用键盘快捷键&#xff1a;按下键盘上的Command键和空格键&#xff0…

线性表(顺序表、链表、栈、队列)总结梳理

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

提高项目团队执行力的6大注意事项

项目执行力的强弱直接影响项目的进度和时间安排&#xff0c;项目执行力高的团队通常能够更好地分配任务、协同合作和解决问题。这可以大幅提高工作效率&#xff0c;避免重复劳动和资源浪费。而执行力低的项目团队&#xff0c;往往难以按时完成任务无法及时发现和应对风险&#…

无涯教程-JavaScript - TYPE函数

描述 TYPE函数接收一个值,并返回一个表示指定值的数据类型的整数。当另一个函数的行为取决于特定单元格中值的类型时,请使用TYPE。 语法 TYPE (value) 争论 Argument描述Required/OptionalValue 值或对包含您想知道其类型的值的单元格的引用。 数据类型可以是数字,文本,逻辑…