当前位置: 首页 > news >正文

SpringIoC原理

我是南城余!阿里云开发者平台专家博士证书获得者!

欢迎关注我的博客!一同成长!

一名从事运维开发的worker,记录分享学习。

专注于AI,运维开发,windows Linux 系统领域的分享!

本章节对应知识库

https://www.yuque.com/nanchengcyu/java

本内容来自尚硅谷课程,此处在知识库做了个人理解

4、原理-手写IoC

我们都知道,Spring框架的IOC是基于Java反射机制实现的,下面我们先回顾一下java反射。

4.1、回顾Java反射

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。

要想解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API**(1)java.lang.Class(2)java.lang.reflect**,所以,Class对象是反射的根源

自定义类

package com.atguigu.reflect;public class Car {//属性private String name;private int age;private String color;//无参数构造public Car() {}//有参数构造public Car(String name, int age, String color) {this.name = name;this.age = age;this.color = color;}//普通方法private void run() {System.out.println("私有方法-run.....");}//get和set方法public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}@Overridepublic String toString() {return "Car{" +"name='" + name + '\'' +", age=" + age +", color='" + color + '\'' +'}';}
}

编写测试类

package com.atguigu.reflect;import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class TestCar {//1、获取Class对象多种方式@Testpublic void test01() throws Exception {//1 类名.classClass clazz1 = Car.class;//2 对象.getClass()Class clazz2 = new Car().getClass();//3 Class.forName("全路径")Class clazz3 = Class.forName("com.atguigu.reflect.Car");//实例化Car car = (Car)clazz3.getConstructor().newInstance();System.out.println(car);}//2、获取构造方法@Testpublic void test02() throws Exception {Class clazz = Car.class;//获取所有构造// getConstructors()获取所有public的构造方法
//        Constructor[] constructors = clazz.getConstructors();// getDeclaredConstructors()获取所有的构造方法public  privateConstructor[] constructors = clazz.getDeclaredConstructors();for (Constructor c:constructors) {System.out.println("方法名称:"+c.getName()+" 参数个数:"+c.getParameterCount());}//指定有参数构造创建对象//1 构造public
//        Constructor c1 = clazz.getConstructor(String.class, int.class, String.class);
//        Car car1 = (Car)c1.newInstance("夏利", 10, "红色");
//        System.out.println(car1);//2 构造privateConstructor c2 = clazz.getDeclaredConstructor(String.class, int.class, String.class);c2.setAccessible(true);Car car2 = (Car)c2.newInstance("捷达", 15, "白色");System.out.println(car2);}//3、获取属性@Testpublic void test03() throws Exception {Class clazz = Car.class;Car car = (Car)clazz.getDeclaredConstructor().newInstance();//获取所有public属性//Field[] fields = clazz.getFields();//获取所有属性(包含私有属性)Field[] fields = clazz.getDeclaredFields();for (Field field:fields) {if(field.getName().equals("name")) {//设置允许访问field.setAccessible(true);field.set(car,"五菱宏光");System.out.println(car);}System.out.println(field.getName());}}//4、获取方法@Testpublic void test04() throws Exception {Car car = new Car("奔驰",10,"黑色");Class clazz = car.getClass();//1 public方法Method[] methods = clazz.getMethods();for (Method m1:methods) {//System.out.println(m1.getName());//执行方法 toStringif(m1.getName().equals("toString")) {String invoke = (String)m1.invoke(car);//System.out.println("toString执行了:"+invoke);}}//2 private方法Method[] methodsAll = clazz.getDeclaredMethods();for (Method m:methodsAll) {//执行方法 runif(m.getName().equals("run")) {m.setAccessible(true);m.invoke(car);}}}
}

4.2、实现Spring的IoC

我们知道,IoC(控制反转)和DI(依赖注入)是Spring里面核心的东西,那么,我们如何自己手写出这样的代码呢?下面我们就一步一步写出Spring框架最核心的部分。

①搭建子模块

搭建模块:guigu-spring,搭建方式如其他spring子模块

②准备测试需要的bean

添加依赖

<dependencies><!--junit5测试--><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.3.1</version></dependency>
</dependencies>

创建UserDao接口

package com.atguigu.spring6.test.dao;public interface UserDao {public void print();
}

创建UserDaoImpl实现

package com.atguigu.spring6.test.dao.impl;import com.atguigu.spring.dao.UserDao;public class UserDaoImpl implements UserDao {@Overridepublic void print() {System.out.println("Dao层执行结束");}
}

创建UserService接口

package com.atguigu.spring6.test.service;public interface UserService {public void out();
}

创建UserServiceImpl实现类

package com.atguigu.spring.test.service.impl;import com.atguigu.spring.core.annotation.Bean;
import com.atguigu.spring.service.UserService;@Bean
public class UserServiceImpl implements UserService {//    private UserDao userDao;@Overridepublic void out() {//userDao.print();System.out.println("Service层执行结束");}
}

③定义注解

我们通过注解的形式加载bean与实现依赖注入

bean注解

package com.atguigu.spring.core.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}

依赖注入注解

package com.atguigu.spring.core.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}

说明:上面两个注解可以随意取名

④定义bean容器接口

package com.atguigu.spring.core;public interface ApplicationContext {Object getBean(Class clazz);
}

⑤编写注解bean容器接口实现

AnnotationApplicationContext基于注解扫描bean

package com.atguigu.spring.core;import java.util.HashMap;public class AnnotationApplicationContext implements ApplicationContext {//存储bean的容器private HashMap<Class, Object> beanFactory = new HashMap<>();@Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}/*** 根据包扫描加载bean* @param basePackage*/public AnnotationApplicationContext(String basePackage) {}
}

⑥编写扫描bean逻辑

我们通过构造方法传入包的base路径,扫描被@Bean注解的java对象,完整代码如下:

package com.atguigu.spring.core;import com.atguigu.spring.core.annotation.Bean;import java.io.File;
import java.util.HashMap;public class AnnotationApplicationContext implements ApplicationContext {//存储bean的容器private HashMap<Class, Object> beanFactory = new HashMap<>();private static String rootPath;@Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}/*** 根据包扫描加载bean* @param basePackage*/public AnnotationApplicationContext(String basePackage) {try {String packageDirName = basePackage.replaceAll("\\.", "\\\\");Enumeration<URL> dirs =Thread.currentThread().getContextClassLoader().getResources(packageDirName);while (dirs.hasMoreElements()) {URL url = dirs.nextElement();String filePath = URLDecoder.decode(url.getFile(),"utf-8");rootPath = filePath.substring(0, filePath.length()-packageDirName.length());loadBean(new File(filePath));}} catch (Exception e) {throw new RuntimeException(e);}}private  void loadBean(File fileParent) {if (fileParent.isDirectory()) {File[] childrenFiles = fileParent.listFiles();if(childrenFiles == null || childrenFiles.length == 0){return;}for (File child : childrenFiles) {if (child.isDirectory()) {//如果是个文件夹就继续调用该方法,使用了递归loadBean(child);} else {//通过文件路径转变成全类名,第一步把绝对路径部分去掉String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);//选中class文件if (pathWithClass.contains(".class")) {//    com.xinzhi.dao.UserDao//去掉.class后缀,并且把 \ 替换成 .String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");try {Class<?> aClass = Class.forName(fullName);//把非接口的类实例化放在map中if(!aClass.isInterface()){Bean annotation = aClass.getAnnotation(Bean.class);if(annotation != null){Object instance = aClass.newInstance();//判断一下有没有接口if(aClass.getInterfaces().length > 0) {//如果有接口把接口的class当成key,实例对象当成valueSystem.out.println("正在加载【"+ aClass.getInterfaces()[0] +"】,实例对象是:" + instance.getClass().getName());beanFactory.put(aClass.getInterfaces()[0], instance);}else{//如果有接口把自己的class当成key,实例对象当成valueSystem.out.println("正在加载【"+ aClass.getName() +"】,实例对象是:" + instance.getClass().getName());beanFactory.put(aClass, instance);}}}} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {e.printStackTrace();}}}}}}}

⑦java类标识Bean注解

@Bean
public class UserServiceImpl implements UserService
@Bean
public class UserDaoImpl implements UserDao 

⑧测试Bean加载

package com.atguigu.spring;import com.atguigu.spring.core.AnnotationApplicationContext;
import com.atguigu.spring.core.ApplicationContext;
import com.atguigu.spring.test.service.UserService;
import org.junit.jupiter.api.Test;public class SpringIocTest {@Testpublic void testIoc() {ApplicationContext applicationContext = new AnnotationApplicationContext("com.atguigu.spring.test");UserService userService = (UserService)applicationContext.getBean(UserService.class);userService.out();System.out.println("run success");}
}

控制台打印测试

⑨依赖注入

只要userDao.print();调用成功,说明就注入成功

package com.atguigu.spring.test.service.impl;import com.atguigu.spring.core.annotation.Bean;
import com.atguigu.spring.core.annotation.Di;
import com.atguigu.spring.dao.UserDao;
import com.atguigu.spring.service.UserService;@Bean
public class UserServiceImpl implements UserService {@Diprivate UserDao userDao;@Overridepublic void out() {userDao.print();System.out.println("Service层执行结束");}
}

执行第八步:报错了,说明当前userDao是个空对象

⑩依赖注入实现

package com.atguigu.spring.core;import com.atguigu.spring.core.annotation.Bean;
import com.atguigu.spring.core.annotation.Di;import java.io.File;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class AnnotationApplicationContext implements ApplicationContext {//存储bean的容器private HashMap<Class, Object> beanFactory = new HashMap<>();private static String rootPath;@Overridepublic Object getBean(Class clazz) {return beanFactory.get(clazz);}/*** 根据包扫描加载bean* @param basePackage*/public AnnotationApplicationContext(String basePackage) {try {String packageDirName = basePackage.replaceAll("\\.", "\\\\");Enumeration<URL> dirs =Thread.currentThread().getContextClassLoader().getResources(packageDirName);while (dirs.hasMoreElements()) {URL url = dirs.nextElement();String filePath = URLDecoder.decode(url.getFile(),"utf-8");rootPath = filePath.substring(0, filePath.length()-packageDirName.length());loadBean(new File(filePath));}} catch (Exception e) {throw new RuntimeException(e);}//依赖注入loadDi();}private  void loadBean(File fileParent) {if (fileParent.isDirectory()) {File[] childrenFiles = fileParent.listFiles();if(childrenFiles == null || childrenFiles.length == 0){return;}for (File child : childrenFiles) {if (child.isDirectory()) {//如果是个文件夹就继续调用该方法,使用了递归loadBean(child);} else {//通过文件路径转变成全类名,第一步把绝对路径部分去掉String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);//选中class文件if (pathWithClass.contains(".class")) {//    com.xinzhi.dao.UserDao//去掉.class后缀,并且把 \ 替换成 .String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");try {Class<?> aClass = Class.forName(fullName);//把非接口的类实例化放在map中if(!aClass.isInterface()){Bean annotation = aClass.getAnnotation(Bean.class);if(annotation != null){Object instance = aClass.newInstance();//判断一下有没有接口if(aClass.getInterfaces().length > 0) {//如果有接口把接口的class当成key,实例对象当成valueSystem.out.println("正在加载【"+ aClass.getInterfaces()[0] +"】,实例对象是:" + instance.getClass().getName());beanFactory.put(aClass.getInterfaces()[0], instance);}else{//如果有接口把自己的class当成key,实例对象当成valueSystem.out.println("正在加载【"+ aClass.getName() +"】,实例对象是:" + instance.getClass().getName());beanFactory.put(aClass, instance);}}}} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {e.printStackTrace();}}}}}}private void loadDi() {for(Map.Entry<Class,Object> entry : beanFactory.entrySet()){//就是咱们放在容器的对象Object obj = entry.getValue();Class<?> aClass = obj.getClass();Field[] declaredFields = aClass.getDeclaredFields();for (Field field : declaredFields){Di annotation = field.getAnnotation(Di.class);if( annotation != null ){field.setAccessible(true);try {System.out.println("正在给【"+obj.getClass().getName()+"】属性【" + field.getName() + "】注入值【"+ beanFactory.get(field.getType()).getClass().getName() +"】");field.set(obj,beanFactory.get(field.getType()));} catch (IllegalAccessException e) {e.printStackTrace();}}}}}}

执行第八步:执行成功,依赖注入成功

相关文章:

SpringIoC原理

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…...

如何对售后服务的全流程进行精细化的管理?

——“如何对售后服务的全流程进行精细化的管理&#xff1f;” ——“售后又是一个十分复杂的过程&#xff0c;仅靠手工或者电子表格记录这些内容&#xff0c;肯定是低效率、易出错的。最好的办法是借助合适的管理工具进行精细化的过程管理。” 假设你购买了一台新的家用电器…...

SAP UI5 walkthrough step2 Bootstrap

我的理解&#xff0c;这就是一个引导指令 1.我们右键打开命令行--执行 ui5 use OpenUI5 2.执行命令&#xff1a;ui5 add sap.ui.core sap.m themelib_sap_horizon 执行完之后&#xff0c;会更新 yaml 文件 3.修改index.html <!DOCTYPE html> <html> <head&…...

Gemini:定义下一代人工智能的里程碑

Google最近发布号称世界最强的大模型"Gemini"&#xff0c;其强大多模态LLM&#xff0c;标志着AI技术的一个新时代。 Gemini作为"迄今为止最强大的AI模型"之一&#xff0c;其独特之处在于它融合了多种模式的处理能力&#xff0c;能够同时理解和生成文本、代…...

一些系统日常运维命令和语句

一、前言 记录一些日常系统运维的命令和语句 二、linux命令与语句 1、linux查看各目录使用磁盘情况 du -h /home home为目录 du -h /home 2.查看内存使用情况 free -h 3、查看进程和CPU使用情况 top top 三、数据库语句 1、统计mysql数据库表数量 SELECT COUNT(*) A…...

微信小程序uni.chooseImage()无效解决方案

Bug场景&#xff1a; 微信小程序在上传图片时可以通过 uni.chooseImage()方案进行上传&#xff0c;这里不再赘述具体参数。一直项目都可以正常使用&#xff0c;突然有一天发现无法使用该方法&#xff0c;于是查了一下&#xff0c;发现是用户隐私协议问题。故记录一下解决方案。…...

Rust深入浅出:编程的深邃大海中的奇妙冒险

第一章&#xff1a;前言 欢迎来到Rust的深邃大海&#xff0c;这里是一片充满挑战和奇妙冒险的领域。在这篇文章中&#xff0c;我们将深入浅出&#xff0c;探索Rust编程语言的深层次特性&#xff0c;并通过诙谐而深刻的方式&#xff0c;带你走进这个奇妙的编程世界。 第二章&a…...

go-zero开发入门-API网关开发示例

开发一个 API 网关&#xff0c;代理 https://blog.csdn.net/Aquester/article/details/134856271 中的 RPC 服务。 网关完整源代码 // file: main.go package mainimport ("flag""fmt""github.com/zeromicro/go-zero/core/conf""github.c…...

TCP一对一通信

package 二十一章; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner;/*** Socket服务端**/ public class SocketServer {public static void main(String[] args) {ServerSocket ss null;Socket s null;try {// 创建监听…...

laravel DB::connection 报错 Database connection [{$name}] not configured

DB::connection(mysql2);//不是连接数据库名...

快捷支付是什么?快捷支付好申请吗?

快捷支付是指用户在购买商品时&#xff0c;不需要打开网上银行&#xff0c;只需提供银行卡号码、户名、手机号码等信息&#xff0c;银行验证手机号码的正确性&#xff0c;输入动态密码即可完成支付&#xff0c;无需打开网上银行。持卡人将银行卡绑定到第三方支付应用程序&#…...

如何在Spring Boot中集成RabbitMQ

如何在Spring Boot中集成RabbitMQ 在现代微服务架构中&#xff0c;消息队列&#xff08;如RabbitMQ&#xff09;扮演了关键的角色&#xff0c;它不仅能够提供高效的消息传递机制&#xff0c;还能解耦服务间的通信。本文将介绍如何在Spring Boot项目中集成RabbitMQ&#xff0c;…...

【Spring Boot 源码学习】ApplicationContextInitializer 详解

Spring Boot 源码学习系列 ApplicationContextInitializer 详解 引言往期内容主要内容1. 初识 ApplicationContextInitializer2. 加载 ApplicationContextInitializer3. ApplicationContextInitializer 的初始化 总结 引言 书接前文《初识 SpringApplication》&#xff0c;我们…...

软考2018下午第六题改编逻辑(状态模式)

在状态模式中&#xff0c;我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象 package org.example.状态模式.软考航空;/*** author lst* date 2023年12月07日 15:37*/ class FrequentFlyer {CState state;double flyMiles;public FrequentFlyer() {…...

基于深度学习的典型目标跟踪算法

目标跟踪是计算机视觉领域中一个重要的任务&#xff0c;它涉及在视频序列中持续地定位和追踪目标对象。以下是一些常见的深度学习目标跟踪算法&#xff1a; Siamese Network: Siamese网络是一种孪生网络结构&#xff0c;它通过将目标图像与周围环境进行对比&#xff0c;学习目…...

docker搭建nginx实现负载均衡

docker搭建nginx实现负载均衡 安装nginx 查询安装 [rootlocalhost ~]# docker search nginx [rootlocalhost ~]# docker pull nginx准备 创建一个空的nginx文件夹里面在创建一个nginx.conf文件和conf.d文件夹 运行映射之前创建的文件夹 端口&#xff1a;8075映射80 docker…...

Android蓝牙协议栈fluoride(二) - 软件框架

概述 fluoride 协议栈在整个软件框架中作为一个中间件的角色&#xff0c;向上对接APP&#xff0c;向下对接蓝牙芯片。fluoride采用C语言实现&#xff0c;与APP(Jave)通信采用JNI机制&#xff1b;与蓝牙芯片通信使用HCI硬件接口&#xff08;HCI软件协议参考蓝牙核心规范&#x…...

IDEA中的Postman!

Postman是大家最常用的API调试工具&#xff0c;那么有没有一种方法可以不用手动写入接口到Postman&#xff0c;即可进行接口调试操作&#xff1f;今天给大家推荐一款IDEA插件&#xff1a;Apipost Helper&#xff0c;写完代码就可以调试接口并一键生成接口文档&#xff01;而且还…...

el-tooltip (element-plus)修改长度

初始状态&#xff1a; 修改后&#xff1a; 就是添加 :teleported"false"&#xff0c;问题解决&#xff01;&#xff01;&#xff01; <el-tooltipeffect"dark"content"要求密码长度为9-30位&#xff0c;需包含大小写字母、数字两种或以上与特殊字…...

Verilog学习 | 用initial语句写出固定的波形

initial beginia 0;ib 1;clk 0;#10ia 1; #20ib 0;#20ia 0; endalways #5 clk ~clk; 或者 initial clk 0;initial beginia 0;#10ia 1; #40ia 0; endinitial beginib 1;#30 ib 0; endalways #5 clk ~clk;...

解锁光猫配置自由:中兴ONT解密工具完全指南

解锁光猫配置自由&#xff1a;中兴ONT解密工具完全指南 【免费下载链接】ZET-Optical-Network-Terminal-Decoder 项目地址: https://gitcode.com/gh_mirrors/ze/ZET-Optical-Network-Terminal-Decoder 你是否曾经因为无法修改光猫设置而感到束手无策&#xff1f;当运营…...

【AI重塑科研】无需通读全文,三步教你用大模型高效产出文献综述

1. 为什么你需要AI辅助文献综述&#xff1f; 每次打开文献库看到上百篇待读论文就头皮发麻&#xff1f;我完全理解这种感受。去年准备开题报告时&#xff0c;导师要求我两周内完成50篇核心文献的综述&#xff0c;当时差点崩溃。直到我发现用大模型处理文献可以节省90%的时间&am…...

OpenClaw+Qwen3.5-4B-Claude-4.6-Opus-Reasoning-Distilled-GGUF:学术论文辅助写作系统

OpenClawQwen3.5-4B-Claude-4.6-Opus-Reasoning-Distilled-GGUF&#xff1a;学术论文辅助写作系统 1. 为什么需要AI辅助学术写作 去年冬天&#xff0c;我在赶一篇计算机视觉领域的会议论文时&#xff0c;经历了所有研究者都熟悉的痛苦&#xff1a;连续三天熬夜整理参考文献、…...

企业级流程建模前端架构:基于Vite+Vue3的低代码解决方案

企业级流程建模前端架构&#xff1a;基于ViteVue3的低代码解决方案 【免费下载链接】vite-vue-bpmn-process 基于 Vite TypeScript Vue3 NaiveUI Bpmn.js 的流程编辑器&#xff08;前端部分&#xff09;。支持高度自定义&#x1f680;&#x1f680;&#x1f680;。Vue 2 版本…...

5分钟打造私人语音助手:开源离线语音键盘Sayboard全解析

5分钟打造私人语音助手&#xff1a;开源离线语音键盘Sayboard全解析 【免费下载链接】Sayboard An open-source on-device voice IME (keyboard) for Android using the Vosk library. 项目地址: https://gitcode.com/gh_mirrors/sa/Sayboard 在智能手机普及的今天&…...

从零开始:如何为你的深度学习项目选择最合适的开源数据集

从零开始&#xff1a;如何为你的深度学习项目选择最合适的开源数据集 当你站在深度学习项目的起点&#xff0c;面对琳琅满目的开源数据集时&#xff0c;如何做出明智的选择往往决定了项目的成败。数据集不仅是模型训练的"原材料"&#xff0c;更是影响最终性能的关键变…...

如何用stressapptest进行高效内存和磁盘压力测试?实战案例分享

如何用stressapptest进行高效内存和磁盘压力测试&#xff1f;实战案例分享 在服务器运维和硬件性能评估中&#xff0c;内存和磁盘的稳定性直接关系到系统的可靠性。想象一下&#xff0c;当你的服务器在凌晨三点突然因为内存错误崩溃&#xff0c;或者磁盘在高峰期出现读写异常&a…...

智慧生鲜配送:揭秘生鲜配送商城APP功能版块设计

在数字化消费浪潮中&#xff0c;生鲜配送商城APP成为居民采购食材的重要渠道。其功能版块设计聚焦用户需求&#xff0c;通过智能化、便捷化的操作体验&#xff0c;打造高效生鲜购物场景。以下揭秘其核心功能玩法&#xff0c;解析如何实现“从指尖到餐桌”的流畅服务。一、首页&…...

Ubuntu 24.04镜像源配置全攻略:从原理到实战(含常见报错解决)

Ubuntu 24.04镜像源深度解析与高效配置实战 最近在帮朋友配置新装的Ubuntu 24.04时&#xff0c;发现这个版本在软件源管理上做了重大调整——从传统的sources.list文件变成了结构化更强的sources.d目录配置方式。这个变化让不少习惯了旧版本的用户感到困惑&#xff0c;也让我意…...

从浮点到定点:手把手教你用MATLAB自定义函数实现加减乘除(避坑溢出与精度损失)

从浮点到定点&#xff1a;手把手教你用MATLAB自定义函数实现加减乘除&#xff08;避坑溢出与精度损失&#xff09; 当算法需要从实验室环境迁移到嵌入式设备时&#xff0c;浮点运算的硬件开销常常成为瓶颈。这时定点数运算就像一把手术刀——精准控制每个比特的用途&#xff0c…...