目录
- 环境搭建
添加 Maven/Gradle 依赖
(图片来源网络,侵删) - 核心概念
Workbook(工作簿)Sheet(工作表)Row(行)Cell(单元格)
- 完整示例:创建并写入 Excel
- 创建
.xls(Excel 97-2003) 格式 - 创建
.xlsx(Excel 2007+) 格式
- 创建
- 完整示例:读取 Excel
- 读取
.xls和.xlsx文件 - 获取单元格数据并处理不同类型
- 读取
- 高级功能
- 样式设置:字体、颜色、边框、对齐方式
- 处理大数据量:
SXSSFWorkbook(流式 API) 避免内存溢出 - 合并单元格
- 调整列宽
- 创建下拉列表
- 总结与最佳实践
环境搭建
你需要在你的 Java 项目中添加 POI 的依赖,推荐使用 Maven 或 Gradle。
Maven 依赖
你需要根据你操作的 Excel 版本选择不同的依赖模块,现在推荐使用 .xlsx 格式。
<dependencies>
<!-- 针对 .xlsx 格式 (Office 2007+) -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version> <!-- 请使用最新版本 -->
</dependency>
<!-- 针对 .xls 格式 (Office 97-2003) -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.5</version> <!-- 请使用最新版本 -->
</dependency>
<!-- 用于处理 XML 等相关依赖,poi-ooxml 会自动包含 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-lite</artifactId>
<version>5.2.5</version>
</dependency>
</dependencies>
注意:poi-ooxml 已经包含了 poi 的核心功能,所以如果你只操作 .xlsx 文件,可以只添加 poi-ooxml,但为了兼容性,通常两者都加上。
Gradle 依赖
implementation 'org.apache.poi:poi-ooxml:5.2.5' implementation 'org.apache.poi:poi:5.2.5'
核心概念
理解 POI 的对象模型是关键:

Workbook: 代表一个 Excel 文件(工作簿),对于.xls是HSSFWorkbook,对于.xlsx是XSSFWorkbook。Sheet: 代表Workbook中的一个工作表(Sheet1, Sheet2...)。Row: 代表Sheet中的某一行,从 0 开始计数。Cell: 代表Row中的某一个单元格,从 0 开始计数。
操作流程通常是:创建 Workbook -> 创建 Sheet -> 创建 Row -> 创建 Cell -> 写入数据 -> 保存文件。
完整示例:创建并写入 Excel
这个例子将创建一个包含学生信息的 Excel 文件。
1 创建 .xlsx (推荐) 格式
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class WriteExcelXlsxExample {
public static void main(String[] args) {
// 1. 创建一个新的 XSSFWorkbook (对应 .xlsx 格式)
Workbook workbook = new XSSFWorkbook();
// 2. 创建一个工作表,名为 "学生信息"
Sheet sheet = workbook.createSheet("学生信息");
// 3. 创建表头行 (第 0 行)
Row headerRow = sheet.createRow(0);
// 4. 创建表头单元格并设置值
Cell headerCell1 = headerRow.createCell(0);
headerCell1.setCellValue("ID");
Cell headerCell2 = headerRow.createCell(1);
headerCell2.setCellValue("姓名");
Cell headerCell3 = headerRow.createCell(2);
headerCell3.setCellValue("年龄");
Cell headerCell4 = headerRow.createCell(3);
headerCell4.setCellValue("分数");
// 5. 创建数据行
Object[][] studentsData = {
{1, "张三", 20, 95.5},
{2, "李四", 21, 88.0},
{3, "王五", 19, 76.5}
};
for (int i = 0; i < studentsData.length; i++) {
Row dataRow = sheet.createRow(i + 1); // 从第 1 行开始写数据
Object[] student = studentsData[i];
for (int j = 0; j < student.length; j++) {
Cell cell = dataRow.createCell(j);
// 根据数据类型设置单元格值
if (student[j] instanceof String) {
cell.setCellValue((String) student[j]);
} else if (student[j] instanceof Integer) {
cell.setCellValue((Integer) student[j]);
} else if (student[j] instanceof Double) {
cell.setCellValue((Double) student[j]);
}
}
}
// 6. 设置列宽,防止内容被截断
sheet.autoSizeColumn(0);
sheet.autoSizeColumn(1);
sheet.autoSizeColumn(2);
sheet.autoSizeColumn(3);
// 7. 写入文件
try (FileOutputStream fileOut = new FileOutputStream("students.xlsx")) {
workbook.write(fileOut);
System.out.println("Excel 文件 students.xlsx 创建成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 8. 关闭工作簿,释放资源
try {
if (workbook != null) {
workbook.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2 创建 .xls 格式
代码几乎完全一样,只需要将 XSSFWorkbook 替换为 HSSFWorkbook,文件名后缀改为 .xls。
// 将 XSSFWorkbook 替换为 HSSFWorkbook
Workbook workbook = new HSSFWorkbook();
// ...
// 文件名改为 students.xls
FileOutputStream fileOut = new FileOutputStream("students.xls");
完整示例:读取 Excel
这个例子将读取上面创建的 students.xlsx 文件。
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
public class ReadExcelExample {
public static void main(String[] args) {
String filePath = "students.xlsx";
try (FileInputStream fileIn = new FileInputStream(filePath);
Workbook workbook = new XSSFWorkbook(fileIn)) { // 自动识别 .xlsx 或 .xls
// 1. 获取第一个工作表
Sheet sheet = workbook.getSheetAt(0); // 也可以用 workbook.getSheet("学生信息");
// 2. 遍历每一行
for (Row row : sheet) {
// 跳过表头
if (row.getRowNum() == 0) {
continue;
}
// 3. 遍历每一个单元格
StringBuilder rowData = new StringBuilder();
for (Cell cell : row) {
// 4. 根据单元格类型获取值
switch (cell.getCellType()) {
case STRING:
rowData.append(cell.getStringCellValue()).append("\t");
break;
case NUMERIC:
// 检查是整数还是日期
if (DateUtil.isCellDateFormatted(cell)) {
rowData.append(cell.getDateCellValue()).append("\t");
} else {
rowData.append(cell.getNumericCellValue()).append("\t");
}
break;
case BOOLEAN:
rowData.append(cell.getBooleanCellValue()).append("\t");
break;
case FORMULA:
rowData.append(cell.getCellFormula()).append("\t");
break;
case BLANK:
rowData.append("[BLANK]").append("\t");
break;
default:
rowData.append("[UNKNOWN]").append("\t");
}
}
System.out.println(rowData.toString());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
高级功能
1 样式设置
通过 CellStyle 可以设置单元格的字体、颜色、边框、背景、对齐方式等。
// ... 在创建 Cell 之前 ...
// 1. 创建一个样式
CellStyle style = workbook.createCellStyle();
// 2. 设置字体
Font font = workbook.createFont();
font.setFontName("Arial");
font.setFontHeightInPoints((short) 12);
font.setBold(true);
font.setColor(IndexedColors.RED.getIndex());
style.setFont(font);
// 3. 设置对齐方式
style.setAlignment(HorizontalAlignment.CENTER); // 水平居中
style.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中
// 4. 设置边框
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
style.setRightBorderColor(IndexedColors.BLACK.getIndex());
// 5. 设置背景色
style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 6. 将样式应用到单元格
Cell cell = row.createCell(0);
cell.setCellValue("带样式的文本");
cell.setCellStyle(style);
2 处理大数据量 (避免 OOM)
当处理几十万甚至上百万行数据时,XSSFWorkbook 会将所有数据加载到内存中,导致内存溢出,此时必须使用 SXSSFWorkbook (Streaming Usermodel)。
SXSSFWorkbook 采用一种“滑动窗口”的机制,它只保留一部分数据在内存中,其余数据临时写入磁盘。
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import java.io.FileOutputStream;
import java.io.IOException;
public class WriteLargeExcelExample {
public static void main(String[] args) {
// 1. 创建 SXSSFWorkbook,参数 100 表示在内存中保留的行数
Workbook workbook = new SXSSFWorkbook(100); // <-- 关键
// 2. 创建工作表
Sheet sheet = workbook.createSheet("大数据");
// 3. 写入数据 (模拟 10 万行)
for (int rowNum = 0; rowNum < 100000; rowNum++) {
Row row = sheet.createRow(rowNum);
for (int colNum = 0; colNum < 10; colNum++) {
Cell cell = row.createCell(colNum);
cell.setCellValue("Row " + rowNum + ", Col " + colNum);
}
}
// 4. 写入文件
try (FileOutputStream fileOut = new FileOutputStream("large_data.xlsx")) {
workbook.write(fileOut);
System.out.println("大数据 Excel 文件创建成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 5. 清理临时文件 (非常重要!)
((SXSSFWorkbook) workbook).dispose();
try {
if (workbook != null) {
workbook.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
注意:
SXSSFWorkbook只支持.xlsx格式。- 不支持
Sheet.clone()和Workbook.cloneSheet()。 - 不支持公式计算。
- 写入完成后,必须调用
dispose()方法来删除临时文件。
3 合并单元格
// ...
Sheet sheet = workbook.createSheet("合并单元格示例");
// 合并从 (0,0) 到 (2,3) 的区域
sheet.addMergedRegion(new CellRangeAddress(0, 2, 0, 3));
// 在合并后的区域的左上角单元格写入内容
Cell mergedCell = sheet.createRow(0).createCell(0);
mergedCell.setCellValue("这是一个合并的大单元格");
4 调整列宽
// ...
Sheet sheet = workbook.createSheet("调整列宽");
// 自动调整所有列的宽度(基于内容)
sheet.autoSizeColumn(0);
sheet.autoSizeColumn(1);
// 或者手动设置列宽(单位是 1/256 个字符的宽度)
sheet.setColumnWidth(0, 30 * 256); // 设置第 0 列宽度为 30 个字符
sheet.setColumnWidth(1, 20 * 256); // 设置第 1 列宽度为 20 个字符
5 创建下拉列表
// ...
Sheet sheet = workbook.createSheet("下拉列表");
// 1. 创建一个隐藏的工作表来存放下拉列表的选项
Sheet hiddenSheet = workbook.createSheet("hidden_sheet");
hiddenSheet.createRow(0).createCell(0).setCellValue("选项1");
hiddenSheet.createRow(1).createCell(0).setCellValue("选项2");
hiddenSheet.createRow(2).createCell(0).setCellValue("选项3");
// 2. 定义名称,指向这些选项
Name name = workbook.createName();
name.setNameName("dropdownList");
name.setRefersToFormula("hidden_sheet!$A$1:$A$3");
// 3. 在主工作表的 A1 单元格创建下拉列表
DataValidationHelper validationHelper = sheet.getDataValidationHelper();
DataValidationConstraint constraint = validationHelper.createFormulaListConstraint("dropdownList");
CellRangeAddressList addressList = new CellRangeAddressList(0, 0, 0, 0); // A1 单元格
DataValidation validation = validationHelper.createValidation(constraint, addressList);
sheet.addValidationData(validation);
总结与最佳实践
-
选择正确的 API:
- 旧版
.xls->HSSFWorkbook - 新版
.xlsx->XSSFWorkbook - 大数据量
.xlsx->SXSSFWorkbook(必选)
- 旧版
-
资源管理:始终使用
try-with-resources语句来关闭FileInputStream和Workbook,确保文件句柄和内存被正确释放。 -
样式复用:不要为每个单元格都创建一个新的
CellStyle,如果多个单元格需要相同的样式,请创建一个CellStyle对象并重复使用它,以节省内存。 -
数据类型:写入数据时,尽量使用
setCellValue的强类型方法(如setCellValue(String value)),而不是依赖自动转换,这更可靠。 -
异常处理:文件操作是 I/O 密集型任务,务必进行充分的异常处理。
希望这份详细的指南能帮助你掌握 Java POI 操作 Excel!
