三种主流方案对比
| 特性 | Apache POI | EasyExcel | JXL |
|---|---|---|---|
| 维护方 | Apache 软件基金会(顶级项目) | 阿里巴巴(开源) | JavaExcel (已停止更新) |
| 优势 | 功能最全面,支持所有 Office 格式(.xls, .xlsx, .docx等) |
性能极高,内存占用极低,尤其适合处理大文件 | 简单易用,轻量级 |
| 劣势 | 对于大文件(如 .xlsx),内存消耗巨大,容易导致 OOM(内存溢出) |
相对较新,社区和功能生态不如 POI 完善 | 仅支持 .xls 格式,不支持 .xlsx,已不推荐使用 |
| 适用场景 | 需要处理多种 Office 格式,或对功能有复杂要求 | 专门为大数据量 Excel 设计,是处理 .xlsx 文件的首选 |
遗留系统维护,或处理非常小的、仅 .xls 格式的文件 |
总结建议:

- 新项目、处理
.xlsx文件(尤其是大文件):强烈推荐使用 EasyExcel。 - 需要处理
.xls文件,或功能复杂:使用 Apache POI。 - 不推荐使用 JXL,除非有特殊的历史遗留原因。
使用 Apache POI (功能全面)
Apache POI 是 Java 操作 Office 文件的事实标准。
添加 Maven 依赖
你需要根据你处理的 Excel 版本(.xls 或 .xlsx)来添加不同的依赖。
<!-- 对于 .xls 格式 (HSSFWorkbook) -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.3</version>
</dependency>
<!-- 对于 .xlsx 格式 (XSSFWorkbook) -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
<!-- 为了支持 XML 解析,还需要这个依赖 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-lite</artifactId>
<version>5.2.3</version>
</dependency>
代码示例 (读取 .xlsx 文件)
这里我们读取一个名为 user_data.xlsx 的文件,它有三列:ID, 姓名, 年龄。
Excel 文件内容 (user_data.xlsx):
| ID | 姓名 | 年龄 |
| :-- | :--- | :--- |
| 1 | 张三 | 25 |
| 2 | 李四 | 30 |
| 3 | 王五 | 28 |

Java 代码:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class PoiExcelReader {
public static void main(String[] args) {
String filePath = "user_data.xlsx"; // 文件路径
List<User> userList = readExcelWithPoi(filePath);
// 打印读取结果
for (User user : userList) {
System.out.println(user);
}
}
public static List<User> readExcelWithPoi(String filePath) {
List<User> userList = new ArrayList<>();
FileInputStream fis = null;
Workbook workbook = null;
try {
// 1. 获取文件输入流
fis = new FileInputStream(filePath);
// 2. 根据文件格式创建 Workbook 对象
// 对于 .xlsx 文件,使用 XSSFWorkbook
// 对于 .xls 文件,使用 HSSFWorkbook
workbook = new XSSFWorkbook(fis);
// 3. 获取第一个工作表 (Sheet)
Sheet sheet = workbook.getSheetAt(0);
// 4. 遍历每一行 (从第二行开始,跳过表头)
for (int i = 1; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
if (row == null) {
continue; // 跳过空行
}
// 5. 获取单元格的值
// 注意:getCell() 的第二个参数 CellType.BLANK 表示如果单元格为空,则返回一个空白单元格,而不是 null
Cell idCell = row.getCell(0, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
Cell nameCell = row.getCell(1, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
Cell ageCell = row.getCell(2, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
// 6. 处理不同类型的单元格数据
// getCellType() 获取单元格类型,然后根据类型转换
long id = (long) idCell.getNumericCellValue();
String name = nameCell.getStringCellValue();
int age = (int) ageCell.getNumericCellValue();
// 7. 将数据封装到对象中
User user = new User(id, name, age);
userList.add(user);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 8. 关闭资源
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return userList;
}
}
// 用于封装数据的简单实体类
class User {
private long id;
private String name;
private int age;
public User(long id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
使用 EasyExcel (高性能,推荐)
阿里巴巴开源的 EasyExcel 通过 SAX 模式(事件驱动)来解析 Excel,极大地降低了内存消耗,非常适合处理百万行级别的大数据。
添加 Maven 依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.1</version>
</dependency>
代码示例 (读取 .xlsx 文件)
EasyExcel 的核心是 监听器,你创建一个监听器,当 EasyExcel 读取到一行数据时,就会调用监听器中的方法。
Excel 文件内容: 与 POI 示例相同 (user_data.xlsx)。

Java 代码:
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.util.ArrayList;
import java.util.List;
public class EasyExcelReader {
public static void main(String[] args) {
String fileName = "user_data.xlsx";
// 创建一个监听器
ReadDataListener listener = new ReadDataListener();
// EasyExcel 会自动关闭流,你不需要手动关闭
EasyExcel.read(fileName, User.class, listener).sheet().doRead();
// 从监听器中获取数据
List<User> userList = listener.getDataList();
for (User user : userList) {
System.out.println(user);
}
}
}
/**
* 自定义监听器
*/
class ReadDataListener extends AnalysisEventListener<User> {
/**
* 使用这个 list 来存储每一条数据,数据量大的话,建议使用数据库等存储方式。
*/
private List<User> dataList = new ArrayList<>();
@Override
public void invoke(User user, AnalysisContext analysisContext) {
// 每解析一行数据,就会调用一次这个方法
System.out.println("解析到一条数据: " + user);
dataList.add(user);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
// 所有数据解析完毕后调用这个方法
System.out.println("所有数据解析完成!");
}
public List<User> getDataList() {
return dataList;
}
}
// User 类需要与 Excel 列一一对应,可以通过 @ExcelProperty 注解指定列名
class User {
// 使用 @ExcelProperty("ID") 来指定对应的 Excel 列标题
// 如果不写,默认按字段顺序匹配
@com.alibaba.excel.annotation.ExcelProperty("ID")
private Long id;
@com.alibaba.excel.annotation.ExcelProperty("姓名")
private String name;
@com.alibaba.excel.annotation.ExcelProperty("年龄")
private Integer age;
// EasyExcel 要求有无参构造器
public User() {
}
// 可以省略 getter/setter,但为了方便打印,这里加上
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
EasyExcel 的优点:
- 内存友好:
invoke方法是逐行调用的,读完一行后,这一行的数据可以被垃圾回收,不会一直堆积在内存中。 - 代码简洁:无需手动创建
Workbook和Sheet对象,代码量更少。 - 功能强大:支持将数据直接读入数据库(
AnalysisEventListener有现成的模板),支持复杂的表头处理等。
使用 JXL (简单过时)
JXL 是一个轻量级的库,非常简单,但已停止更新多年,不支持 .xlsx 格式。
添加 Maven 依赖
JXL 没有官方的 Maven 仓库,通常需要手动下载 JAR 包并安装到本地仓库,或者直接在项目中引入 JAR。
<!-- 如果你将 jxl.jar 放在了项目的 lib 目录下 -->
<dependency>
<groupId>net.sourceforge.jexcelapi</groupId>
<artifactId>jxl</artifactId>
<version>2.6.12</version>
</dependency>
代码示例 (仅支持 .xls)
import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class JxlExcelReader {
public static void main(String[] args) {
String filePath = "user_data.xls"; // 注意:必须是 .xls 文件
List<User> userList = readExcelWithJxl(filePath);
for (User user : userList) {
System.out.println(user);
}
}
public static List<User> readExcelWithJxl(String filePath) {
List<User> userList = new ArrayList<>();
Workbook workbook = null;
try {
// 1. 创建 Workbook 对象
workbook = Workbook.getWorkbook(new File(filePath));
// 2. 获取第一个工作表
Sheet sheet = workbook.getSheet(0);
// 3. 遍历行 (从第二行开始)
for (int i = 1; i < sheet.getRows(); i++) {
// 4. 遍历列
long id = Long.parseLong(sheet.getCell(0, i).getContents());
String name = sheet.getCell(1, i).getContents();
int age = Integer.parseInt(sheet.getCell(2, i).getContents());
User user = new User(id, name, age);
userList.add(user);
}
} catch (IOException | BiffException e) {
e.printStackTrace();
} finally {
// 5. 关闭 Workbook
if (workbook != null) {
workbook.close();
}
}
return userList;
}
}
// User 类与前面相同
总结与最终建议
| 库 | 易用性 | 性能 | 功能 | 推荐度 |
|---|---|---|---|---|
| EasyExcel | 高 | 极高 | 良好 | ⭐⭐⭐⭐⭐ (首选) |
| Apache POI | 中等 | 中等 (大文件差) | 最全 | ⭐⭐⭐⭐ (备选) |
| JXL | 高 | 中等 | 基础 | ⭐ (不推荐) |
对于任何新的 Java 项目,强烈建议你从 EasyExcel 开始,它在处理现代 Excel 格式(.xlsx)方面,无论是性能还是开发效率,都远超传统的 Apache POI,只有当你需要兼容旧版 .xls 文件或遇到 POI 独有的复杂功能时,才考虑使用 POI。
