SpringBoot 整合 Excel 轻松实现数据自由导入导出
01、背景介绍
在实际的业务系统开发过程中,操作 Excel 实现数据的导入导出基本上是个非常常见的需求。
之前,我们有介绍一款非常好用的工具:EasyPoi,有读者提出在数据量大的情况下,EasyPoi 会占用内存大,性能不够好,严重的时候,还会出现内存异常的现象。
今天我给大家推荐一款性能更好的 Excel 导入导出工具:EasyExcel,希望对大家有所帮助!
easyexcel 是阿里开源的一款 Excel导入导出工具,具有处理速度快、占用内存小、使用方便的特点,底层逻辑也是基于 apache poi 进行二次开发的,目前的应用也是非常广!

相比 EasyPoi,EasyExcel 的处理数据性能非常高,读取 75M (46W行25列) 的Excel,仅需使用 64M 内存,耗时 20s,极速模式还可以更快!

废话也不多说了,下面直奔主题!
02、方案实践
在 SpringBoot 项目中集成 EasyExcel 其实非常简单,仅需一个依赖即可。
<!--EasyExcel相关依赖-->
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version>
</dependency>
EasyExcel 的导出导入支持两种方式进行处理
-
第一种是通过实体类注解方式来生成文件和反解析文件数据映射成对象
-
第二种是通过动态参数化生成文件和反解析文件数据
下面我们以用户信息的导出导入为例,分别介绍两种处理方式。
2.1、简单导出
首先,我们只需要创建一个UserEntity用户实体类,然后添加对应的注解字段即可,示例代码如下:
public class UserWriteEntity {@ExcelProperty(value = "姓名")private String name;@ExcelProperty(value = "年龄")private int age;@DateTimeFormat("yyyy-MM-dd HH:mm:ss")@ExcelProperty(value = "操作时间")private Date time;//set、get...
}
然后,使用 EasyExcel 提供的EasyExcel工具类,即可实现文件的导出。
public static void main(String[] args) throws FileNotFoundException {List<UserWriteEntity> dataList = new ArrayList<>();for (int i = 0; i < 10; i++) {UserWriteEntity userEntity = new UserWriteEntity();userEntity.setName("张三" + i);userEntity.setAge(20 + i);userEntity.setTime(new Date(System.currentTimeMillis() + i));dataList.add(userEntity);}//定义文件输出位置FileOutputStream outputStream = new FileOutputStream(new File("/Users/panzhi/Documents/easyexcel-export-user1.xlsx"));EasyExcel.write(outputStream, UserWriteEntity.class).sheet("用户信息").doWrite(dataList);
}
运行程序,打开文件内容结果!

2.2、简单导入
这种简单固定表头的 Excel 文件,如果想要读取文件数据,操作也很简单。
以上面的导出文件为例,使用 EasyExcel 提供的EasyExcel工具类,即可来实现文件内容数据的快速读取,示例代码如下:
首先创建读取实体类
/*** 读取实体类*/
public class UserReadEntity {@ExcelProperty(value = "姓名")private String name;/*** 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配*/@ExcelProperty(index = 1)private int age;@DateTimeFormat("yyyy-MM-dd HH:mm:ss")@ExcelProperty(value = "操作时间")private Date time;//set、get...
}
然后读取文件数据,并封装到对象里面
public static void main(String[] args) throws FileNotFoundException {//同步读取文件内容FileInputStream inputStream = new FileInputStream(new File("/Users/panzhi/Documents/easyexcel-user1.xls"));List<UserReadEntity> list = EasyExcel.read(inputStream).head(UserReadEntity.class).sheet().doReadSync();System.out.println(JSONArray.toJSONString(list));
}
运行程序,输出结果如下:
[{"age":20,"name":"张三0","time":1616920360000},{"age":21,"name":"张三1","time":1616920360000},{"age":22,"name":"张三2","time":1616920360000},{"age":23,"name":"张三3","time":1616920360000},{"age":24,"name":"张三4","time":1616920360000},{"age":25,"name":"张三5","time":1616920360000},{"age":26,"name":"张三6","time":1616920360000},{"age":27,"name":"张三7","time":1616920360000},{"age":28,"name":"张三8","time":1616920360000},{"age":29,"name":"张三9","time":1616920360000}]
2.3、动态自由导出导入
在实际使用开发中,我们不可能每来一个 excel 导入导出需求,就编写一个实体类,很多业务需求需要根据不同的字段来动态导入导出,没办法基于实体类注解的方式来读取文件或者写入文件。
因此,基于EasyExcel提供的动态参数化生成文件和动态监听器读取文件方法,我们可以单独封装一套动态导出导出工具类,省的我们每次都需要重新编写大量重复工作,以下就是小编我在实际使用过程,封装出来的工具类,在此分享给大家!
-
首先,我们可以编写一个动态导出工具类
public class DynamicEasyExcelExportUtils {private static final Logger log = LoggerFactory.getLogger(DynamicEasyExcelExportUtils.class);private static final String DEFAULT_SHEET_NAME = "sheet1";/*** 动态生成导出模版(单表头)* @param headColumns 列名称* @return excel文件流*/public static byte[] exportTemplateExcelFile(List<String> headColumns){List<List<String>> excelHead = Lists.newArrayList();headColumns.forEach(columnName -> { excelHead.add(Lists.newArrayList(columnName)); });byte[] stream = createExcelFile(excelHead, new ArrayList<>());return stream;}/*** 动态生成模版(复杂表头)* @param excelHead 列名称* @return*/public static byte[] exportTemplateExcelFileCustomHead(List<List<String>> excelHead){byte[] stream = createExcelFile(excelHead, new ArrayList<>());return stream;}/*** 动态导出文件(通过map方式计算)* @param headColumnMap 有序列头部* @param dataList 数据体* @return*/public static byte[] exportExcelFile(LinkedHashMap<String, String> headColumnMap, List<Map<String, Object>> dataList){//获取列名称List<List<String>> excelHead = new ArrayList<>();if(MapUtils.isNotEmpty(headColumnMap)){//key为匹配符,value为列名,如果多级列名用逗号隔开headColumnMap.entrySet().forEach(entry -> {excelHead.add(Lists.newArrayList(entry.getValue().split(",")));});}List<List<Object>> excelRows = new ArrayList<>();if(MapUtils.isNotEmpty(headColumnMap) && CollectionUtils.isNotEmpty(dataList)){for (Map<String, Object> dataMap : dataList) {List<Object> rows = new ArrayList<>();headColumnMap.entrySet().forEach(headColumnEntry -> {if(dataMap.containsKey(headColumnEntry.getKey())){Object data = dataMap.get(headColumnEntry.getKey());rows.add(data);}});excelRows.add(rows);}}byte[] stream = createExcelFile(excelHead, excelRows);return stream;}/*** 生成文件(自定义头部排列)* @param rowHeads* @param excelRows* @return*/public static byte[] customerExportExcelFile(List<List<String>> rowHeads, List<List<Object>> excelRows){//将行头部转成easyexcel能识别的部分List<List<String>> excelHead = transferHead(rowHeads);return createExcelFile(excelHead, excelRows);}/*** 生成文件* @param excelHead* @param excelRows* @return*/private static byte[] createExcelFile(List<List<String>> excelHead, List<List<Object>> excelRows){try {if(CollectionUtils.isNotEmpty(excelHead)){ByteArrayOutputStream outputStream = new ByteArrayOutputStream();EasyExcel.write(outputStream).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).head(excelHead).sheet(DEFAULT_SHEET_NAME).doWrite(excelRows);return outputStream.toByteArray();}} catch (Exception e) {log.error("动态生成excel文件失败,headColumns:" + JSONArray.toJSONString(excelHead) + ",excelRows:" + JSONArray.toJSONString(excelRows), e);}return null;}/*** 将行头部转成easyexcel能识别的部分* @param rowHeads* @return*/public static List<List<String>> transferHead(List<List<String>> rowHeads){//将头部列进行反转List<List<String>> realHead = new ArrayList<>();if(CollectionUtils.isNotEmpty(rowHeads)){Map<Integer, List<String>> cellMap = new LinkedHashMap<>();//遍历行for (List<String> cells : rowHeads) {//遍历列for (int i = 0; i < cells.size(); i++) {if(cellMap.containsKey(i)){cellMap.get(i).add(cells.get(i));} else {cellMap.put(i, Lists.newArrayList(cells.get(i)));}}}//将列一行一行加入realHeadcellMap.entrySet().forEach(item -> realHead.add(item.getValue()));}return realHead;}/*** 导出文件测试* @param args* @throws IOException*/public static void main(String[] args) throws IOException {//导出包含数据内容的文件(方式一)LinkedHashMap<String, String> headColumnMap = Maps.newLinkedHashMap();headColumnMap.put("className","班级");headColumnMap.put("name","学生信息,姓名");headColumnMap.put("sex","学生信息,性别");List<Map<String, Object>> dataList = new ArrayList<>();for (int i = 0; i < 5; i++) {Map<String, Object> dataMap = Maps.newHashMap();dataMap.put("className", "一年级");dataMap.put("name", "张三" + i);dataMap.put("sex", "男");dataList.add(dataMap);}byte[] stream1 = exportExcelFile(headColumnMap, dataList);FileOutputStream outputStream1 = new FileOutputStream(new File("/Users/panzhi/Documents/easyexcel-export-user5.xlsx"));outputStream1.write(stream1);outputStream1.close();//导出包含数据内容的文件(方式二)//头部,第一层List<String> head1 = new ArrayList<>();head1.add("第一行头部列1");head1.add("第一行头部列1");head1.add("第一行头部列1");head1.add("第一行头部列1");//头部,第二层List<String> head2 = new ArrayList<>();head2.add("第二行头部列1");head2.add("第二行头部列1");head2.add("第二行头部列2");head2.add("第二行头部列2");//头部,第三层List<String> head3 = new ArrayList<>();head3.add("第三行头部列1");head3.add("第三行头部列2");head3.add("第三行头部列3");head3.add("第三行头部列4");//封装头部List<List<String>> allHead = new ArrayList<>();allHead.add(head1);allHead.add(head2);allHead.add(head3);//封装数据体//第一行数据List<Object> data1 = Lists.newArrayList(1,1,1,1);//第二行数据List<Object> data2 = Lists.newArrayList(2,2,2,2);List<List<Object>> allData = Lists.newArrayList(data1, data2);byte[] stream2 = customerExportExcelFile(allHead, allData);FileOutputStream outputStream2 = new FileOutputStream(new File("/Users/panzhi/Documents/easyexcel-export-user6.xlsx"));outputStream2.write(stream2);outputStream2.close();}
}
-
然后,编写一个动态导入工具类
/*** 创建一个文件读取监听器*/
public class DynamicEasyExcelListener extends AnalysisEventListener<Map<Integer, String>> {private static final Logger LOGGER = LoggerFactory.getLogger(UserDataListener.class);/*** 表头数据(存储所有的表头数据)*/private List<Map<Integer, String>> headList = new ArrayList<>();/*** 数据体*/private List<Map<Integer, String>> dataList = new ArrayList<>();/*** 这里会一行行的返回头** @param headMap* @param context*/@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap));//存储全部表头数据headList.add(headMap);}/*** 这个每一条数据解析都会来调用** @param data* one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublic void invoke(Map<Integer, String> data, AnalysisContext context) {LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));dataList.add(data);}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库LOGGER.info("所有数据解析完成!");}public List<Map<Integer, String>> getHeadList() {return headList;}public List<Map<Integer, String>> getDataList() {return dataList;}
}
-
动态导入工具类
/*** 编写导入工具类*/
public class DynamicEasyExcelImportUtils {/*** 动态获取全部列和数据体,默认从第一行开始解析数据* @param stream* @return*/public static List<Map<String,String>> parseExcelToView(byte[] stream) {return parseExcelToView(stream, 1);}/*** 动态获取全部列和数据体* @param stream excel文件流* @param parseRowNumber 指定读取行* @return*/public static List<Map<String,String>> parseExcelToView(byte[] stream, Integer parseRowNumber) {DynamicEasyExcelListener readListener = new DynamicEasyExcelListener();EasyExcelFactory.read(new ByteArrayInputStream(stream)).registerReadListener(readListener).headRowNumber(parseRowNumber).sheet(0).doRead();List<Map<Integer, String>> headList = readListener.getHeadList();if(CollectionUtils.isEmpty(headList)){throw new RuntimeException("Excel未包含表头");}List<Map<Integer, String>> dataList = readListener.getDataList();if(CollectionUtils.isEmpty(dataList)){throw new RuntimeException("Excel未包含数据");}//获取头部,取最后一次解析的列头数据Map<Integer, String> excelHeadIdxNameMap = headList.get(headList.size() -1);//封装数据体List<Map<String,String>> excelDataList = Lists.newArrayList();for (Map<Integer, String> dataRow : dataList) {Map<String,String> rowData = new LinkedHashMap<>();excelHeadIdxNameMap.entrySet().forEach(columnHead -> {rowData.put(columnHead.getValue(), dataRow.get(columnHead.getKey()));});excelDataList.add(rowData);}return excelDataList;}/*** 文件导入测试* @param args* @throws IOException*/public static void main(String[] args) throws IOException {FileInputStream inputStream = new FileInputStream(new File("/Users/panzhi/Documents/easyexcel-export-user5.xlsx"));byte[] stream = IoUtils.toByteArray(inputStream);List<Map<String,String>> dataList = parseExcelToView(stream, 2);System.out.println(JSONArray.toJSONString(dataList));inputStream.close();}
}
为了方便后续的操作流程,在解析数据的时候,会将列名作为key!
03、小结
在实际的业务开发过程中,根据参数动态实现 Excel 的导出导入还是非常广的。
当然,EasyExcel 的功能还不只上面介绍的那些内容,还有基于模版进行 excel的填充,web 端 restful 的导出导出,使用方法大致都差不多!
相关文章:
SpringBoot 整合 Excel 轻松实现数据自由导入导出
01、背景介绍 在实际的业务系统开发过程中,操作 Excel 实现数据的导入导出基本上是个非常常见的需求。 之前,我们有介绍一款非常好用的工具:EasyPoi,有读者提出在数据量大的情况下,EasyPoi 会占用内存大,…...
PyTorch 基础学习(13)- 混合精度训练
系列文章: 《PyTorch 基础学习》文章索引 基本概念 混合精度训练是深度学习中一种优化技术,旨在通过结合高精度(torch.float32)和低精度(如 torch.float16 或 torch.bfloat16)数据类型的优势,…...
Mycat分片-垂直拆分
目录 场景 配置 测试 全局表配置 续接上篇:MySQ分库分表与MyCat安装配置-CSDN博客 续接下篇:Mycat分片-水平拆分-CSDN博客 场景 在业务系统中, 涉及以下表结构 ,但是由于用户与订单每天都会产生大量的数据, 单台服务器的数据 存储及处理能力是有限…...
一元四次方程求解-【附MATLAB代码】
目录 前言 求解方法 编辑 MATLAB验证 附:一元四次方程的故事 前言 最近在研究机器人的干涉(碰撞)检测,遇到了一个问题,就是在求椭圆到原点的最短距离时,构建的方程是一个一元四次方程。无论是高中的…...
【极限性能,尽在掌控】ROG NUC:游戏与创作的微型巨擘
初见ROG NUC,你或许会为它的小巧体型惊讶。然而,这看似不起眼的机身内,蕴藏着游戏、创意的强大能量。 掌中风暴,性能无界 ROG NUC搭载英特尔高性能处理器,配合高速NVMe SSD固态硬盘以及可选的高端独立显卡(…...
Ecosmos开启公测,将深度赋能CIOE中国光博会元宇宙参会新体验
如今,生成式AI技术的发展,极大地降低了3D数字资产的制作成本,元宇宙作为一种可以无缝将物理和数字资产进行融合的技术,在推动电子产业数字化进程、助力产业高质量发展的方面展现出了巨大的潜力。 当前,发展新质生产力是…...
【Kubernetes】k8s集群之包管理器Helm
目录 一.Helm概述 1.Helm的简介 2.Helm的三个重要概念 3.Helm2与Helm3的的区别 二.Helm 部署 1.安装 helm 2.使用 helm 安装 Chart 3.Helm 自定义模板 4.Helm 仓库 每个成功的软件平台都有一个优秀的打包系统,比如Debian、Ubuntu 的 apt,RedH…...
嵌入式linux系统镜像制作day3(构建镜像)
点击上方"蓝字"关注我们 01、上节回顾 嵌入式linux系统镜像制作day1嵌入式linux系统镜像制作day2提前下载好准备工具,不然失败了大眼瞪小眼。 02、构建 Poky 的 Sato 镜像1 环境: ubuntu18.04poky版本:Dizzy 工具git 在开始之前,针对不同的发行版,需要先执行…...
【生日视频制作】教师节中秋节国庆节车模特美女举牌AE模板修改文字软件生成器教程特效素材【AE模板】
教师节中秋节国庆节车模特美女举牌生日视频制作教程AE模板改文字软件生成器素材 怎么如何做的【生日视频制作】教师节中秋节国庆节车模特美女举牌AE模板修改文字软件生成器教程特效素材【AE模板】 生日视频制作步骤: 安装AE软件下载AE模板把AE模板导入AE软件修改图…...
RongCallKit iOS 端本地私有 pod 方案
RongCallKit iOS 端本地私有 pod 方案 需求背景 适用于源码集成 CallKit 时,使用 pod 管理 RTC framework 以及源码。集成 CallKit 时,需要定制化修改 CallKit 的样式以及部分 UI 功能。适用于 CallKit 源码 Debug 调试便于定位相关问题。 解决方案 从…...
C++11:可变参数模板
目录 一、概述 二、场景 1.深拷贝的类 2.浅拷贝的类 C使用指南 一、概述 // Args是一个模板参数包,args是一个函数形参参数包 // 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。 template <class ...Args> void ShowList(…...
C++ 与 QML 之间进行数据交互的几种方法
https://www.cnblogs.com/jzcn/p/17774676.html 一、属性绑定 这是最简单的方式,可以在QML中直接绑定C 对象的属性。通过在C 对象中使用Q_PROPERTY宏定义属性,然后在QML中使用绑定语法将属性与QML元素关联起来。 1. person.h #include <QObject&g…...
Javaweb学习之Vue项目的创建(二)
学习资料 Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org) 准备工作都做完了,接下来开始Vue的正式学习。 第一步,打开VS Code 在VS Code里,我们也需要使用到终端,如果不是以管理员身份打开,在新建Vue项目的时候…...
『深度长文』4种有效提高LLM输出质量的方法!
LLM,全称Large Language Model,意为大型语言模型,是一种基于深度学习的AI技术,能够生成、理解和处理自然语言文本,也因此成为当前大多数AI工具的核心引擎。LLM通过学习海量的文本数据,掌握了词汇、语法、语…...
【工业机器人】工业异常检测大模型AnomalyGPT
AnomalyGPT 工业异常检测视觉大模型AnomalyGPT AnomalyGPT: Detecting Industrial Anomalies using Large Vision-Language Models AnomalyGPT是一种基于大视觉语言模型(LVLM)的新型工业异常检测(IAD)方法。它利用LVLM的能力来理…...
【PGCCC】PostgreSQL案例:planning time超长问题分析#PG初级
在使用 PostgreSQL 时,查询的执行计划(planning time)有时会出现异常长的情况,这可能会影响数据库的整体性能。分析和解决这种问题可以从多个角度入手,以下是常见原因和相应的解决思路: 1. 统计信息不准确…...
【图文并茂】ant design pro 如何给后端发送 json web token - 请求拦截器的使用
上一节有讲过 【图文并茂】ant design pro 如何对接后端个人信息接口 还差一个东西,去获取个人信息的时候,是要发送 token 的,不然会报 403. 就是说在你登录之后才去获得个人信息。这样后端才能知道是谁的信息。 token 就代码了某个人。 …...
【微信小程序】自定义组件 - behaviors
1. 什么是 behaviors 2. behaviors 的工作方式 3. 创建 behavior 调用 Behavior(Object object) 方法即可创建一个共享的 behavior 实例对象,供所有的组件使用: 4. 导入并使用 behavior 5. behavior 中所有可用的节点 6. 同名字段的覆盖和组合规则* 关…...
Linux ubuntu 24.04 安装运行《帝国时代3》免安装绿色版游戏,解决 “Could not load DATAP.BAR”等问题
Linux ubuntu 24.04 安装运行《帝国时代3》游戏,解决 “Could not load DATAP.BAR" 等问题 《帝国时代 3》是一款比较经典的即时战斗游戏,伴随了我半个高中时代,周末有时间就去泡网吧,可惜玩的都是简单人机,高难…...
Springboot 图片
Springboot 图片 因为 server.servlet.context-path: /api 所以 url是这个的时候 http://127.0.0.1:9100/api/staticfiles/image/dd56a59d-da84-441a-8dac-1d97f9e42090.jpeg 配置代码的前面的 /api 是不要写的 package com.gk.study.config;import org.springframework.conte…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
