当前位置:首页 > 后端开发 > 正文

Java如何轻松打印购物小票?

在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/网络打印机)。

Java如何轻松打印购物小票?  第1张

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();
    }
}

关键注意事项:

  1. 中文乱码问题
    确保打印机支持UTF-8编码,热敏打印机通常需设置为中文字库模式(如ESC/POS指令集)。
  2. 打印机选择
    可通过 PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null); 列出所有打印机。
  3. 格式对齐
    使用制表符 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小票已生成");
        }
    }
}

最佳实践与避坑指南

  1. 格式对齐

    • 使用 String.format() 控制列宽,
      String.format("%-20s %10.2f", productName, price)
      (左对齐商品名,右对齐价格)
  2. 字符编码

    • 物理打印时,若遇中文乱码:
      • 方案1:打印机切换至GBK/GB2312模式
      • 方案2:将文本转换为字节流时指定编码:
        receiptContent.getBytes("GB18030")
  3. 打印超时处理

    job.print(doc, attributes); // 默认可能阻塞
    // 建议改用异步线程,避免主线程卡死
  4. 商业系统推荐

    • 复杂场景(如POS机)使用 ESC/POS指令集 直接控制打印机(需厂商文档)
    • 开源库推荐:JavaPOSQZ Tray(支持网页调用打印机)

引用说明

  • Java Print Service API:Oracle官方文档
  • PDFBox库:Apache PDFBox官网
  • 热敏打印机指令参考:ESC/POS命令手册
    代码示例遵循MIT许可,可自由用于商业项目。
0