在 Android 开发初期,很多开发者会把所有逻辑塞进 Activity—— 网络请求、数据处理、UI 更新全堆在一起,导致代码超过数千行,改一个按钮点击都要翻半天。这种 “面条式代码” 的根源是缺乏架构设计。随着应用复杂度提升,MVC、MVP、MVVM 三种架构逐渐成为主流,它们通过 “分层设计” 解决代码耦合问题。本文将从核心思想、代码实现到适用场景,全面解析这三种架构的设计逻辑,帮你找到适合项目的架构方案。

一、架构设计的核心目标

无论哪种架构,最终目的都是解决三个核心问题:

  • 解耦:分离 UI、业务逻辑、数据处理,避免 “改一处动全身”;
  • 可测试:业务逻辑可独立于 UI 测试(如无需启动 Activity 就能测试登录逻辑);
  • 可维护:分层清晰,新人能快速定位代码位置(如 “UI 相关找 View 层,网络请求找 Model 层”)。

形象比喻:架构就像 “餐厅分工”—— 厨师(Model)负责做菜(数据处理),服务员(Presenter/ViewModel)负责传递需求(业务逻辑),顾客(View)只负责点餐(UI 交互),各司其职才高效。

二、MVC 架构:最基础的分层思想

MVC(Model-View-Controller)是最早普及的分层架构,核心是 “将数据、UI、逻辑分离”。在 Android 中,MVC 的实现有其特殊性 —— 因 Activity 同时承担部分 View 和 Controller 职责,与传统 MVC 略有差异。

2.1 MVC 核心结构与职责

层级

核心职责

Android 中的载体

示例操作

Model

数据管理(网络请求、数据库、实体)

JavaBean、Repository、Dao

调用登录接口、从数据库查用户信息

View

展示 UI、接收用户输入

XML 布局、Activity(部分)、View

显示登录按钮、输入用户名密码

Controller

处理业务逻辑、协调 Model 和 View

Activity(主要)、Fragment

点击登录后调用 Model 校验,通知 View 显示结果

2.2 Android MVC 的实现(登录案例)

以 “登录功能” 为例,MVC 的代码结构如下:

(1)Model 层:数据与数据处理
// 1. 数据实体(User.java)
public class User {private String username;private String password;// 构造方法、getter、setter
}// 2. 数据处理(登录接口调用,LoginModel.java)
public class LoginModel {// 模拟网络请求public void login(User user, LoginCallback callback) {new Thread(() -> {try {// 模拟网络延迟Thread.sleep(1000);// 简单校验逻辑if ("admin".equals(user.getUsername()) && "123456".equals(user.getPassword())) {callback.onSuccess("登录成功");} else {callback.onFail("用户名或密码错误");}} catch (InterruptedException e) {callback.onFail("网络异常");}}).start();}// 回调接口(Model通知Controller结果)public interface LoginCallback {void onSuccess(String msg);void onFail(String msg);}
}
(2)View 层:UI 展示(XML 布局)
<!-- activity_login.xml -->
<LinearLayout><EditTextandroid:id="@+id/et_username"hint="用户名"/><EditTextandroid:id="@+id/et_password"hint="密码"inputType="textPassword"/><Buttonandroid:id="@+id/btn_login"text="登录"/><TextViewandroid:id="@+id/tv_result"/>
</LinearLayout>
(3)Controller 层:逻辑协调(Activity)
public class LoginActivity extends AppCompatActivity implements LoginModel.LoginCallback {private EditText etUsername;private EditText etPassword;private TextView tvResult;private LoginModel loginModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_login);// 初始化ViewetUsername = findViewById(R.id.et_username);etPassword = findViewById(R.id.et_password);tvResult = findViewById(R.id.tv_result);loginModel = new LoginModel();// 登录按钮点击(用户输入触发Controller)findViewById(R.id.btn_login).setOnClickListener(v -> {String username = etUsername.getText().toString();String password = etPassword.getText().toString();// 调用Model处理数据loginModel.login(new User(username, password), this);});}// Model回调:更新UI(Controller通知View)@Overridepublic void onSuccess(String msg) {runOnUiThread(() -> tvResult.setText(msg));}@Overridepublic void onFail(String msg) {runOnUiThread(() -> tvResult.setText(msg));}
}

2.3 MVC 的优缺点与适用场景

优势:
  • 简单直观:无需额外类和接口,新手易上手;
  • 开发快速:适合小型项目(如工具类 APP),无需复杂设计。
缺陷:
  • Activity 职责过重:既做 Controller(逻辑)又做 View(UI),代码易膨胀(上千行很常见);
  • 耦合度较高:View 和 Controller 通过 Activity 强耦合,难以单独测试(测登录逻辑需启动 Activity);
  • 复用性差:逻辑与 Activity 绑定,换个界面(如从 Activity 换成 Dialog)需重写逻辑。
适用场景:
  • 小型项目(如单个 Activity 的工具 APP);
  • 快速原型开发(需快速验证功能,不考虑长期维护)。

三、MVP 架构:解耦 View 与逻辑的中间层

MVP(Model-View-Presenter)是为解决 MVC 中 “Activity 职责过重” 而诞生的架构。其核心是引入Presenter 作为中间层,彻底分离 View(UI)和业务逻辑,让 Activity 只专注于 UI 展示。

3.1 MVP 核心结构与职责

MVP 在 MVC 基础上拆分出 Presenter,各层职责更清晰:

层级

核心职责

Android 中的载体

核心交互

Model

数据管理(与 MVC 一致)

JavaBean、Repository

登录接口调用、数据校验

View

纯 UI 层(展示、用户输入)

Activity、Fragment、XML 布局

显示加载框、暴露更新 UI 的方法

Presenter

业务逻辑核心、协调 Model 和 View

Presenter 类(独立于 Android 框架)

接收 View 的登录请求→调用 Model→通知 View 更新

核心改进

  • View 与 Presenter 通过接口交互(View 只暴露 UI 方法,不包含逻辑);
  • Presenter 完全独立于 Android 框架(不持有 Activity 上下文),可单独测试。

3.2 Android MVP 的实现(登录案例)

同样以登录功能为例,MVP 通过 “接口定义交互” 实现解耦:

(1)Model 层:与 MVC 一致(复用 LoginModel)
// 复用MVC中的LoginModel和User,无需修改
public class LoginModel {public void login(User user, LoginCallback callback) { ... }// 回调接口public interface LoginCallback { ... }
}
(2)View 层:定义 UI 接口 + 实现
// 1. View接口(定义UI操作,与Presenter交互)
public interface LoginView {// 显示加载状态void showLoading();// 隐藏加载状态void hideLoading();// 更新登录结果void showResult(String msg);// 获取用户输入String getUsername();String getPassword();
}// 2. View实现(Activity只做UI,不处理逻辑)
public class LoginActivity extends AppCompatActivity implements LoginView {private EditText etUsername;private EditText etPassword;private TextView tvResult;private ProgressDialog loadingDialog;private LoginPresenter presenter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_login);// 初始化UIetUsername = findViewById(R.id.et_username);etPassword = findViewById(R.id.et_password);tvResult = findViewById(R.id.tv_result);loadingDialog = new ProgressDialog(this);loadingDialog.setMessage("登录中...");// 创建Presenter,传入View接口presenter = new LoginPresenter(this);// 登录按钮点击(View只通知Presenter,不处理逻辑)findViewById(R.id.btn_login).setOnClickListener(v -> presenter.login());}// 实现LoginView接口的UI方法@Overridepublic void showLoading() {loadingDialog.show();}@Overridepublic void hideLoading() {loadingDialog.dismiss();}@Overridepublic void showResult(String msg) {tvResult.setText(msg);}@Overridepublic String getUsername() {return etUsername.getText().toString();}@Overridepublic String getPassword() {return etPassword.getText().toString();}// 生命周期管理:避免内存泄漏@Overrideprotected void onDestroy() {super.onDestroy();presenter.detachView(); // 断开Presenter与View的引用}
}
(3)Presenter 层:逻辑核心
public class LoginPresenter {// 持有View接口(而非具体Activity)和Modelprivate LoginView loginView;private LoginModel loginModel;// 弱引用(避免Presenter持有Activity导致内存泄漏)private WeakReference<LoginView> viewRef;// 构造方法:关联View和Modelpublic LoginPresenter(LoginView view) {this.viewRef = new WeakReference<>(view);this.loginModel = new LoginModel();}// 登录逻辑(核心)public void login() {LoginView view = viewRef.get();if (view == null) return;// 1. 通知View显示加载view.showLoading();// 2. 获取View的输入数据String username = view.getUsername();String password = view.getPassword();// 3. 调用Model处理登录loginModel.login(new User(username, password), new LoginModel.LoginCallback() {@Overridepublic void onSuccess(String msg) {// 4. 通知View更新结果if (viewRef.get() != null) {viewRef.get().hideLoading();viewRef.get().showResult(msg);}}@Overridepublic void onFail(String msg) {if (viewRef.get() != null) {viewRef.get().hideLoading();viewRef.get().showResult(msg);}}});}// 断开View引用(避免内存泄漏)public void detachView() {if (viewRef != null) {viewRef.clear();viewRef = null;}}
}

3.3 MVP 的核心改进与优缺点

核心改进:
  • 完全解耦:View 只做 UI,Presenter 只做逻辑,修改 UI 不影响逻辑;
  • 可测试性:Presenter 不依赖 Android 框架,可通过 JUnit 直接测试(无需启动 APP);
    // 测试Presenter(纯Java测试,不依赖Android)
    public class LoginPresenterTest {@Testpublic void testLoginSuccess() {// 模拟ViewLoginView mockView = Mockito.mock(LoginView.class);// 模拟输入Mockito.when(mockView.getUsername()).thenReturn("admin");Mockito.when(mockView.getPassword()).thenReturn("123456");LoginPresenter presenter = new LoginPresenter(mockView);presenter.login();// 验证逻辑:是否调用了显示加载和隐藏加载Mockito.verify(mockView).showLoading();Mockito.verify(mockView).hideLoading();Mockito.verify(mockView).showResult("登录成功");}
    }

  • 复用性提升:Presenter 可搭配不同 View(如用同一 LoginPresenter 支持 Activity 和 Fragment)。
缺陷:
  • 代码量增加:需定义大量接口(View 接口、回调),简单功能也需多个类;
  • 生命周期管理复杂:Presenter 需手动处理 View 的生命周期(如detachView),否则易内存泄漏;
  • 接口冗余:View 接口可能定义大量方法(如 10 个 UI 更新方法),维护成本高。
适用场景:
  • 中型项目(如多模块应用,需团队协作);
  • 需频繁迭代的项目(逻辑与 UI 分离,便于维护);
  • 对测试有要求的项目(需单元测试覆盖核心逻辑)。

四、MVVM 架构:数据驱动 UI 的响应式设计

MVVM(Model-View-ViewModel)是当前 Android 主流架构,借助 “数据绑定(DataBinding)” 和 “响应式数据(如 LiveData)” 实现 “数据驱动 UI”——UI 自动响应数据变化,无需手动调用更新方法。

4.1 MVVM 核心结构与职责

MVVM 的核心是ViewModel 与 View 的数据绑定,各层职责如下:

层级

核心职责

Android 中的载体

核心交互

Model

数据管理(与前两种架构一致)

JavaBean、Repository、Room

登录接口、数据库操作

View

UI 层(自动响应数据变化)

Activity、Fragment、XML+DataBinding

声明式绑定数据,无需手动更新

ViewModel

持有可观察数据、处理业务逻辑

ViewModel(Jetpack 组件)

调用 Model 获取数据→更新 LiveData→View 自动刷新

核心优势

  • 数据与 UI 通过 DataBinding 绑定,省去大量setText等更新代码;
  • ViewModel 生命周期独立于 Activity(屏幕旋转时不重建),数据自动保留;
  • 响应式编程(LiveData 自动通知数据变化),逻辑更清晰。

4.2 Android MVVM 的实现(登录案例)

结合 Jetpack 组件(ViewModel、LiveData、DataBinding)实现登录功能:

(1)Model 层:数据与仓库(引入 Repository 模式)
// 1. 数据实体(User.java)
public class User { ... }// 2. 数据源(登录接口,LoginDataSource.java)
public class LoginDataSource {public void login(User user, LoginCallback callback) {// 模拟网络请求(与MVP的Model一致)new Thread(() -> {try {Thread.sleep(1000);if ("admin".equals(user.getUsername()) && "123456".equals(user.getPassword())) {callback.onSuccess("登录成功");} else {callback.onFail("用户名或密码错误");}} catch (InterruptedException e) {callback.onFail("网络异常");}}).start();}public interface LoginCallback { ... }
}// 3. 仓库(统一管理数据源,LoginRepository.java)
public class LoginRepository {private static LoginRepository instance;private LoginDataSource dataSource;// 单例仓库(可同时管理网络和本地数据源)public static LoginRepository getInstance() {if (instance == null) {instance = new LoginRepository(new LoginDataSource());}return instance;}private LoginRepository(LoginDataSource dataSource) {this.dataSource = dataSource;}// 暴露登录接口给ViewModelpublic void login(User user, LoginDataSource.LoginCallback callback) {dataSource.login(user, callback);}
}
(2)ViewModel 层:持有 LiveData 与逻辑
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;public class LoginViewModel extends ViewModel {// 可观察数据(登录结果,View会自动监听)private MutableLiveData<String> loginResult = new MutableLiveData<>();// 加载状态private MutableLiveData<Boolean> isLoading = new MutableLiveData<>();// 仓库引用private LoginRepository repository;public LoginViewModel() {repository = LoginRepository.getInstance();}// 暴露给View的只读LiveData(防止View直接修改)public LiveData<String> getLoginResult() {return loginResult;}public LiveData<Boolean> getIsLoading() {return isLoading;}// 登录逻辑public void login(String username, String password) {isLoading.setValue(true); // 通知加载开始User user = new User(username, password);repository.login(user, new LoginDataSource.LoginCallback() {@Overridepublic void onSuccess(String msg) {isLoading.postValue(false); // 子线程用postValueloginResult.postValue(msg);}@Overridepublic void onFail(String msg) {isLoading.postValue(false);loginResult.postValue(msg);}});}
}
(3)View 层:DataBinding 绑定数据
<!-- activity_login.xml(启用DataBinding) -->
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"><!-- 数据变量定义 --><data><variablename="viewModel"type="com.example.mvvm.LoginViewModel" /><variablename="activity"type="com.example.mvvm.LoginActivity" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><EditTextandroid:id="@+id/et_username"android:hint="用户名"/><EditTextandroid:id="@+id/et_password"android:hint="密码"android:inputType="textPassword"/><Buttonandroid:text="登录"android:onClick="@{() -> activity.login()}"/><TextViewandroid:text="@{viewModel.loginResult}" /> <!-- 自动绑定结果 --><ProgressBarandroid:visibility="@{viewModel.isLoading ? View.VISIBLE : View.GONE}" /> <!-- 自动绑定加载状态 --></LinearLayout>
</layout>
(4)Activity:关联 ViewModel 与 DataBinding
public class LoginActivity extends AppCompatActivity {private ActivityLoginBinding binding; // DataBinding自动生成的类private LoginViewModel loginViewModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 1. 初始化DataBindingbinding = DataBindingUtil.setContentView(this, R.layout.activity_login);// 2. 获取ViewModel(通过ViewModelProvider,确保生命周期正确)loginViewModel = new ViewModelProvider(this).get(LoginViewModel.class);// 3. 绑定ViewModel到布局binding.setViewModel(loginViewModel);binding.setActivity(this);// 绑定生命周期所有者(让LiveData感知Activity生命周期)binding.setLifecycleOwner(this);}// 登录按钮点击(调用ViewModel的登录方法)public void login() {String username = binding.etUsername.getText().toString();String password = binding.etPassword.getText().toString();loginViewModel.login(username, password);}
}

4.3 MVVM 的核心优势与优缺点

核心优势:
  • 数据驱动 UI:通过 LiveData+DataBinding,数据变化自动更新 UI,省去runOnUiThread和setText;
  • 生命周期安全:ViewModel 在屏幕旋转时不重建(数据保留),避免重复请求网络;
  • 低耦合:View 只绑定数据,ViewModel 只处理逻辑,Model 只管数据,修改 UI 不影响逻辑;
  • 可测试性:ViewModel 独立于 Android 框架,可直接测试(如验证登录逻辑是否正确更新 LiveData)。
缺陷:
  • 学习成本高:需掌握 DataBinding、LiveData、ViewModel 等 Jetpack 组件;
  • 调试难度增加:数据绑定是黑盒操作,UI 异常时需排查绑定关系;
  • 简单功能冗余:小功能(如单个按钮)用 MVVM 显得繁琐。
适用场景:
  • 大型项目(如电商 APP、社交 APP,需长期维护);
  • 频繁更新 UI 的场景(如列表刷新、实时数据展示);
  • 团队协作项目(架构规范统一,新人易接手)。

五、三种架构对比与选择指南

维度

MVC

MVP

MVVM

核心思想

分层但 View 与 Controller 耦合

Presenter 中间层解耦

数据绑定 + 响应式数据驱动

代码量

少(无额外接口)

中(需定义 View 接口)

多(需 ViewModel 和绑定)

耦合度

高(Activity 承担多重职责)

中(接口交互,需手动管理)

低(数据绑定,自动响应)

可测试性

低(需依赖 Activity)

高(Presenter 可独立测试)

高(ViewModel 独立测试)

维护成本

高(后期改不动)

中(接口清晰但需维护)

低(分层明确,数据驱动)

Android 适配

原生支持(简单但粗糙)

需手动实现接口和生命周期管理

依赖 Jetpack(官方推荐)

5.1 架构选择建议

  1. 按项目规模选择
  • 小型项目(<5 个 Activity)→ MVC(快速开发);
  • 中型项目(5-20 个页面)→ MVP(平衡开发效率和维护性);
  • 大型项目(>20 个页面)→ MVVM(长期维护,团队协作)。
  1. 按团队情况选择
  • 新手团队→ MVC(降低学习成本);
  • 有经验团队→ MVVM(利用 Jetpack 提升效率)。
  1. 按功能复杂度选择
  • 简单功能(如设置页面)→ MVC 或 MVP;
  • 复杂功能(如首页列表 + 购物车 + 实时消息)→ MVVM。

六、架构设计的本质:灵活应变

无论 MVC、MVP 还是 MVVM,都不是 “银弹”。实际开发中不必严格遵守某一种架构,可根据需求混合使用:

  • 小型项目用 MVC,但抽取工具类减少 Activity 代码;
  • MVP 中引入 DataBinding 简化 View 更新;
  • MVVM 中保留 Presenter 的部分逻辑(如复杂表单校验)。

架构的本质是 “解决当前问题”—— 能让团队高效开发、代码易于维护的就是好架构。随着项目演进,架构也可逐步升级(如从 MVC 重构为 MVVM),关键是保持 “分层清晰、职责单一” 的核心原则。

掌握这三种架构后,你会发现:优秀的 Android 代码不是 “堆功能”,而是通过合理设计让每一行代码都有明确的位置和职责 —— 这也是从 “会写代码” 到 “能设计系统” 的关键一步。

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

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

相关文章

使用 gh-pages 将 next.js15 静态项目部署到 github pages

以下我使用 next.js15 写的 Todo List 为例,假设我们本地已经存在一个 next.js15 的 Todo List 项目。 说明:解决了项目部署到 github pages 后访问不到 css、js、字体以及访问不到 public 目录下的图片问题。 第一步 安装 gh-pages: npm i gh-pages第二步 在 public 目…

rename系统调用及示例

21. rename - 重命名文件或目录 函数介绍 rename系统调用用于重命名文件或目录&#xff0c;也可以将文件或目录移动到另一个位置。如果目标文件已存在&#xff0c;则会被替换。 函数原型 #include <stdio.h>int rename(const char *oldpath, const char *newpath);功能 将…

PHP框架之Laravel框架教程:3. 数据库操作(简要)

3. 数据库操作&#xff08;简要&#xff09; 配置 数据库的配置文件在 config/database.php 文件中&#xff0c;你可以在这个文件中定义所有的数据库连接配置&#xff0c;并指定默认的数据库连接。这个文件中提供了大部分 Laravel 能够支持的数据库配置示例。 mysql > [driv…

项目七.AI大模型部署

环境准备此处使用的是rock linux8.9操作系统k8s集群三个设备&#xff0c;使用centos7.9操作系统设备配置##上传ollama工具的压缩包 [rootproject ~]# ll total 1497732 -rw-r--r-- 1 root root 1533674176 Jul 21 11:27 ollama-linux-amd64.tgz [rootproject ~]# tar -C /usr -…

Oracle 19C RU 19.28 升级和安装

背景介绍 概述 本次升级包括安全漏扫中所有19c数据库,漏扫预警版本19.3到19.27各个版本,数据库需要升级至19.28版本满足安全要求。 原端19C 升级目标端19.28 db_name racdb racdb ORACLE_SID racdb1/2 racdb1/2 ORACLE_HOME GI:/oracle/asm DB:/oracle/db GI:/orac…

嵌入式学习日志————对射式红外传感器计次

前言这是第二次学习这部分内容了&#xff0c;第一次是大一上学期&#xff0c;因为大二下忙着其他事一直没来得及吧STM32学完&#xff0c;所以假期从头开始再学&#xff0c;比第一次也有了更深的理解&#xff0c;以下内容均是看【STM32入门教程-2023版 细致讲解 中文字幕】https…

ONLYOFFICE深度解锁系列.13-如何复制、重新排序 PDF 页面:onlyoffice 9.0.3 新功能

在处理合同、讲义、研究资料或扫描文档时&#xff0c;保持页面顺序井然尤为重要。有时文件页数繁多、排序混乱或缺少逻辑&#xff0c;这不仅影响阅读体验&#xff0c;也不利于后续使用或分享。好消息是&#xff0c;借助 ONLYOFFICE PDF 编辑器&#xff0c;您可以轻松拖拽页面&a…

vue递归树形结构删除不符合数据 生成一个新数组

首先看一下数据结构&#xff08;我的是路由菜单&#xff09;{"code": 200,"message": "请求成功!","success": true,"data": [{"startDate": null,"endDate": null,"createTime": "2023…

【机器学习之推荐算法】基于K最近邻的协同过滤推荐与基于回归模型的协同过滤推荐

基于K最近邻的协同过滤推荐 基于K最近邻的协同过滤推荐其实本质上就是MemoryBased CF&#xff0c;只不过在选取近邻的时候&#xff0c;加上K最近邻的限制。 这里我们直接根据MemoryBased CF的代码实现 修改以下地方 class CollaborativeFiltering(object):based Nonedef __ini…

望言OCR视频字幕提取2025终极评测:免费版VS专业版提全方位对比(含免费下载)

大家好&#xff0c;欢迎来到程序视点&#xff01;我是你们的老朋友.小二&#xff01;一、产品定位&#xff1a;AI时代的视频字幕处理专家望言OCR作为专业的视频硬字幕提取工具&#xff0c;在AI视频处理领域占据重要地位。最新评测显示&#xff0c;其免费版本依然保持着惊人的97…

Matplotlib(二)- Matplotlib简单绘图

文章目录一、pyplot模块介绍二、Matplotlib简单绘图1. 绘制折线图1.1 折线图介绍1.2 plt.plot()函数介绍1.3 绘制简单折线图1.3.1 绘制单条折线图1.3.2 绘制多条折线图1.4 示例&#xff1a;绘制天气气温折线图2. 绘制柱形图2.1 柱形图介绍2.2 plt.bar()函数介绍2.3 绘制柱形图2…

【世纪龙科技】数字化技术解锁新能源汽车电驱动总成装调与检修

随着新能源汽车产业加速升级&#xff0c;电驱动总成装调与检修技术已成为职业院校汽车专业教学的核心挑战。传统实训模式面临设备投入高、更新周期长、高压操作安全隐患多、教学与产业需求脱节等现实问题&#xff0c;导致学生实践能力培养滞后于行业发展。如何通过数字化手段突…

springboot基于Java与MySQL库的健身俱乐部管理系统设计与实现

用户&#xff1a;注册&#xff0c;登录&#xff0c;健身教练&#xff0c;健身课程&#xff0c;健身器材&#xff0c;健身资讯&#xff0c;课程报名管理&#xff0c;教练预约管理&#xff0c;会员充值管理&#xff0c;个人中心管理员&#xff1a;登录&#xff0c;个人中心&#…

如何修改debian的ip地址

编辑配置文件&#xff1a; sudo nano /etc/network/interfaces修改内容&#xff08;示例将 eth0 设为静态IP&#xff09;&#xff1a; auto eth0 iface eth0 inet static address 192.168.1.100 netmask 255.255.255.0 gateway 192.168.1.1 dns-nameservers 8.8.8.8 8.8.4.4 #…

haproxy七层代理(知识点+相关实验部署)

目录 1.负载均衡介绍 1.1 什么是负载均衡 1.2 为什么用负载均衡 1.3 负载均衡类型 1.3.1 四层负载均衡 1.3.2 七层负载均衡 1.3.3 四层和七层的区别 2.haproxy简介 2.1 haproxy主要特性 2.2 haproxy的优点与缺点 3.haproxy的安装和服务信息 3.1 实验环境 3.1.1 hap…

【集合】JDK1.8 HashMap 底层数据结构深度解析

一、核心数据结构&#xff1a;为什么是 "数组 链表 红黑树"&#xff1f;​HashMap 的底层设计本质是用空间换时间&#xff0c;通过哈希表的快速定位特性&#xff0c;结合链表和红黑树处理冲突&#xff0c;平衡查询与插入效率。​1.1 基础容器&#xff1a;哈希桶数组…

【element-ui】HTML引入本地文件出现font找不到/fonts/element-icons.woff

文章目录目录结构问题复现解决办法目录结构 |-web|- public|- lib|- ...|- index.htmlindex.html <!DOCTYPE html> <html> <head><meta charset"UTF-8"><!-- import CSS --><link rel"stylesheet" href"./public/…

Windows|CUDA和cuDNN下载和安装,默认安装在C盘和不安装在C盘的两种方法

本篇文章将详细介绍在Windows操作系统中配置CUDA和cuDNN的步骤。通过本教程&#xff0c;您将能够轻松完成CUDA和cuDNN的安装、环境变量配置以及与深度学习框架&#xff08;如TensorFlow和PyTorch&#xff09;兼容性测试&#xff0c;从而为您的深度学习项目提供强大的硬件支持。…

Vue 项目动态接口获取翻译数据实现方案(前端处理语言翻译 vue-i18n)

在大型多语言项目中&#xff0c;将翻译数据硬编码在项目中往往不够灵活。通过接口动态获取翻译数据&#xff0c;并结合本地缓存提升性能&#xff0c;是更优的国际化实现方式。本文将详细介绍如何在 Vue 项目中实现这一方案。 方案优势 灵活性高&#xff1a;翻译内容更新无需修改…

Mybatis-plus多数据源

适用于多种场景&#xff1a;纯粹多库、 读写分离、 一主多从、 混合模式等目前我们就来模拟一个纯粹多库的一个场景&#xff0c;其他场景类似场景说明&#xff1a;我们创建两个库&#xff0c;分别为&#xff1a; mybatis_plus&#xff08;以前的库不动&#xff09;与my…