使用Apache POI生成XLS文件时部分行样式丢失问题求助
问题分析与解决方案
从你的描述和代码来看,虽然样式总数没超过XLS的4000上限,但样式丢失的问题主要是由样式创建与复用逻辑的错误、未完成的方法以及引用类型的样式被意外修改导致的,具体原因和修复方案如下:
核心问题分析
1. 未完成的createStyleObject方法
你贴出的代码里,createStyleObject方法只写了一半:
private static CellStyle createStyleObject() { Workbook workbook = spreadshe
这会导致该方法无法正确返回CellStyle对象,当样式数量达到75后,后续尝试获取样式时会拿到null,直接造成单元格样式丢失。
2. 样式复用逻辑混乱
- 比如在
case 1中,你设置完列1的样式后,错误执行了styles.put(0, style);,这会把列1的样式覆盖列0的样式,导致两列样式混乱。 - 动态判断
workbook.getNumCellStyles() <75的逻辑不合理:当样式数达到75后,新行的某些列可能还没在styles中存入对应样式,导致style为null,单元格无样式。
3. 复用样式被意外修改
在日期列(case2、5、8、9)的处理中,你每次都会给style重新设置DataFormat:
style.setDataFormat(poiFormat.getFormat(excelFormatPattern));
由于CellStyle是引用类型,修改已复用的样式对象会影响所有使用该样式的单元格,导致格式被意外覆盖,看起来像是样式丢失。
4. 过时的日期构造方法
使用new Date(rowNo %10, rowNo%12...)是过时的API,且年份从1900开始计算,可能导致日期显示异常(虽然不是样式丢失的直接原因,但需要修正)。
修复后的完整代码
我们重构样式逻辑,提前初始化所有需要的样式,避免动态创建的混乱,同时修正所有错误点:
import org.apache.poi.hssf.usermodel.*; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.DateFormatConverter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; public class ExcelSheet { static Workbook workbook; static Sheet spreadsheet; static Map<Integer, CellStyle> styles = new HashMap<>(); public static void main(String[] args) throws IOException { workbook = new HSSFWorkbook(); spreadsheet = workbook.createSheet("Employee Info"); // 提前初始化所有需要的样式,避免循环中动态创建的混乱 initAllStyles(); System.out.println("Writing data to excel..."); for (int i = 1; i < 500; i++) { Row row = spreadsheet.createRow(i); createCells(row, i); } for (int i = 0; i < 10; i++) { spreadsheet.setColumnWidth(i, 4500); } FileOutputStream out = new FileOutputStream(new File("E:\\Dan\\Writesheet.xls")); System.out.println("No. of cell styles : " + workbook.getNumCellStyles()); System.out.println("No. of font styles : " + workbook.getNumberOfFonts()); workbook.write(out); out.close(); } // 初始化所有列需要的样式,复用相同格式的样式 private static void initAllStyles() { DataFormat poiFormat = workbook.createDataFormat(); String dateFormatPattern = DateFormatConverter.convert(Locale.US, "yyyy-MM-dd"); short dateFormatIndex = poiFormat.getFormat(dateFormatPattern); // 数字通用样式 CellStyle numStyle = workbook.createCellStyle(); setCommonBorder(numStyle); numStyle.setAlignment(HorizontalAlignment.RIGHT); // 字符串通用样式 CellStyle strStyle = workbook.createCellStyle(); setCommonBorder(strStyle); strStyle.setAlignment(HorizontalAlignment.LEFT); // 日期通用样式(复用数字样式的边框,仅修改格式) CellStyle dateStyle = workbook.createCellStyle(); dateStyle.cloneStyleFrom(numStyle); dateStyle.setDataFormat(dateFormatIndex); // 给每列分配对应的样式,相同格式直接复用 styles.put(0, numStyle); // 列0:数字 styles.put(1, strStyle); // 列1:字符串 styles.put(2, dateStyle); // 列2:日期 styles.put(3, numStyle); // 列3:数字(复用) styles.put(4, strStyle); // 列4:字符串(复用) styles.put(5, dateStyle); // 列5:日期(复用) styles.put(6, numStyle); // 列6:数字(复用) styles.put(7, strStyle); // 列7:字符串(复用) styles.put(8, dateStyle); // 列8:日期(复用) styles.put(9, dateStyle); // 列9:日期(复用) } // 设置单元格通用边框样式,避免重复代码 private static void setCommonBorder(CellStyle style) { style.setBorderBottom(BorderStyle.THIN); style.setBorderTop(BorderStyle.THIN); style.setBorderLeft(BorderStyle.THIN); style.setBorderRight(BorderStyle.THIN); style.setVerticalAlignment(VerticalAlignment.CENTER); } private static void createCells(Row row, int rowNo) { for (int i = 0; i < 10; i++) { Cell cell = row.createCell(i); try { writeData(cell, i, rowNo); } catch (Exception e) { System.out.println("Exception : " + e); } } } private static void writeData(Cell cell, int i, int rowNo) throws Exception { CellStyle style = styles.get(i); if (style == null) { // 默认使用字符串样式 style = styles.get(7); } switch (i) { case 0: cell.setCellValue(rowNo); cell.setCellType(CellType.NUMERIC); break; case 1: cell.setCellValue("Test Data " + rowNo); cell.setCellType(CellType.STRING); break; case 2: // 使用Calendar代替过时的Date构造方法 Calendar cal = Calendar.getInstance(); cal.set(1900 + (rowNo % 10), rowNo % 12, rowNo % 28, rowNo % 12, rowNo % 60, rowNo % 60); cell.setCellValue(cal.getTime()); break; case 3: cell.setCellValue(rowNo + rowNo); cell.setCellType(CellType.NUMERIC); break; case 4: cell.setCellValue("Sample Text " + rowNo); cell.setCellType(CellType.STRING); break; case 5: Calendar cal5 = Calendar.getInstance(); cal5.set(1900 + (rowNo % 18), rowNo % 12, rowNo % 28, rowNo % 12, rowNo % 60, rowNo % 60); cell.setCellValue(cal5.getTime()); break; case 6: cell.setCellValue(rowNo + i); cell.setCellType(CellType.NUMERIC); break; case 7: cell.setCellValue("Exports Data " + rowNo); cell.setCellType(CellType.STRING); break; case 8: Calendar cal8 = Calendar.getInstance(); cal8.set(1900 + (rowNo % 25), rowNo % 12, rowNo % 28, rowNo % 12, rowNo % 60, rowNo % 60); cell.setCellValue(cal8.getTime()); break; case 9: cell.setCellValue(new Date()); break; default: cell.setCellValue("Default Value " + rowNo); cell.setCellType(CellType.STRING); break; } cell.setCellStyle(style); } }
POI使用CellStyle的最佳实践
- 提前创建所有样式:不要在循环中动态创建样式,避免逻辑混乱和样式数量失控。
- 复用相同样式:相同格式的单元格使用同一个CellStyle对象,减少样式总数。
- 避免修改已复用的样式:CellStyle是引用类型,修改一个会影响所有使用该样式的单元格;如果需要不同样式,先通过
cloneStyleFrom复制再修改。 - 注意XLS的样式限制:虽然XLS允许最多4000个样式,但过多样式会导致文件变大、性能下降,尽量精简。
内容的提问来源于stack exchange,提问作者user1300073
相关产品推荐
相关产品推荐

