上一篇
在Java中打印购物小票可通过控制台输出实现:使用
System.out.printf()格式化输出商品名称、单价、数量和金额,创建商品对象列表,遍历计算总价,最后打印表头和汇总信息,关键点包括对齐列宽、保留小数位数和添加分隔线增强可读性。
在Java中打印购物小票是零售、餐饮等系统开发的常见需求,以下是三种实用方法,涵盖不同场景(控制台调试、物理打印机输出、PDF生成),均采用标准Java API或主流库实现,确保代码健壮性和可维护性。
基础方案:控制台打印(调试用)
适用于开发阶段快速验证小票格式和逻辑。
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class ConsoleReceiptPrinter {
public static void main(String[] args) {
// 模拟订单数据
Map<String, Double> items = new HashMap<>();
items.put("可口可乐", 3.50);
items.put("薯片", 8.00);
items.put("矿泉水", 2.00);
printReceipt("C001", items, 0.9); // 9折优惠
}
public static void printReceipt(String orderId, Map<String, Double> items, double discount) {
StringBuilder sb = new StringBuilder();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 小票头部
sb.append("n============ 超市收银小票 ============n");
sb.append("订单号: ").append(orderId).append("n");
sb.append("时间: ").append(sdf.format(new Date())).append("n");
sb.append("-----------------------------------n");
// 商品列表
double subtotal = 0;
for (Map.Entry<String, Double> entry : items.entrySet()) {
String itemName = entry.getKey();
double price = entry.getValue();
sb.append(String.format("%-10st¥%.2fn", itemName, price)); // 左对齐商品名
subtotal += price;
}
// 费用计算
double discountedAmount = subtotal * discount;
double savings = subtotal - discountedAmount;
sb.append("-----------------------------------n");
sb.append(String.format("小计: tt¥%.2fn", subtotal));
sb.append(String.format("折扣: tt%.0f折n", discount * 10));
sb.append(String.format("优惠: tt¥%.2fn", savings));
sb.append(String.format("总计: tt¥%.2fn", discountedAmount));
sb.append("===================================n");
sb.append("感谢惠顾!客服电话:400-123-4567n");
System.out.println(sb.toString());
}
}
输出效果:
============ 超市收银小票 ============
订单号: C001
时间: 2025-10-05 14:30:25
-----------------------------------
可口可乐 ¥3.50
薯片 ¥8.00
矿泉水 ¥2.00
-----------------------------------
小计: ¥13.50
折扣: 9折
优惠: ¥1.35
总计: ¥12.15
===================================
感谢惠顾!客服电话:400-123-4567
实战方案:连接物理打印机
使用 javax.print API 直接驱动打印机(支持USB/网络打印机)。

import javax.print.*;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.Copies;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
public class PhysicalPrinterService {
public static void main(String[] args) {
String receiptContent = generateReceiptContent(); // 生成小票文本
try {
// 1. 获取打印机服务
PrintService printer = PrintServiceLookup.lookupDefaultPrintService();
if (printer == null) {
throw new IllegalStateException("未找到默认打印机");
}
// 2. 设置打印参数
PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet();
attributes.add(new Copies(1)); // 打印份数
// 3. 创建打印任务
DocPrintJob job = printer.createPrintJob();
Doc doc = new SimpleDoc(
new ByteArrayInputStream(receiptContent.getBytes(StandardCharsets.UTF_8)),
DocFlavor.INPUT_STREAM.AUTOSENSE,
null
);
// 4. 执行打印
job.print(doc, attributes);
System.out.println("小票打印任务已发送");
} catch (Exception e) {
e.printStackTrace();
}
}
private static String generateReceiptContent() {
// 复用控制台方案中的生成逻辑
return new ConsoleReceiptPrinter().generateReceiptString();
}
}
关键注意事项:
- 中文乱码问题:
确保打印机支持UTF-8编码,热敏打印机通常需设置为中文字库模式(如ESC/POS指令集)。 - 打印机选择:
可通过PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);列出所有打印机。 - 格式对齐:
使用制表符t或空格调整列宽,推荐固定宽度字体(如”Courier New”)。
扩展方案:生成PDF小票
使用 Apache PDFBox 库生成可保存/打印的PDF文件(需添加Maven依赖):
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.27</version>
</dependency>
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import java.io.IOException;
public class PdfReceiptGenerator {
public static void main(String[] args) throws IOException {
try (PDDocument document = new PDDocument()) {
PDPage page = new PDPage();
document.addPage(page);
try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
contentStream.setFont(PDType1Font.COURIER_BOLD, 14);
contentStream.beginText();
contentStream.newLineAtOffset(50, 700); // 起始坐标
// 逐行写入内容(实际项目可封装为方法)
contentStream.showText("============ 超市电子小票 ============");
contentStream.newLineAtOffset(0, -20); // 下移20像素
contentStream.showText("订单号: PDF20251005001");
// 更多行省略... 参考控制台方案的格式
contentStream.endText();
}
document.save("receipt.pdf"); // 保存文件
System.out.println("PDF小票已生成");
}
}
}
最佳实践与避坑指南
-
格式对齐

- 使用
String.format()控制列宽,
String.format("%-20s %10.2f", productName, price)
(左对齐商品名,右对齐价格)
- 使用
-
字符编码
- 物理打印时,若遇中文乱码:
- 方案1:打印机切换至GBK/GB2312模式
- 方案2:将文本转换为字节流时指定编码:
receiptContent.getBytes("GB18030")
- 物理打印时,若遇中文乱码:
-
打印超时处理

job.print(doc, attributes); // 默认可能阻塞 // 建议改用异步线程,避免主线程卡死
-
商业系统推荐
- 复杂场景(如POS机)使用 ESC/POS指令集 直接控制打印机(需厂商文档)
- 开源库推荐:
JavaPOS、QZ Tray(支持网页调用打印机)
引用说明:
- Java Print Service API:Oracle官方文档
- PDFBox库:Apache PDFBox官网
- 热敏打印机指令参考:ESC/POS命令手册
代码示例遵循MIT许可,可自由用于商业项目。
