Spring高级面试题-2024

news2024/11/18 4:19:07

Spring 框架中都用到了哪些设计模式?

1. 简单工厂:
    ○ BeanFactory:Spring的BeanFactory充当工厂,负责根据配置信息创建Bean实例。它是一种工厂模式的应用,根据指定的类名或ID创建Bean对象。
2. 工厂方法:
    ○ FactoryBean:FactoryBean接口允许用户自定义Bean的创建逻辑,实现了工厂方法模式。开发人员可以使用FactoryBean来创建复杂的Bean实例。
3. 单例模式:
    ○ Bean实例:Spring默认将Bean配置为单例,确保在容器中只有一个共享的实例,这有助于节省资源和提高性能。
4. 适配器模式:
    ○ SpringMVC中的HandlerAdapter:SpringMVC的HandlerAdapter允许不同类型的处理器适配到处理器接口,以实现统一的处理器调用。这是适配器模式的应用。
5. 装饰器模式:
    ○ BeanWrapper:Spring的BeanWrapper允许在不修改原始Bean类的情况下添加额外的功能,这是装饰器模式的实际应用。
6. 代理模式:
    ○ AOP底层:Spring的AOP(面向切面编程)底层通过代理模式来实现切面功能,包括JDK动态代理和CGLIB代理。
7. 观察者模式:
    ○ Spring的事件监听:Spring的事件监听机制是观察者模式的应用,它允许组件监听和响应特定类型的事件,实现了松耦合的组件通信。
8. 策略模式:
    ○ excludeFilters、includeFilters:Spring允许使用策略模式来定义包扫描时的过滤策略,如在@ComponentScan注解中使用的excludeFilters和includeFilters。
9. 模板方法模式:
    ○ Spring几乎所有的外接扩展:Spring框架的许多模块和外部扩展都采用模板方法模式,例如JdbcTemplate、HibernateTemplate等。
10. 责任链模式:
    ○ AOP的方法调用:Spring AOP通过责任链模式实现通知(Advice)的调用,确保通知按顺序执行。
Spring框架的设计哲学是通过这些设计模式来提供强大的功能和可定制性。它的模块化、松耦合的设计使得开发人员能够更轻松地构建可维护、可扩展和灵活的应用程序。这些设计模式的应用有助于实现代码重用、降低开发成本,是Spring框架广受欢迎的原因之一。

Spring事件监听的核心机制是什么?

观察者模式: 它允许一个对象(称为主题或被观察者)维护一组依赖于它的对象(称为观察者),并在主题状态发生变化时通知观察者。

它包含三个核心:

  1. 事件: 事件是观察者模式中的主题状态变化的具体表示,它封装了事件发生时的信息。在Spring中,事件通常是普通的Java对象,用于传递数据或上下文信息。
  2. 事件发布者: 在Spring中,事件发布者充当主题的角色,负责触发并发布事件。它通常实现了ApplicationEventPublisher接口或使用注解@Autowired来获得事件发布功能。
  3. 事件监听器: 事件监听器充当观察者的角色,负责监听并响应事件的发生。它实现了ApplicationListener接口,通过onApplicationEvent()方法来处理事件。

Spring事务的失效原因?

  1. 方法是private也会失效,解决:改成public: Spring的事务代理通常是通过Java动态代理或CGLIB动态代理生成的,这些代理要求目标方法是公开可访问的(public)。私有方法无法被代理,因此事务将无效。解决方法是将目标方法改为public或protected。
  2. 目标类没有配置为Bean也会失效,解决:配置为Bean: Spring的事务管理需要在Spring容器中配置的Bean上才能生效。如果目标类没有被配置为Spring Bean,那么事务将无法被应用。解决方法是确保目标类被正确配置为Spring Bean。
  3. 自己捕获了异常,解决:不要捕获处理: Spring事务管理通常依赖于抛出未捕获的运行时异常来触发事务回滚。如果您在方法内部捕获了异常并处理了它,事务将不会回滚。解决方法是让异常在方法内部被抛出,以触发事务回滚。
  4. 使用CGLIB动态代理,但@Transactional声明在接口上: 默认情况下,Spring的事务代理使用基于接口的JDK动态代理。如果您将@Transactional注解声明在接口上,而目标类是使用CGLIB代理的,事务将不会生效。解决方法是将@Transactional注解移到目标类的方法上,或者配置Spring以使用CGLIB代理接口。
  5. 跨越多个线程的事务管理,解决:使用编程式事务或分布式事务: 如果您的应用程序在多个线程之间共享数据库连接和事务上下文,事务可能会失效,除非适当地配置事务传播属性。
  6. 事务传播属性或捕获异常等熟悉设置不正确: 事务传播属性定义了事务如何传播到嵌套方法或外部方法。如果事务传播属性设置不正确,可能会导致事务失效或不符合预期的行为。

Spring多线程事务 能否保证事务的一致性

        在多线程环境下,Spring事务管理默认情况下无法保证全局事务的一致性。这是因为Spring的本地事务管理是基于线程的,每个线程都有自己的独立事务。

          分布式事务: 如果您的应用程序需要跨多个资源(例如多个数据库)的全局事务一致性,那么您可能需要使用分布式事务管理(如2PC/3PC TCC等)来管理全局事务。这将确保所有参与的资源都处于相同的全局事务中,以保证一致性。

什么情况下AOP会失效,怎么解决?

  1. 内部方法调用: 如果在同一个类中的一个方法调用另一个方法,AOP通知可能不会触发,因为AOP通常是通过代理对象拦截外部方法调用的。解决方式是注入本类对象进行调用, 或者设置暴露当前代理对象到本地线程, 可以通过AopContext.currentProxy() 拿到当前正在调用的动态代理对象。
  2. 静态方法: AOP通常无法拦截静态方法的调用,因为静态方法不是通过对象调用的。解决方法是将静态方法调用替换为实例方法调用,或者考虑其他技术来实现横切关注点。
  3. AOP配置问题: 错误的AOP配置可能导致通知不正确地应用于目标方法,或者在不希望的情况下应用。解决方法是仔细检查AOP配置,确保切点表达式和通知类型正确配置。
  4. 代理问题: 如果代理对象不正确地创建或配置,AOP通知可能无法生效。解决方法是调试底层源码确保代理对象正确创建,并且AOP通知能够拦截代理对象的方法调用。

JDK动态代理和CGLIB动态代理的区别

从性能上特性对比:

        JDK动态代理要求目标对象必须实现至少一个接口,因为它基于接口生成代理类。而CGLIB动态代理不依赖于目标对象是否实现接口,可以代理没有实现接口的类,它通过继承或者代理目标对象的父类来实现代理。

从创建代理时的性能对比:

        JDK动态代理通常比CGLIB动态代理创建速度更快,因为它不需要生成字节码文件。而CGLIB动态代理的创建速度通常比较慢,因为CGLIB需要生成字节码文件。另外,JDK代理生成的代理类较小,占用较少的内存,而CGLIB生成的代理类通常较大,占用更多的内存。

从调用时的性能对比:

        JDK动态代理在方法调用时需要通过反射机制来调用目标方法,因此性能略低于CGLIB,尽管JDK动态代理在Java 8中有了性能改进,但CGLIB动态代理仍然具有更高的方法调用性能。CGLIB动态代理在方法调用时不需要通过反射,直接调用目标方法,通常具有更高的方法调用性能,同时无需类型转换。

介绍下SpringAop的底层实现

Spring AOP是Spring框架的一个重要组成部分,用于实现面向切面编程。它通过在方法调用前、调用后或异常抛出时插入通知,允许开发者在核心业务逻辑之外执行横切关注点的代码。

底层实现主要分两部分:创建AOP动态代理调用代理

在启动Spring会创建AOP动态代理

首先通过AspectJ解析切点表达式: 在创建代理对象时,Spring AOP使用AspectJ来解析切点表达式。它会根据定义的条件匹配目标Bean的方法。如果Bean不符合切点的条件,将跳过,否则将会通动态代理包装Bean对象:具体会根据目标对象是否实现接口来选择使用JDK动态代理或CGLIB代理。这使得AOP可以适用于各种类型的目标对象。

在调用阶段:

  1. Spring AOP使用责任链模式来管理通知的执行顺序。通知拦截链包括前置通知、后置通知、异常通知、最终通知和环绕通知,它们按照配置的顺序形成链式结构。
  2. 通知的有序执行: 责任链确保通知按照预期顺序执行。前置通知在目标方法执行前执行,后置通知在目标方法成功执行后执行,异常通知在方法抛出异常时执行,最终通知无论如何都会执行,而环绕通知包裹目标方法,允许在方法执行前后添加额外的行为。

Spring是如何解决Bean的循环依赖?

Spring是如何解决的循环依赖: 采用三级缓存解决的 就是三个Map ; 关键: 一定要有一个缓存保存它的早期对象作为死循环的出口

  1. 1、一级缓存singletonObjects存放可以使用的单例。
    2、二级缓存earlySingletonObjects存放的是早期的bean,即半成品,此时还无法使用。
    3、三级缓存singletonFactories是一个对象工厂,用于创建对象并放入二级缓存中。同时,如果对象有Aop代理,则对象工厂返回代理对象。

单例bean和单例模式有什么区别

1. 定义和用途:
    ○ 单例Bean:在Spring框架中,单例Bean是指在整个应用程序中只存在一个实例的Bean对象。单例Bean的作用是共享和复用对象实例,以提高性能和减少资源消耗。
    ○ 单例模式:单例模式是一种设计模式,用于确保一个类只有一个实例,并提供一个全局访问点来获取该实例。单例模式的目的是限制类的实例化次数,以保证全局唯一性和避免资源浪费。
2. 实现方式:
    ○ 单例Bean:在Spring中,单例Bean的创建和管理由Spring容器负责。Spring容器在启动时会创建单例Bean的实例,并在整个应用程序的生命周期中共享该实例。
    ○ 单例模式:单例模式的实现方式可以有多种,常见的方式包括饿汉式(在类加载时就创建实例)、懒汉式(在第一次使用时创建实例)等。
3. 适用范围:
    ○ 单例Bean:单例Bean适用于需要共享和复用对象实例的场景,例如数据库连接池、线程池等。
    ○ 单例模式:单例模式适用于需要确保全局唯一性和避免资源浪费的场景,例如配置信息管理、日志记录器等。

Bean有哪几种配置方式?

  1. XML配置:使用XML文件来配置Bean,通过<bean>元素定义Bean的属性和依赖关系。可以使用Spring的XML命名空间和标签来简化配置。
  2. 注解配置:使用注解来配置Bean,通过在Bean类上添加注解,如@Component、@Service、@Repository等,来标识Bean的角色和作用。
  3. JavaConfig方式:使用Java类来配置Bean,通过编写一个配置类,使用@Configuration注解标识,然后在方法上使用@Bean注解来定义Bean。
  4. @Import:@Import注解可以用于导入其他配置类,也可以用于导入其他普通类。当导入的是配置类时,被导入的配置类中定义的Bean会被纳入到当前配置类的上下文中;当导入的是普通类时,被导入的类本身会被当作一个Bean进行注册。

Spring-Ioc容器的加载过程

  • 配置解析阶段主要做的工作是加载和解析配置文件,将配置的bean解析成 BeanDefinition。
  1. 读取配置:通过BeanDefinitionReader读取配置文件或配置类
  2. 解析配置信息:如ComonentScan、Bean配置等
  3. 扫描类注解:根据ComonentScan扫描@Component、@Bean、@Configuration、@Import等注解...
  4. 将符合的bean注册为BeanDefinition
  • Bean的创建过程主要做的工作是根据 BeanDefinition创建Bean。
  1. 实例化Bean:容器根据配置文件中的Bean定义,实例化Bean对象。可以通过构造函数实例化、工厂方法实例化、静态工厂方法实例化等方式来创建Bean对象。
  2. 注入Bean属性:容器会为实例化的Bean对象设置属性值,可以通过setter方法注入属性值,也可以通过构造函数注入属性值。
  3. 处理依赖关系:容器会处理Bean之间的依赖关系,将依赖的Bean注入到需要的地方 。
  4. 执行初始化方法:容器会调用Bean的初始化方法,可以通过实现InitializingBean接口或在配置文件中指定初始化方法来定义Bean的初始化逻辑。
  5. 注册Bean:容器会将实例化、属性设置和初始化完成的Bean对象注册到容器中,以便后续的使用和管理。
  6. 完成加载:容器完成所有Bean的加载和初始化后,即完成了IoC容器的加载过程。此时,可以通过容器调用getBean获取Bean对象。

基础篇

说一下Spring的事务传播行为

  1. REQUIRED:如果当前存在事务,则加入该事务,如果当前没有事务,则创建一个新的事务。这是最常用的传播行为,也是默认的,适用于大多数情况。
  2. REQUIRES_NEW:无论当前是否存在事务,都创建一个新的事务。如果当前存在事务,则将当前事务挂起。适用于需要独立事务执行的场景,不受外部事务的影响。
  3. SUPPORTS:如果当前存在事务,则加入该事务,如果当前没有事务,则以非事务方式执行。适用于不需要强制事务的场景,可以与其他事务方法共享事务。
  4. NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则将当前事务挂起。适用于不需要事务支持的场景,可以在方法执行期间暂时禁用事务。
  5. MANDATORY:如果当前存在事务,则加入该事务,如果当前没有事务,则抛出异常。适用于必须在事务中执行的场景,如果没有事务则会抛出异常。
  6. NESTED:如果当前存在事务,则在嵌套事务中执行,如果当前没有事务,则创建一个新的事务。嵌套事务是外部事务的一部分,可以独立提交或回滚。适用于需要在嵌套事务中执行的场景。
  7. NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。适用于不允许在事务中执行的场景,如果存在事务则会抛出异常。

通过@Transactional注解的propagation属性来指定事务传播行为 。

Spring-AOP通知和执行顺序?

Spring切面可以应用5种类型的通知:

  1. 前置通知:在目标方法被调用之前调用通知功能;
  2. 后置通知:在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
  3. 返回通知:在目标方法成功执行之后调用通知;
  4. 异常通知:在目标方法抛出异常后调用通知;
  5. 环绕通知:通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

执行顺序:

Spring在5.2.7之前的执行顺序是:

Spring在5.2.7之后就改变的通知的执行顺序改为:

1、正常执行:前置--->方法---->返回--->后置

2、异常执行:前置--->方法---->异常--->后置

谈谈你对Spring的理解

可以从2个层面理解Spring:

  • 首先Spring是一个生态:可以构建企业级应用程序所需的一切基础设施
  • 但是,通常Spring指的就是Spring Framework,它有两大核心:
  1. IOC 和 DI 的支持

Spring 的核心就是一个大的工厂容器,可以维护所有对象的创建和依赖关系,Spring 工厂用于生成 Bean,并且管理 Bean 的生命周期,实现高内聚低耦合的设计理念。

        2. AOP 编程的支持

Spring 提供了面向切面编程,面向切面编程允许我们将横切关注点从核心业务逻辑中分离出来,实现代码的模块化和重用。可以方便的实现对程序进行权限拦截、运行监控、日志记录等切面功能。

除了这两大核心还提供了丰富的功能和模块, 数据访问、事务管理、Web开发等。数据访问模块提供了对数据库的访问支持,可以方便地进行数据库操作。事务管理模块提供了对事务的管理支持,确保数据的一致性和完整性。Web开发模块则提供了构建Web应用程序的工具和框架,简化了Web开发的过程。

总结一句话:它是一个轻量级、非入侵式的控制反转 (IoC) 和面向切面 (AOP) 的容器框架。

Spring有哪些缺点

  1. 学习曲线较陡峭:Spring框架是一个功能强大且灵活的框架,但也因此学习曲线较陡峭。对于初学者来说,可能需要花费一些时间来理解和掌握Spring的核心概念和特性。
  2. 配置复杂:Spring框架的配置通常使用XML或注解进行,这种配置方式可能会导致配置文件变得复杂和冗长。特别是在大型项目中,配置文件的维护和管理可能会变得困难,当然这个问题在SpringBoot中得到解决。
  3. 运行时性能:由于Spring框架提供了很多功能和特性,它的运行时性能可能相对较低。尤其是在需要频繁创建和管理对象的场景下,可能会对系统的性能产生一定的影响。
  4. 过度依赖:在使用Spring框架时,可能会出现过度依赖的情况。由于Spring提供了很多功能和模块,开发人员可能会过度依赖Spring框架,导致项目的可移植性和可维护性下降。
  5. 文档和社区支持:尽管Spring框架有很多优秀的文档和活跃的社区支持,但有时候可能会遇到文档不完善或社区资源有限的情况。这可能会给开发人员带来一些困扰。

需要注意的是,这些缺点并不意味着Spring框架不好,而是在使用过程中需要注意和克服的问题。同时,Spring框架的优点和功能远远超过了它的缺点,因此它仍然是一个非常受欢迎和广泛使用的框架。

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

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

相关文章

numcpp boostvs2017踩坑记录

之前想用boost1.69版本&#xff0c;但是boost与numcpp编译过程死活找不到boost1.69&#xff0c;踩坑无数&#xff0c;只能采用1.79版本。 https://www.cnblogs.com/tang-zhou-zhou/p/16067695.html 在 Windows 下通过 CMake 使用 Boost 库_cmake boost-CSDN博客 在VS2019中配…

析构 函数

对象死亡的时候会调用析构函数 #include<iostream> using namespace std;class MM { public:~MM(){cout << "调用析构函数" << endl << endl;}};int main() {{MM mm;//动态申请的内存需要手动释放MM* p new MM();cout << "1...…

【Java常用的API】JDK7以前时间相关类

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏 …

js实现拖放效果

dataTransfer对象 说明&#xff1a;dataTransfer对象用于从被拖动元素向放置目标传递字符串数据。因为这个对象是 event 的属性&#xff0c;所以在拖放事件的事件处理程序外部无法访问 dataTransfer。在事件处理程序内部&#xff0c;可以使用这个对象的属性和方法实现拖放功能…

Linux(centos7)部署hadoop集群

部署环境要求:已完成JDK环境部署、配置完成固定IP、SSH免费登录、防火墙关闭等。 1、下载、上传主机 官网:https://hadoop.apache.org 2、解压缩、创建软连接 解压: tar -zxvf hadoop-3.3.6.tar.gz软连接: ln -s /usr/local/apps/hadoop-3.3.6 hadoop3、文件配置 hadoo…

合辑下载 | MatrixOne 与 MySQL 全面对比

前言 MatrixOne是一款高度兼容MySQL语法的HTAP数据库&#xff0c;采用云原生化和存储、计算、事务分离的架构打造了HSTAP超融合数据引擎&#xff0c;实现单一数据库系统同时支持OLTP、OLAP、流计算等多种业务负载。基于MatrixOne高度兼容MySQL的定位&#xff0c;社区的小伙伴在…

多层陶瓷电容器(MLCC)的基本结构与特点

多层陶瓷电容器&#xff08;MLCC&#xff09;是一种电子元件&#xff0c;用于存储电荷和调节电路中的电容值。它们由多个陶瓷层组成&#xff0c;每个层之间夹有金属电极&#xff0c;然后堆叠在一起&#xff0c;并在两端连接上导体引线&#xff0c;形成一个整体结构。在外部通常…

【保姆级讲解如何下载MATLAB和安装MATLAB】

&#x1f308;博主&#xff1a;程序员不想YY啊&#x1f308; &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家&#x1f3c6; &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 &#x1f91d;希望本文对您有所裨益&#xff0c;如有…

【Java程序设计】【C00384】基于(JavaWeb)Springboot的民航网上订票系统(有论文)

【C00384】基于&#xff08;JavaWeb&#xff09;Springboot的民航网上订票系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;已经做了六年的毕业设计程序开发&#x…

Linux的VirtualBox中USB设备无法选择USB3.0怎么办?

在VirtualBox中&#xff0c;如果遇到USB设备无法选择 USB 3.0 的问题&#xff0c;可以尝试按照以下步骤来解决&#xff1a; 确保VirtualBox版本支持USB 3.0&#xff1a;首先&#xff0c;你需要确认你的VirtualBox版本是否支持USB 3.0。一些较旧的版本可能不支持&#xff0c;因此…

09 网络ARP请求,响应,ICMP协议

arp协议_arp请求_arp回应 ICMP包构造ping搜狐服务器参考 #include <stdio.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <linux/if_packet.h> #include <linux/if_ether.h> #include <string.h> #includ…

加密软件VMProtect教程:使用脚本Mach-O文件

VMProtect是新一代软件保护实用程序。VMProtect支持德尔菲、Borland C Builder、Visual C/C、Visual Basic&#xff08;本机&#xff09;、Virtual Pascal和XCode编译器。 同时&#xff0c;VMProtect有一个内置的反汇编程序&#xff0c;可以与Windows和Mac OS X可执行文件一起…

怎么制作iOS证书

首先我们登录appuploder官网 搜索 appuploder 第一个就是我们官网啦&#xff0c;网址是&#xff1a;Appuploader home -- A tool improve ios develop efficiency such as submit ipa to appstore and manage ios certificate 可以跨平台开发&#xff0c;无论是Windows还是Ma…

前端面试题---->JavaScript

const声明的对象属性和数组的值可以被修改吗&#xff1f;为什么 原因&#xff1a;当使用const声明一个对象或数组时&#xff0c;实际上是保证了对象或数组的引用不会被修改&#xff0c;但对象或数组本身的属性或元素是可以被修改的。这是因为const只能保证指向的内存地址不变&a…

搭载前净柔泉女冲技术的科勒马桶盖,你的家中必备

近几年智能家居逐渐成为众多家庭的首选&#xff0c;尤其是智能马桶已经逐渐变成家中必备&#xff0c;但智能马桶动辄几千上万的价格让很多家庭望而却步&#xff0c;于是智能马桶盖应运而生。科勒清舒宝智能便盖不仅可以适配市面上大多数马桶&#xff0c;而且功能丰富&#xff0…

基于springboot+vue调用百度ai实现车牌号识别功能

百度车牌号识别官方文档 结果视频演示 后端代码 private String getCarNumber(String imagePath, int count) {// 请求urlString url "https://aip.baidubce.com/rest/2.0/ocr/v1/license_plate";try {byte[] imgData FileUtil.readFileByBytes(imagePath);Stri…

精酿啤酒:多阶段发酵工艺的特点与优势

Fendi Club啤酒采用多阶段发酵工艺&#xff0c;这种工艺在啤酒酿造中具有显著的特点和优势。 首先&#xff0c;多阶段发酵工艺是一种复杂的酿造过程&#xff0c;它包括多个阶段的发酵和陈化过程。这种工艺需要切确控制每个阶段的时间、温度和酵母种类等参数&#xff0c;以确保…

【揭秘】企讯通106短信通知:那些你不可不知的“幕后功臣”

在日常生活中&#xff0c;你是否留意过那些来自“106”开头的短信&#xff1f;它们悄无声息地出现在你的手机收件箱&#xff0c;为你带来账单提醒、验证码、优惠活动等各类重要信息。看似平淡无奇的“106短信通知”&#xff0c;实则蕴含着丰富的科技力量与智慧结晶。今天&#…

together.ai简介

Together AI Solutions | Fastest Tools for Building Private Models for Enterprise Quickstart

【linux深入剖析】基础IO操作 | 使用Linux库函数实现读写操作 | 文件相关系统调用接口

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 前言1.复习C文件IO相关操…