news 2026/10/17 7:55:09

SpringBoot+Vue3+MyBatis设备管理系统源码解析:从数据库设计到部署实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+Vue3+MyBatis设备管理系统源码解析:从数据库设计到部署实战

前阵子接手了一个做精密零部件加工的客户项目,对方几百台设备的点检记录、维修历史、保养计划全部塞在Excel里,车间主任每次提"这个月哪些设备该保养了"都要翻半小时表格。帮他们做设备管理系统源码梳理的时候,我就意识到——中小企业的设备管理压根不需要动辄几十万的EAM系统,他们要的就是一套SpringBoot+Vue3+MyBatis搭建的轻量级管理工具,能管台账、能留记录、能提醒保养、能查报表,前后端分离、MySQL做存储,跑在普通服务器上稳如老狗就够了。

这篇就结合我完成的那套前后端分离源码,把设备管理系统从需求拆解、数据库设计、后端实现、前端关键路径、部署避坑到二次开发思路,完整过一遍。适合正打算自研设备管理系统的团队,也适合拿到源码后不知道怎么改、不知道从哪下手的朋友。

1. 先搞清楚这套系统到底解决什么:设备管理需求拆解

1.1 Excel台账时代的三个典型痛点

很多中小企业不是没有管理意识,而是工具撑不起意识。最常见的状态是:设备台账确实有,但只有一份Excel,存在车间主任电脑里;点检表是每天打印出来签字,签完塞进文件柜;维修记录靠维修工自己记忆"这台机器去年好像修过两次"。一旦设备报废、转卖或盘点,账实对不上是常态。

我梳理需求时把这些问题归纳成三类:台账分散导致设备编号混乱、存放位置缺失、供应商信息断档;流程断裂导致点检结果没人汇总、维修记录关联不上设备、保养计划全凭经验;数据无沉淀导致设备故障率、维修成本、保养准时率完全算不出来。这三类问题,正好对应这套系统要解决的三个核心模块:设备档案、点检维修保养流程、统计报表。

1.2 功能清单:够用而不臃肿

对照上面三个痛点,我划定的功能边界很明确,绝对不做大而全:

  • 设备档案管理:新增、编辑、停用、报废、调拨,支持按设备编号、名称、部门、状态多条件筛选;
  • 点检管理:按设备配置点检项,生成每日/每周点检任务,手机或PC端提交结果,异常自动转维修工单;
  • 维修工单:从点检异常或手动报修进入工单流程,记录维修人、维修内容、耗时、更换备件、费用;
  • 保养计划:按时间周期或运行时长生成保养提醒,执行后登记保养记录;
  • 备件库存:备件基础档案、入库出库流水、库存预警;
  • 统计报表:设备台账导出、维修费用统计、故障率排行、点检完成率;
  • 组织权限:管理员、设备专员、维修工、普通员工四种角色,菜单和按钮两个维度控制。

这套功能清单里,没有做复杂的审批流、没有对接财务资产模块、没有做移动端APP。不是因为难,而是中小企业根本用不上,过度设计反而是负担。等系统跑起来,用户自然会提"能不能加一个XX",那个时候再迭代,比一开始猜需求准得多。

1.3 四个角色的入口差异

权限设计上我按真实岗位切分:管理员看全局,管用户、管基础数据、管所有配置;设备专员是日常操作主力,维护台账、派发维修单、登记保养执行;维修工权限最窄,只能看到分配给自己的工单和点检任务,提交处理结果;普通员工只读,主要用来查设备位置和技术资料。这套RBAC模型用后端一张角色表加一张菜单权限表就撑起来了,前端再配合动态路由和按钮级指令,能挡住绝大多数误操作。

2. 技术栈取舍:为什么偏偏是SpringBoot+Vue3+MyBatis

2.1 后端框架:SpringBoot是省心选项,但版本要卡好

选SpringBoot的原因不用多讲,主流、稳定、招人容易。这里我多说一句版本选择:这套源码用的SpringBoot 2.7.18配合JDK 8,不是不追新,而是中小企业客户的生产服务器上普遍跑着JDK 8,SpringBoot 2.7是最后一条完整支持JDK 8的版本线,部署时少踩很多坑。如果你的环境已经是JDK 17以上,用SpringBoot 3.x也没问题,但要注意Jakarta命名空间的变化,旧代码里的javax.*要批量替换。

2.2 ORM选型:MyBatis的动态SQL在场景查询里的天然优势

为什么不用JPA?设备管理系统的查询接口有个特点:条件组合极多且不规则。设备列表可能要同时按部门、设备状态、设备类型、购入年份、责任人筛选,这种多条件动态拼接的场景,MyBatis的<if>标签就像拼积木一样直观可控。JPA的Specification也能做,但是写起来绕,排查SQL问题还得翻译JPQL,远不如直接看XML里的SQL来得爽利。

项目里没用MyBatis-Plus,虽然它提供单表CRUD很方便,但设备管理里大量查询是跨四五张表的关联查询,单表拼接只是辅助。用原生MyBatis写关联SQL更清晰,也更容易让后续接手的人理解。当然如果你拿到的是带MyBatis-Plus的版本,核心逻辑是一样的,不用纠结。

2.3 前端框架:Vue3的组合式API更适合业务复杂的页面

Vue3相较Vue2最大的提升就是组合式API。设备点检页面要同时处理表单校验、历史记录列表、异常弹窗、实时保存多个状态,在Vue2的Options API里会拆成data、methods、computed四处,逻辑分散;换成Vue3的setup函数后,同一块业务的state和函数能聚在一起,维护起来舒服太多了。配套我选了Vite做构建工具,开发启动秒开,构建速度也比Webpack时代的记忆明显快一个量级。UI组件库直接上Element Plus,表格、表单、弹窗、日期选择这些设备管理页面高频组件全都现成。

2.4 鉴权方案:JWT+拦截器,不引重型框架

中小企业后台系统的鉴权不需要Spring Security那种重量级方案,一套JWT加HandlerInterceptor足够。Token里只放用户ID、用户名、角色编码,有效期设12小时,前端每次请求放到Header的Authorization里,后端拦截器校验通过后把用户信息塞进ThreadLocal,接口层直接取。整个认证流程代码量不到两百行,好懂也好维护。过分追求技术复杂度的项目基本都死了,团队维护不动。

3. 数据库设计:把设备管理的表一张张画清楚

3.1 设备档案表:台账的核心底座

设备档案是最重要的表,几乎其他所有业务表都外键关联它的device_id。我建议的核心字段组是:设备编号、设备名称、规格型号、设备类型(车床/铣床/空压机等)、所在部门、安装位置、供应商、出厂编号、购入日期、原值、折旧年限、状态(运行/停机/维修中/已报废/已封存)、责任人、备注。

设备编号一般不设自增ID,而是用统一的编码规则生成,比如SB-2024-0001格式,按月或按类型递增。这样做的原因是设备编号要体现到点检表、维修单、二维码标签上,人眼扫一遍就能知道大致信息,纯数字自增ID在车间场景里非常不友好。

3.2 点检、维修、保养:三条业务线的表结构

点检记录表需要记录:所属设备、点检日期、点检人、每项点检内容的结果(正常/异常)、异常描述、处理状态。为了支持"每台设备点检项不同",我再拆一张子表保存设备对应的点检项列表,这样换人点检时前端能动态渲染出对应的检查项目。

维修工单表的核心字段是:单号、设备ID、报修人、报修时间、故障现象、维修人、维修开始结束时间、故障原因、维修措施、更换备件明细、维修费用、工单状态。更换备件明细如果存在主表里就太傻了,我归到一张工单备件关系子表里,关联备件ID和数量,这样备件库存扣减逻辑能跟着工单走。

保养计划表更偏向配置属性:设备ID、保养周期类型(按日历日/按累计运行时长)、周期值、上次保养日期、下次保养日期、保养内容模板、执行人。系统靠这张表的next_date字段跑定时任务,每天扫一次,把到期的设备信息生成待办记录。

3.3 备件库存与流转记录

备件管理要拆两张表:备件档案表和库存流水表。档案表记录备件名称、规格、适用设备类型、供应商、安全库存值、当前库存量;流水表记录每一次入库、出库、盘点调整的动作,关联操作人和工单号,这样才能回答"某个备件用到哪里去了"。

库存字段不需要实时计算,写入流水的时候同步更新档案表的stock_quantity字段即可,查询时直接读档案表,不用SUM流水表,性能好很多。库存数字是否准确靠定期盘点纠正,而不是靠事务锁来硬刚并发。

3.4 用户权限表:RBAC的落地方式

标准五张表:用户表、角色表、菜单表、用户角色关系表、角色菜单关系表。用户表存账号、密码(BCrypt加密)、姓名、手机号、状态、部门;菜单表用父子结构,对应前端路由路径和按钮标识。

我实战中的体会是,菜单表不要做得太深,两级就够——一级菜单对应左侧导航,二级菜单或按钮对应页面路由和操作权限。设备管理系统就三十来个功能点,多了纯属自找麻烦。

3.5 建表时容易犯的五个错

这五个错是我在真实项目里反复见到过的:

  1. 时间字段用VARCHAR存,导致区间筛选和排序全乱,一律用DATETIME;
  2. 状态字段不设默认值,新增设备忘了选状态,列表里出现空状态;
  3. 不建索引,设备表几千行可能没问题,但点检记录表跑到几十万行之后,按设备ID和时间查询直接卡死,联合索引(device_id, check_date)必须有;
  4. 统一用text类型存金额字段,报表SUM时隐式转换全错,金额用DECIMAL(10,2);
  5. 外键约束乱加,设备被维修记录引用时无法删除,其实业务上就是不能直接删设备的,应该在代码层面提示"该设备存在运维记录,请改为停用"而不是靠外键报错给用户看。

4. 后端落地:SpringBoot项目的分层与关键接口

4.1 工程结构与入口说明

后端项目的包结构我习惯按"controller / service / mapper / domain / common"来分。controller收参数、做鉴权和参数校验,service写业务逻辑,mapper只做数据库交互,domain放实体类,common放统一返回、异常、工具类。

启动类用@SpringBootApplication扫包,需要注意MyBatis的Mapper扫描不要遗漏。我用的配置是:

@MapperScan("com.example.device.mapper") @SpringBootApplication public class DeviceSystemApplication { public static void main(String[] args) { SpringApplication.run(DeviceSystemApplication.class, args); } }

application.yml里还有一个关键配置,就是日期格式和时区,不处理好的话前端看到的创建时间总是差八小时:

spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai

4.2 统一返回与全局异常:先打好地基

接口返回格式如果不统一,前端写封装时会疯掉。我的返回结构就三个字段:code、msg、data,code为200是成功,401是未登录或Token过期,500是业务或系统异常。对应的Result类很简单:

public class Result<T> { private Integer code; private String msg; private T data; public static <T> Result<T> ok(T data) { Result<T> r = new Result<>(); r.code = 200; r.msg = "success"; r.data = data; return r; } public static Result<?> error(Integer code, String msg) { ... } }

全局异常处理我用了@RestControllerAdvice加@ExceptionHandler,捕获两类异常:业务异常(我在service里主动抛的BizException)和系统异常(NullPointerException这类)。业务异常返回code 500加自定义提示,系统异常返回code 500加统一文案,同时把堆栈打到日志里。这个机制投入很低,但能让前端拿到稳定的错误格式,值得先做。

4.3 JWT登录流程:从验密到放行

登录接口的逻辑串起来是:接收账号密码、校验验证码是否手动跳过、用BCrypt比对密码、查到用户信息后生成Token返回、同时在Redis里记录Token的签发时间和盐值(便于登出时秒退)。

生成Token我用jjwt库,核心代码量很小:

String token = Jwts.builder() .setSubject(String.valueOf(user.getId())) .claim("username", user.getUsername()) .claim("role", user.getRoleCode()) .setExpiration(new Date(System.currentTimeMillis() + 12 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact();

拦截器这边有一个关键点:排除登录接口和静态资源,否则用户还没登录就被拦了。我用的是WebMvcConfigurer注册拦截器,excludePathPatterns配置好/auth/login、/favicon.ico等路径。同时拦截器里解析Token失败时直接抛401异常,由全局异常处理返回给前端,前端再统一跳登录页。

4.4 MyBatis多条件查询的XML写法

设备列表这种查询接口,核心就是拼动态条件。我在DeviceMapper.xml里的写法是这样的:

<select id="selectDeviceList" resultType="com.example.device.domain.DeviceInfo"> SELECT d.id, d.device_no, d.device_name, d.model, d.type_code, d.dept_name, d.status, d.responsible_name, d.buy_date, d.created_time FROM device_info d <where> <if test="deviceNo != null and deviceNo != ''"> AND d.device_no LIKE CONCAT('%', #{deviceNo}, '%') </if> <if test="deviceName != null and deviceName != ''"> AND d.device_name LIKE CONCAT('%', #{deviceName}, '%') </if> <if test="status != null and status != ''"> AND d.status = #{status} </if> <if test="deptId != null and deptId != ''"> AND d.dept_id = #{deptId} </if> <if test="typeCode != null and typeCode != ''"> AND d.type_code = #{typeCode} </if> </where> ORDER BY d.id DESC </select>

用<where>标签最舒服的一点是,如果所有条件都为空,它自己会去掉前缀AND,SQL不会语法错误。LIKE CONCAT拼接是防注入的标准写法,别把用户输入直接拼进SQL字符串里。

4.5 分页、导入导出这些通用能力

分页我直接用PageHelper,三行搞定:

PageHelper.startPage(pageNum, pageSize); List<DeviceInfo> list = deviceMapper.selectDeviceList(query); PageInfo<DeviceInfo> pageInfo = new PageInfo<>(list);

这里有个容易踩的坑:PageHelper.startPage后面必须紧跟Mapper查询,中间不要穿插其他数据库操作,否则分页线程池会串页。多表关联查询也一样能用,但count语句偶尔会因复杂SQL统计不准,解决办法是手写sql="..."自定义count查询。

导出Excel用的是EasyExcel,实体类上加@ExcelProperty注解标注列名。导出接口在数据量大的时候不要直接用同步返回,设备管理场景一般几千条问题不大,如果上万条建议后端生成临时文件、前端轮询下载,这个接口设计思路要提前想明白。

5. 前端关键路径:Vue3从登录页到业务页面

5.1 项目初始化与依赖清单

前端我用的Vite创建Vue3工程,核心依赖是:vue-router、pinia、axios、element-plus、@element-plus/icons-vue、sass。组件库引入方式可以直接全局注册,省事:

import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' app.use(ElementPlus)

全局注册虽然打包体积会比按需引入大一点,但设备管理这种后台系统不追求首屏极致性能,换来的是不用每写一个页面就手动引入一次表格或弹窗,开发效率优先。

5.2 axios封装:token注入、401统一处理

axios封装是前端工程质量的分水岭。我在request.js里做了三件事:请求拦截器把localStorage里的Token塞进Authorization头;响应拦截器解包Result结构,code不为200时统一弹ElMessage提示;遇到401时清空本地登录信息并跳转登录页。

核心框架长这样:

service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = token } return config }) service.interceptors.response.use(res => { const r = res.data if (r.code === 200) { return r.data } if (r.code === 401) { localStorage.clear() router.push('/login') } ElMessage.error(r.msg || '系统异常') return Promise.reject(new Error(r.msg)) })

一个小提醒:不要把Token也放进Result.data里一起返回,Token应该在登录接口返回后单独存,避免响应拦截器把登录接口的data也解包掉一层,搞混逻辑。

5.3 路由权限:动态菜单是怎么来的

动态路由的实现我简化为三步:登录成功后请求/auth/menus接口拿到当前角色可见的菜单列表;格式化成一棵路由树;用router.addRoute逐个注册到Vue Router。这样不同角色登录后,地址栏能直接敲路径的页面不一样,左侧菜单也是从这棵路由树渲染出来的。

按钮级权限我用了一个自定义指令v-permission,指令的值是权限标识,比如v-permission="'device:export'",指令内部查一下当前用户在登录时存进Store的权限集合,没有就移除该DOM节点。

5.4 设备列表与点检页面的组件化思路

设备列表页我拆成了四个组件:SearchBar(搜索条件区)、DataTable(表格区)、DeviceFormDialog(新增/编辑弹窗)、DetailDrawer(抽屉展示详情)。业务组件间的状态放在父页面,用ref和defineExpose传递和调用子组件方法。

点检页面有个特殊性:它需要先选设备,再动态加载该设备的点检项,然后逐项提交结果。点检项用el-checkbox-group不适合,因为每项还可能要填描述和结果判定,我用的方案是el-form加v-for渲染点检项数组,每项一个结果下拉框加一个备注输入框,整体作为一个数组提交后端批量保存。批量保存的接口记得用事务,要么全成功要么全回滚。

6. 部署上线与踩坑实录:本地跑通不等于能用

6.1 开发环境跨域:Vite proxy怎么配

开发环境的前后端分离跨域,最省心的是用Vite的代理,前端地址访问/api前缀的接口时自动转发到后端8080端口。配置在vite.config.js里:

server: { port: 3000, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true, rewrite: path => path.replace(/^\/api/, '') } } }

这样做的核心好处是,前端写请求时用的相对路径/api/device/list,生产环境把它交给Nginx代理即可,前端代码零改动。

6.2 生产环境:Nginx反向代理前后端分离部署

生产环境我推荐jar包加Nginx的方式。后端构建成jar用java -jar device-system.jar跑起来,前端npm run build生成dist目录,Nginx配置把静态目录指到dist,把/api前缀的请求反代到后端的8080。一个精简的Nginx配置核心段:

server { listen 80; server_name your-domain.com; root /opt/device/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }

注意proxy_pass后面的斜杠,写http://127.0.0.1:8080/会把/api前缀剥掉,写不带斜杠的http://127.0.0.1:8080则会带着/api转发。后端接口路径如果本来就带/api前缀,就选择不带斜杠的写法,这个细节能让你少调一晚上。

6.3 我踩过的三个典型坑

第一个是MySQL时区问题,连接串里没加serverTimezone=Asia/Shanghai,导致所有DATETIME字段查询出来都差8小时,前端凌晨提交的点检记录显示成了前一天晚上。连接串补上参数立刻解决。

第二个是Linux下MySQL表名大小写敏感,数据库里表叫device_info,Java实体类映射的注解或XML里写成了Device_Info,Windows本地跑得好好的,一部署到Linux上就报"Table doesn't exist"。这里要求统一规范:所有表名小写,XML里的SQL与表名严格一致,别依赖环境差异。

第三个是上传图片路径问题,设备照片上传功能在本地路径写/data/upload没毛病,但部署到云服务器后目录没创建,FileOutputStream抛异常。我在代码里改成了启动时自动创建目录:

File dir = new File(uploadPath); if (!dir.exists()) { dir.mkdirs(); }

同时Nginx里加了一个/uploads/的静态映射,用户的图片走Nginx读文件而不是经过Java应用转发,性能差别很大。

6.4 上线前必须做的小优化

上线前建议做三件事:第一,给MySQL做每日自动备份脚本,设备台账数据丢了很难重建,备份是最低成本的保险;第二,后端启动参数加-Xms512m -Xmx1024m,避免内存分配策略不当导致莫名其妙的性能波动;第三,观察一段时间后给高频查询建索引——维护记录表的查询大多按设备ID和时间范围,加联合索引收益最明显。

7. 拿到源码之后:二次开发的关键路径

7.1 第一步不是改代码,而是先跑起来

我见过太多人拿到源码第一件事就是改Logo、改数据库密码,然后跑不起来就开始怀疑人生。正确的顺序是:先按文档建库、导入初始数据、启动后端、启动前端,用管理员账号登录进去,把每个页面点一遍。这一步的目的不是验收,而是建立"改代码前系统是完好状态"的基线。之后再任何改动,出问题都能对比回退。

7.2 按业务加字段的常规套路

设备管理系统用到半年,你最可能收到的需求是"这个页面上加个XX字段"。这个改动横跨六层:数据库表加列、实体类加属性、Mapper XML的resultMap和insert/update语句加字段、前端表单加组件、列表列加表头、导出Excel的映射加注解。这套链路走熟后,一个字段从表到页面全打通通常不超过二十分钟。

这里有个习惯建议:加字段时顺手把remark字段设成可选项,很多中小企业的特殊需求其实能用备注兜住,灵活度远高于再拉一个新字段。

7.3 从新增接口到新页面,五步走

如果业务继续膨胀,你要新增一个完整模块,我建议按这个节奏:先在数据库里建表并插入菜单记录;后端写实体、Mapper、Service、Controller四层;前端在路由文件里把组件路径挂上;在菜单表关联好角色权限;最后用管理员账号刷新看菜单。每一步的验证点都很明确:表结构对、接口通、页面出、权限控。整个流程走顺之后,新模块的开发速度会快得超出预期。

7.4 维护这个系统的一些习惯

源码维护期最怕"裸奔"。我给自己定了几条规矩:所有数据库变更脚本单独存一个schema_migration目录,按日期命名,绝不直接手改生产库;每次发布前打一个git tag,出问题能一键回退上次版本;后端日志按天滚动,保留30天,排查线上问题时日志是唯一可信的证据链;再忙也要每季度做一次数据备份恢复演练,确认备份文件真的能用,而不是备份了个寂寞。

这套SpringBoot+Vue3+MyBatis的骨架其实只是设备管理的起点,真正常态化运转靠的是数据积累。系统跑得越久,点检记录、维修成本和故障率统计越有参考价值,那时候你回看最初的Excel台账,会庆幸当初做了这个从0到1的投入。源码最大的意义从来不是直接能用,而是它把"设备管理"这件事从拍脑袋变成了有数据支撑。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 7:53:43

AI文献综述助手:从文献聚类到引用溯源的科研效率革命

科研路上最磨人的环节&#xff0c;我觉得十有八九会有人投票给文献综述。不是读文献本身累&#xff0c;而是从几百篇论文里理出脉络、找到研究空隙、再组织成有逻辑的论述&#xff0c;这一套流程太耗心气了。我去年写一篇方法论方向的综述&#xff0c;光整理某条研究分支的演变…

作者头像 李华
网站建设 2026/10/17 7:53:23

AI Agent知识管理实战:从文档清洗到混合检索的落地指南

1. AI Agent为什么绕不开知识管理&#xff1a;先把"记性"这件事想清楚1.1 从"有问必答"到"有据可循"&#xff1a;Agent的能力边界取决于知识层我做了两年多企业级的AI Agent项目&#xff0c;最常被客户问到的一句话是&#xff1a;"为什么我…

作者头像 李华
网站建设 2026/10/17 7:53:10

C++任务并行编程:从future嵌套到任务图调度,Taskflow实战解析

很多熟悉我的朋友都知道&#xff0c;我最近在逐条补 CppCon 2025 的录像。今年这场关于 Taskflow 的报告排在我看单靠前的位置&#xff0c;倒不是因为提前知道内容有多惊艳&#xff0c;而是我手上正好有个项目&#xff0c;需要把一批带依赖关系的计算调度到多核上。之前我都是用…

作者头像 李华
网站建设 2026/10/17 7:52:49

上银Lightening 0.193伺服设置软件:D2驱动器调试与参数整定实战

简介&#xff1a;上银Lightening 0.193伺服设置软件多语言版&#xff0c;是专为上银D2系列伺服驱动器开发的配置工具&#xff0c;适用于自动化设备调试、维护与故障排查等工程场景。软件支持RS-485、CAN总线等通讯方式&#xff0c;可精细调整电流限制、速度限制、位置增益、电流…

作者头像 李华
网站建设 2026/10/17 7:52:21

Workflow-DSL实战:AI工作流可视化编排与零代码改造

做AI应用开发的朋友&#xff0c;一定有过这种体验&#xff1a;需求方说“帮我加一个判断&#xff0c;如果用户情绪比较差&#xff0c;就先转人工”&#xff0c;你算了算&#xff0c;要从主流程代码里找到那一段if-else&#xff0c;改完测试&#xff0c;发布&#xff0c;结果他又…

作者头像 李华
网站建设 2026/10/17 7:52:18

ArrayList深度解析:从底层原理到实战避坑

学 Java 集合框架的人&#xff0c;基本都会在某一天和 ArrayList 打个照面。我自己的打卡记录里&#xff0c;ArrayList 刚好排在第二十九天&#xff0c;说巧也巧——它既不像数组那样“裸奔”&#xff0c;也没有后续那些并发容器那样自带层层锁&#xff0c;但几乎每个项目里都有…

作者头像 李华