【实现思路】
    1、为方便保存图书馆的相关消息,可以将图书信息封装成一个实体类。图书售出过程中可能会打印图书相关信息,所以需要对该实体类的 toString()方法进行重写,使其能更清晰地显示图书信息。每次图书售出之后要修改库存数量,还应在实体类中编写一个操作库数量的方法。
    2、对于一个书店,首先要有一个书架,书架上会有许多图书供挑选。这里可以创建一个集合用于模拟书架,然后向集合中添加有具体图书信息的图书对象,此时书架上面便有了图书。
    3、用户购买图书是通过在控制台键盘输入图书编号和购买数量的方式进行的,如果图书编号正确,且购买数量在库存数量之内,则图书购买成功,并将此图书的销售信息保存读到 csv 文件中,同时要将库存数量减少。
    4、查询图书信息时,可以通过 Scanner 类的 nextInt()方法从控制台获取图书编号,之后根据这个编号到书架上查询这本书的信息。如果查到了图书的信息,从控制台获取购买的数量之后,判断库存是否充足,如果充足则将此本书的所有信息进行封装。
    5、将图书的销售信息写到 csv 文件之前,需先拼凑好 csv 文件名,再判断本地是否已存在此文件。这里可通过输入流尝试获取此文件的字节流,如果获取成功,则证明这个文件已存在,那么就通过输出流向文件末尾追加销售信息;如果获取失败,即异常,说明之前并没有生成当日的销售信息,需要新建此文件。
    6、将封装的信息写入 csv 文件中时,csv 格式的文件以纯文本形式存储表格数据,写入文件时可用 Excel 进行编辑。
    7、在拼凑 csv 文件名时,需要获取当日的日期。这里可以通过以下代码获取并拼凑 csv 文件名。

    1. DataFormat format = new SimpleDateFmat("yyyyMMdd"); //定义日期格式
    2. String name = "销售记录" + format.format(date) + ".csv"; //拼接文件名


    【实现代码】

    1、将图书信息封装为一个实体类 Books,具体如下所示。

    1. public class Books {
    2. int id; //图书编号
    3. String name; //图书名称
    4. double price; //图书单价
    5. int number; //图书数量
    6. double money; //交易总价
    7. String publish; //图书出版社
    8. //构造方法
    9. public Books(int id, String name, double price, int number, double money, String publish) {
    10. super();
    11. this.id = id;
    12. this.name = name;
    13. this.price = price;
    14. this.number = number;
    15. this.money = money;
    16. this.publish = publish;
    17. }
    18. @Override
    19. public String toString() {
    20. // TODO Auto-generated method stub
    21. String message = "图书编号:" + id + " 图书名称:《" + name + "》 出版社:" + Publish + " 单价:" + price + " 库存数量:" + number;
    22. return message;
    23. }
    24. public void setNumber(int number) {
    25. this.number = number;
    26. }
    27. }

    2~7 行代码定义了用于标识图书的信息各种字段,9~17 行代码定义了一个有参的构造方法,用于对象的创建和初始化。19~23 行,重写了 toString()方法,用于返回图书的详细信息。24~27 行的代码定义了一个 setNumber()方法,用于修改如数的库存量。

    2、定义 RecordBooksOrder 类来记录和操作图书信息,具体如下所示。

    import java.util.*;
    
    public class RecordBooksOrder {
        //创建书架
        static ArrayList<Books> booksList = new ArrayList<Books>();
        public static void main(String[] args) {
            //初始化书架
            init();
            //将书架上所有图书信息打印出来
            for (int i = 0; i < booksList.size(); i++) {
                System.out.println(booksList.get(i));
            }
            /*用增强 for 循环遍历并打印“书架”
              for(Books book : bookList) {
                  System.out.println(book);
              }
             */ 
            while (true) {
                //获取控制台输入的信息
                Scanner scan = new Scanner(System.in);
                System.out.println("请输入图书编号:");
                int bookId = scan.nextInt();
                Books stockBooks = getBooksById(bookId);    //根据图书编号查找图书信息
                if (stockBooks != null) {    //判断是否存在此图书
                    System.out.println("当前图书信息" + stockBooks);
                    System.out.println("请输入购买数量:");
                    int bookNumber = scan.nextInt();
                    if (bookNumber <= stockBooks.number) {    //判断库存是否足够
                        //将输入信息封装成 Books 对象
                        Books books = new Books(stockBooks.id, stockBooks.name, stockBooks.price, bookNumber, stockBooks.price * bookNumber, stockBooks.publish);
                        //将本条数据保存至本地
                        FileUtil.saveBooks(books);
                        //修改库存数量
                        stockBooks.setNumber(stockBooks.number - bookNumber);
                    }else {
                        System.out.println("库存不足!");
                    }
                }else {
                    System.out.println("图书编号输入错误!");
                }
            }
        }
        private static void init() {
            Books goods1 = new Books(101, "Java基础入门", 44.50, 100, 4450.00, "清华大学出版社");
            Books goods2 = new Books(102, "Java编程思想", 108.00, 50, 5400, "机械工业出版社");
            Books goods3 = new Books(103, "疯狂Java讲义", 99.00, 100, 9900.00, "电子工业出版社");
            booksList.add(goods1);
            booksList.add(goods2);
            booksList.add(goods3);
        }
        private static Books getBooksById(int bookId) {
            //判断当前的书的 ID 与输入的图书编号是否一致
            for (int i = 0; i < booksList.size(); i++) {
                Books thisBooks = booksList.get(i);
                if (bookId == thisBooks.id) {
                    return thisBooks;
                }
            }
            /*for(Books thisBooks : book:List){
                 if(bookId == thisBooks.id) {
                     return thisBooks;
                 }
              }
            */
            return null;    //如果没找到 ID 对应的图书,则返回 null
        }
    }
    

    第 6 行代码创建了 ArrayList 类型的全局变量作为书店的书架。43~50 行代码,初始化了书架信息,向 ArrayList 中添加了 3 本书的信息,并在 11~13 行代码中,通过 for 循环进行展示。18~41 行代码使用 while 循环来获取和处理用户输入信息,每次循环先由第 20~22 行代码从控制台获取图书编号的数据,再由第 51~65 行的代码根据图书编号查询到图书信息。当获得的图书不为空时,可从 25~27 行代码获得购买的数量,通过第 28 行代码判断库存是否充足,如果库存足够,可通过第 30 行代码将所有数据封装,再利用第 31 行代码调用 FileUtil 类中的 saveBooks()方法,将其保存到本地。最后再第 34 行代码中调用 setNumber()方法,修改库存。

    3、定义工具类 FileUtil 保存图书信息,具体如下所示。

    import java.io.*;
    import java.text.*;
    import java.util.Date;
    
    public class FileUtil {
        public static final String SEPARATE_FIELD = ",";    //字段分隔 英文逗号
        public static final String SEPARATE_LINE = "\r\n";    //行分隔
        /**
         * 保存图书信息
         * @param books
         */
        public static void saveBooks(Books books) {
            DateFormat format = new SimpleDateFormat("yyyyMMDd");    //定义日期格式
            String name = "销售记录" + format.format(new Date()) + ".csv";    //拼接文件名
            try {
                FileInputStream in = new FileInputStream(name);
                //判断本地是否存在此文件
                if (in != null) {
                    in.close();    //关闭输入流
                    createFile(name, true, books);    //存在文件,采取修改文件方式
                }
            }catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                createFile(name, false, books);    //不存在文件,采取新建文件方式
            }catch (IOException e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        }
        /**
            * 将图书的售出信息保存到本地,可通过 label 标识来判断是修改文件还是创建文件
         * @param name 文件名
         * @param label 文件已存在的标识 true:已存在则修改 false:不存在则新建
         * @param books 图书信息
         */
        public static void createFile(String name, boolean label, Books books) {
            BufferedOutputStream out = null;
            StringBuffer sbf = new StringBuffer();
            try {
                if (label) {    //说明已存在当天的文件,则在文件内容后追加新数据
                    out = new BufferedOutputStream(new FileOutputStream(name, true));
                } else {    //不存在当天文件,新建文件
                    out = new BufferedOutputStream(new FileOutputStream(name));
                    //创建表头
                    String[] fieldSort = new String[] {"图书编号","图书名称","购买数量","单价","总价","出版社"};
                    for (String fieldKye : fieldSort) {
                        //新建文件时,将表头存入文件中
                        sbf.append(fieldKye).append(SEPARATE_FIELD);
                    }
                }
                //写入购买图书信息到表格
                sbf.append(SEPARATE_LINE);    //追加换行符号
                sbf.append(books.id).append(SEPARATE_FIELD);
                sbf.append(books.name).append(SEPARATE_FIELD);
                sbf.append(books.number).append(SEPARATE_FIELD);
                sbf.append((double) books.price).append(SEPARATE_FIELD);
                sbf.append((double) books.money).append(SEPARATE_FIELD);
                sbf.append(books.Publish).append(SEPARATE_FIELD);
                //通过操作文件的输出流,把图书信息写入到文件中
                String str = sbf.toString();
                byte[] b = str.getBytes();
                for (int i = 0; i < b.length; i++) {
                    out.write(b[i]);
                }
            }catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }finally {
                if (out != null) {
                    try {
                        out.close();
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    当 saveBooks()方法被调用时,获取当前日期并格式化后,拼出了文件名,再通过第 24 行代码尝试获取此文件的字节输入流。当能够获取输入流时,可通过第 26~29 行代码,先关闭输入流,再在文件末尾追加信息。当不能获取输入流时则抛出异常,在异常处理中调用第 32 行代码的 createFile()方法,可以通过此方法中的 label 参数来区分,是新建文件还是在已有文件中追加内容。如果 label 值是 false 则新建文件,并写入表头,其中进行追加还是新操作,由构造函数的 append 参数来定义。然后利用第 60~66 行代码拼出一行数据,且在每次拼接之前都要加上换行符“\r\n”,每个字段之间通过“,”分歌字段,再利用第 68~72 行的代码写入文件。最后第 76~85 行代码关闭了输入流。