学完Spring框架回头再来看反射你会发现真的不一样

news2025/8/9 17:08:13

文章目录

  • 前言
    • 一.什么是反射?
    • 二.如何实现反射?
      • 2.1java.lang.Class
      • 2.2通过反射创建对象
      • 2.3通过反射获取类成员
    • 三.反射的性能
    • 四.反射是如何破坏单例模式的?
    • 五.如何避免单例模式被反射破坏?

前言

你还记得你的Spring入门案例吗?
在这里插入图片描述
字符串经过解析得到了对象并放入到了Bean中,然后,从IOC里按照id拿对象(getBean("…"))就变成了一件耦合度极低的事情。为什么耦合度降低了?因为"new"的地方不一样了,它是如何实现的呢?
反射,功不可没
在以前,调用类成员的方式基本上是通过"new()."的方式,这样做修改了源码,不符合OCP原则,反射的引入可以通过外部配置文件,在不修改源码的基础上控制程序,符合OCP原则。它是Java中很多高级特性的基础,比如注解、动态代理。各类的ORM框架、RPC框架、Spring的IOC、AOP都是以反射作为技术基础的。

一.什么是反射?

简单来讲,反射机制指的是程序在运行时能够获取自身的信息。对于任何一个类,反射可以帮助我们在运行期获得其所有的方法和变量
毫不夸张地说,只要给定类的名字(路径),就可以通过反射机制来获得类的所有属性和方法,就像这样:

   String classPath="xxx.xxx.xxx.类名";
   //(1) 加载类, 返回Class类型的对象cls
   Class cls = Class.forName(classfullpath);
   //(2) 通过 cls 得到你加载的类 xxx.xxx.xxx.类名 的对象实例
   Object o = cls.newInstance();
   //(3) 通过 cls 得到你加载的类 xxx.xxx.xxx.类名 的 methodName"hi"  的方法对象
   //    即:在反射中,可以把方法视为对象(万物皆对象)
   Method method1 = cls.getMethod(methodName);
   //(4) 通过method1 调用方法: 即通过方法对象来实现调用方法
   method1.invoke(o); //传统方法 对象.方法() , 反射机制 方法.invoke(对象)

传统的方法都是通过对象.方法()的方式来调用,而反射机制则是通过方法.invoke(对象)来调用。
在这里插入图片描述
我们都知道Java程序在计算机中有三个阶段:编译阶段、加载阶段、运行阶段
在这里插入图片描述
加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。
这个Class类对象就像一面镜子,透过这个镜子看到类的结构,所以形象的称之为:反射

二.如何实现反射?

2.1java.lang.Class

java.lang.Class类是Java反射机制的基础,必须通过Class类才能获得在运行期一个类的相关信息,怎样获取一个Java类的Class对象呢?
1.调用对象的getClass()方法获取Class对象:

TestObject obj=new TestObject();
Class cls=obj.getClass();

2.根据类名.class获取Class对象:

Class cls=TestObject.class

3.根据Class中的静态方法Class.forName()获取Class对象:

Class cls=Class.forName("xxx.TestObject");

很多人喜欢把Class类和class关键字混淆,千万不要搞错!
JVM会为每个类都创建一个Class对象,在程序运行后JVM首先检查要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将类的Class对象载入。

2.2通过反射创建对象

在以前,通常使用new的方式来创建对象,反射也可以创建对象,并且有两种方式。
方式一:
Class类的newInstance方法,这个newInstance方法是通过调用类中定义的无参构造函数创建对象

Class cls=TestObject.class;
TestObject obj=cls.newInstance();

方式二:
和Class类的newInstance类似,第二种方式则是利用java.lang.reflect.Constructor类的newInstance方法,通过构造器的方式就是方式一的细化

Constructor<TestObject> constructor=TestObject.class.getConstructor();
TestObject obj=constructor.newInstance();

2.3通过反射获取类成员

在Class类中,用Field表示类中的属性,用Method表示类中的方法,用Annotation表示类中的注解,用Constructor表示类的构造函数,可以得到下列方法:

Field[] getFields()
Method[] getMethod()
Annotation[] getAnnotations()
Constructor[] getConstructor()

上面的一组方法其实就是一面镜子,由Class类提供的镜子,当其他类来“照镜子”,就会得到该类在运行时期获得对应的一系列类成员的方法,就会看到镜子里的自己
为了获取私有的类成员,需要使用getDeclared系列的方法

Field[] getDeclaredFields()
Method[] getDeclaredMethod()
Annotation[] getDeclaredAnnotations()
Constructor[] getDeclaredConstructor()

当类“照镜子”过后,想要在运行时期获取指定的方法,属性,注解只需要在上述系列的方法形参列表里传入类成员对应的参数即可,并且返回类型不再是数组。
可以看到,我从外部很轻松地就访问到了类的私有属性,这其实是破坏封装性的一种表现

三.反射的性能

其实,使用反射获取类的成员性能是十分低的,他比通过new的方式耗时多得多,可以写两个方法来分别测试其执行用时:

 public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {

        testTime01();
        testTime02();
    }
    //传统方式
    public static void testTime01(){
        Test test = new Test();
        long l1 = System.currentTimeMillis();
        for (int i = 0; i < 99999999; i++) {
            test.play();
        }
        long l2 = System.currentTimeMillis();
        System.out.println("传统方式执行用时:"+(l2-l1)+"ms");
    }
    //反射方式
    public static void testTime02() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class<?> aClass = Class.forName("javaReflection.Test");
        Object o = aClass.newInstance();
        Method play = aClass.getMethod("play");
        long l1 = System.currentTimeMillis();
        for (int i = 0; i < 99999999; i++) {
            play.invoke(o);
        }
        long l2 = System.currentTimeMillis();
        System.out.println("反射方式执行用时:"+(l2-l1)+"ms");
    }

各自执行耗时:
在这里插入图片描述
如果想要提升反射性能可以设置反射属性,关闭访问检查:xxx.setAccessible(true);
虽然他性能低,破坏封装性,但是反射的有点也比较明显,我们可以在运行时期获得类的信息并操作类的成员,提高了程序的扩展性和灵活性。

四.反射是如何破坏单例模式的?

我们都知道,单例模式保证一个类仅有一个实例,并提供一个访问他的全局访问点。
为了确保单例与线程安全,我们一般这样写:

public class Singletons {
    private static volatile Singletons singleton;
    private Singletons(){}

    public static Singletons getSingleton() {
        if (singleton==null){
            synchronized (Singletons.class){
                if (singleton==null){
                    singleton = new Singletons();
                }
            }
        }
        return singleton;
    }
}

在这里插入图片描述
通过写一个私有的构造器,在类的内部创建出一个单例对象,并加锁保证线程安全。
看完上面的反射再来看这个单例,他真的能做到实例的唯一性吗?很显然是不能的!
但是,反射可以在类的运行期获取并调用类的成员,当然也包括上述的私有构造器,拿到了构造器我就可以为所欲为。
所以,使用反射可以破坏单例模式,不信你看:

public class Break {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //得到单例
        Singletons singleton1 = Singletons.getSingleton();
        //通过反射获取构造器
        Constructor<Singletons> cons = Singletons.class.getDeclaredConstructor();
        //反射爆破
        cons.setAccessible(true);
        //通过构造器的newInstance 创建对象
        Singletons singleton2 = cons.newInstance();
        System.out.println("是同一个单例吗:"+(singleton1 == singleton2));
    }
}

从运行结果来看,单例模式已经被反射爆破所破坏,单例模式已经不再单例~
在这里插入图片描述

五.如何避免单例模式被反射破坏?

其实很简单,单例模式之所以遭到破坏是因为在类的运行期间我拿到的私有的构造器,并通过他创建了对象的实例,我可以在构造器里做点手脚就可以完美解决这个问题!
就像这样:

    private Singletons(){
        if (singleton!=null){
            throw new RuntimeException("单例模式中对象只能被创建一次哟~");
        }
    }

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

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

相关文章

查询利器—索引

目录 索引的优缺点 常见索引分类 MySQL数据操作的宏观过程 认识磁盘 正式理解索引结构 采用B树的原因 聚簇索引与非聚簇索引 索引操作 索引创建原则 索引的优缺点 优点&#xff1a;提高一个海量数据的检索速度 缺点&#xff1a;查询速度的提高是以插入、更新、删除…

pdf生成:wkhtmltopdf

wkhtmltopdf是开源&#xff08;LGPLv3&#xff09;命令行工具&#xff0c;使用Qt WebKit渲染引擎将HTML渲染为PDF和各种图像格式。这些完全以“headless”模式运行&#xff0c;不需要显示或显示服务wkhtmltoimage。建议&#xff1a; 不要将wkhtmltopdf与任何不受信任的HTML一起…

sqli-labs/Less-61

这一关的欢迎界面提示我们尝试机会为5次 然后要以id为注入点 首先先判断一下是否为数字型 输入id1 and 12 回显如下 所以属于字符型 然后输入1 回显如下 出现了报错信息 说明可以进行报错注入 也说明了注入类型 佐证一下 输入一个1))--回显如下 这道题我还是使用报错注入 首…

使用Python查询国内 COVID-19 疫情

有时我们只是想要一个速的工具来告诉当前疫情的情况&#xff0c;我们只需要最少的数据。 使用Python语言和tkinter图形化显示数据。 首先&#xff0c;我们使用 Tkinter 库使我们的脚本可以图形化显示。 使用 requests 库从 丁香园 获取数据。 然后我们将在这种情况下显示我们…

c# .net+香橙派orangepi 200块多打造自家 浇花助手 系统

由于出差多&#xff0c;这里的花经常过一段时间才能浇水,有些花都旱晕了&#xff0c;所以想做一个助手帮我远程就可以操作浇花&#xff0c;当然也完全可以发展为一个商用系统&#xff0c;拿浇花来做为一个测试平台吧&#xff0c;现在已经完成了&#xff0c;并已经运行了一个多月…

异双功能连接体:Alkyne PEG4 APN,2183440-36-8

一、产品描述&#xff1a; APN-C3-PEG4-炔是一种含有APN部分的异双功能连接体&#xff0c;对半胱氨酸和炔基具有很强的化学选择性。APN半胱氨酸缀合物在水性介质、人血浆和活细胞中的优异稳定性使这种新的巯基点击反应成为生物缀合应用的一种有前景的方法。 APN-C3-PEG4 alkyne…

C语言:关键字----switch、case、default(开关语句)

C语言&#xff1a;基础开发----目录 C语言&#xff1a;关键字—32个(分类说明) 有32个关键字详细说明&#xff0c;还有跳转链接&#xff01; 一、开关语句----介绍 开关语句&#xff0c;包括以下四种关键字&#xff1a; switch&#xff1a;开关语句case&#xff1a; 开关语句…

PHP如何实现订单的延时处理详解

业务需求 订单是我们在日常开发中经常会遇到的一个功能&#xff0c;最近在做业务的时候需要实现客户下单之后订单超时未支付自动取消的功能&#xff0c;刚开始确认了几种方法&#xff1a; 客户端到时间请求取消服务端定时查询有没有需要取消的订单&#xff0c;然后批量处理下…

深度学习入门(五十)计算机视觉——转置卷积

深度学习入门&#xff08;五十&#xff09;计算机视觉——转置卷积前言计算机视觉——转置卷积课件&#xff08;初见转置卷积&#xff09;1 转置卷积2 为什么称之为“转置”课件&#xff08;再谈转置卷积&#xff09;1 转置卷积2 重新排列输入和核形状换算同反卷积的关系总结教…

Android -- 每日一问:Activity的启动模式(launchMode)有哪些,有什么区别?

经典回答 这应该是一道很虐人的面试题&#xff0c;很多人都答不上来&#xff0c;很多人根本就没有用过。当我发现在被我面试的人中有80%的比例对它不了解时&#xff0c;我找过一些同事讨论是否还有在面试中考查这个问题的必要&#xff0c;得到的回答是“程序员何苦为难程序员”…

网络面试题总结

IO协议 问题一:如何理解IP的不可靠和无连接 不可靠:指的是不能保证数据包能成功地到达目的地。发生错误的时候,丢弃该数据包,发送ICMP消息给信源端。可靠性由上层提供。无连接: :IP 不维护关于后续数据报的状态信息。 体现在,IP 数据可以不按顺序发送和接收。A 发送连续…

信息安全工程实践笔记--Day2 暴力破解

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录实验目标一、弱口令分类1.系统服务弱口令2.设备弱口令3.社工型的弱口令二、弱口令的攻击方式暴力破解获取密码的方式三、bp实操1.bp模块讲解1.sniper&#xff08;狙击…

跨平台下移动应用的开发框架对比与分析

当前由于移动互联网技术的日益发展&#xff0c;人们对移动互联网程序的要求也是越来越高&#xff0c;以往的互联网程序已经不能满足人们日益增长的需求&#xff0c;对此相关的工作技术人员要不断努力&#xff0c;研发出与时俱进的移动互联网程序。 近些年来由于平板电脑、智能…

【HTML5】调查问卷制作简约版

当你第一次使用CSS时候 目录 1、调查问卷网页展示及源码 1.1html源码 1.2css源码 2、form表单属性的用法 2.1date属性 2.2radio属性 2.3checkbox属性 2.4textarea标签 2.5required属性 2.6button标签 前言&#xff1a; 大家好&#xff0c;我是拳击哥带你捶键盘。首先感…

是前端受欢迎,不是你学了前端你就受欢迎好吧

☆ 这几天世界杯真是爆冷啊&#xff0c;也许是这届世界杯是冬天举办的原因吧。 ☆ 昨晚的德国对日本&#xff0c;日本做为亚洲小组冲出的第一名&#xff0c;实力强没有问题。而且我从小就看《足球小子》&#xff0c;大空翼深入人心啊。还记得那个动画片&#xff0c;放了学赶紧回…

运营版uniapp多商户商城小程序+H5+APP+商家入驻短视频社区种草直播阶梯拼团

运营版uniapp多商户商城小程序H5APP商家入驻短视频社区种草直播阶梯拼团 前后端全套源码&#xff0c; 支持二次开发&#xff0c;代码无加密&#xff01; 独立商家后台 用于店铺商品管理订单管理发货管理等 多类经营模式 多商家B2B2C、自营B2C运营模式 私有化部署 前端Uni…

Unity编辑器拓展最全实现

1编辑器顶部菜单学习 验证事项 2编辑器Scene菜单学习 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor;public class TestSceneMenu : MonoBehaviour { [InitializeOnLoadMethod] //加入这个属性&#xff0c;就相当于…

7.前端笔记-CSS-元素显示模式

HTML元素分为块元素和行内元素两种类型 1、块元素 常见的块元素&#xff1a; <h1>~<h6> <p> <div> <ul> <ol> <li>块元素的特点&#xff1a; &#xff08;1&#xff09;独占一行 &#xff08;2&#xff09;高度、宽度、内外边距可…

java线程池详解

一 介绍 线程我们可以使用 new 的方式去创建&#xff0c;但如果并发的线程很多&#xff0c;每个线程执行的时间又不长&#xff0c;这样频繁的创建线程会大大的降低系统处理的效率&#xff0c;因为创建和销毁进程都需要消耗资源&#xff0c;线程池就是用来解决类似问题。 线程…

餐饮+KTV融合消费模式,会受消费者喜欢吗?

这个五一&#xff0c;我们雨科网门店系统的客户&#xff0c;大侠火锅店终于是将KTV搬到了自己的门店里&#xff0c;运用门店小程序功能及纸质代金券及礼品的噱头吸引客户进店&#xff0c;只需消费并和任意一人合唱一首歌即可领取&#xff0c;消费者在等餐或放松的时候一键点歌演…