Калькулятор расходов/доходов. Как сделать его проще?

Калькулятор расходов/доходов. Пользователь может ввести расход/доход: описание, сумму.

В файл сохраняются эти поля + дата записи.

В конец файла сохраняется итог по месяцам.

Сделайте базовый класс, и реализации наследников: на базе txt файла,

Как сделать так, чтоб текст продолжал записываться в файл, а не стирал предыдущую или есть другой, более корректный способ выполнить это задание?
Вот моя попытка:

int account = 10000;

char exit = 'n';
boolean isRecord = false;
Scanner scan = new Scanner(System.in);
do {
    System.out.println("1.  доходы");
    System.out.println("2.  расходы");
    System.out.println("3.  Выход");
    System.out.print("Пожалуйста, выберите 1-3:");
    int choose = scan.nextInt();
    do {
        if (choose >= 1 && choose <= 3) {
            break;
        } else {
            System.out.print("Ввод неправильный, и ввод:");
            choose = scan.nextInt();
        }
    } while (true);// Убедитесь, что ввод неверен
    switch (choose) {
        case 1:
            isRecord = true;
            System.out.print("Эта сумма дохода:");
            int in = scan.nextInt();
            account += in;
            System.out.print("Описание суммы дохода:");
            String input = scan.next();
            System.out.println("Дата: ");
            String date = scan.next();
            writeToFile(String.valueOf(in + input + date));        
            break;
        case 2:
            isRecord = true;
            System.out.print("Сумма этих расходов:");
            int out = scan.nextInt();
            account -= out;
            System.out.print("Описание расходов:");
            String output = scan.next();
            System.out.println(account);
            break;
        case 3:
            System.out.println("Подтвердите выход? Y/N");
            String str = scan.next();
            exit = str.charAt(0);
            break;
    }
    if (exit == 'y') {
        break;
    }
} while (true);
//
public static void writeToFile(String str) throws IOException {
    BufferedWriter writer = new BufferedWriter(new FileWriter("k.txt"));
    writer.write(str);
    writer.close();
}

Ответы (2 шт):

Автор решения: Котлетка еее

Есть такой вариант для решения вашей задачи:

    private static void writeToFile(String str) throws IOException {
    List<String> lines = new ArrayList<>();
    lines.add(str);

    Files.write(Paths.get("k.txt"),
            lines,
            StandardCharsets.UTF_8,
            StandardOpenOption.CREATE,
            StandardOpenOption.APPEND);
    }

Если файла не существует, он будет создан, а если существует - строка будет дописана в его конец. Кодировка - UTF-8. При этом происходит автоматический контроль за открытием-закрытием файлов, поэтому самостоятельно этим заниматься не нужно.

→ Ссылка
Автор решения: Котлетка еее

Ну, я попробовала сделать это. Все файлы:
MoneyTransfer.java:

import java.time.LocalDate;

/**
 * класс, представляющий собой переход денег
 * (расход или доход)
 */
public abstract class MoneyTransfer implements Comparable<MoneyTransfer> {
    protected int value;
    protected LocalDate date;
    protected String description;

    public static final String CURRENCY = "$";

    public LocalDate getDate() {
        return date;
    }

    public MoneyTransfer(int value, LocalDate date,  String description) {
        this.value = value;
        this.date = date;
        this.description = description;
    }

    /**
     * получить прибавление этого перехода денег к итоговой сумме,
     * то есть для расхода это будет value со знаком -,
     * а для дохода - value со знаком +
     */
    public abstract int getAdditionToSum();

    @Override
    public String toString() {
        return "Сумма: " + value + CURRENCY + " | Дата: " + date + " | Описание: " + description;
    }

    /**
     * сравнение объектов MoneyTransfer по их дате -
     * это нужно для сортировки
     */
    public int compareTo(MoneyTransfer other) {
        return date.compareTo(other.date);
    }
}

Revenue.java:

import java.time.LocalDate;

/**
 * класс, представляющий собой доход денежных средств
 */
public class Revenue extends MoneyTransfer {
    public Revenue(int value, LocalDate date, String description) {
        super(value, date, description);
    }

    @Override
    public int getAdditionToSum() {
        return value;
    }

    @Override
    public String toString() {
        return " * Доход * \n" + super.toString();
    }
}

Expense.java:

import java.time.LocalDate;

/**
 * класс, представляющий собой расход денежных средств
 */
public class Expense extends MoneyTransfer {
    public Expense(int value, LocalDate date, String description) {
        super(value, date, description);
    }

    @Override
    public int getAdditionToSum() {
        return -value;
    }

    @Override
    public String toString() {
        return " * Расход * \n" + super.toString();
    }
}

Account.java:

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDate;
import java.util.*;

/**
 * класс, представляющий собой счёт с денежными средствами
 */
public class Account {
    private List<MoneyTransfer> moneyTransfers = new ArrayList<>();

    private final String FILENAME = "account.txt";
    private final int INITIAL_VALUE;

    public Account(int initialValue) {
        this.INITIAL_VALUE = initialValue;
    }

    /**
     * рассчитать текущий баланс
     */
    public int calculateCurrentBalance() {
        int balance = INITIAL_VALUE;
        for (MoneyTransfer transfer : moneyTransfers) {
            balance += transfer.getAdditionToSum();
        }
        return balance;
    }

    /**
     * добавить денежный переход (расход или доход) в список
     */
    public void addMoneyTransfer(MoneyTransfer transfer) {
        moneyTransfers.add(transfer);
    }

    /**
     * записать информацию в файл
     */
    public void saveToFile() throws IOException {
        Collections.sort(moneyTransfers);

        String result = getAllMoneyTransfersString() + "\n\n" + getAllMonthsResultsString();
        writeToFile(result);
    }

    /**
     * получить строку со всеми доходами и расходами
     */
    private String getAllMoneyTransfersString() {
        StringBuilder sb = new StringBuilder("---------КАЛЬКУЛЯТОР ДОХОДОВ И РАСХОДОВ---------");
        for (MoneyTransfer transfer : moneyTransfers) {
            sb.append("\n").append(transfer.toString());
        }
        return sb.toString();
    }

    /**
     * получить строку с результатами по месяцам
     */
    private String getAllMonthsResultsString() {
        if (moneyTransfers.size() == 0) {
            return "";
        }
        StringBuilder sb = new StringBuilder("----РЕЗУЛЬТАТЫ ПО МЕСЯЦАМ----");

        int lastMonth = moneyTransfers.get(0).getDate().getMonthValue();
        int lastYear = moneyTransfers.get(0).getDate().getYear();
        int transferResultForMonth = moneyTransfers.get(0).getAdditionToSum();

        for (int i = 1; i < moneyTransfers.size(); i++) {
            MoneyTransfer transfer = moneyTransfers.get(i);
            if (!doesDateMatchMonthAndYear(transfer.getDate(), lastMonth, lastYear)) {
                String monthResultStr = createStringResultForMonthOfYear(transferResultForMonth, lastMonth, lastYear);
                sb.append("\n").append(monthResultStr);

                transferResultForMonth = 0;
            }

            lastMonth = transfer.getDate().getMonth().getValue();
            lastYear = transfer.getDate().getYear();
            transferResultForMonth += transfer.getAdditionToSum();
        }

        String monthResultStr = createStringResultForMonthOfYear(transferResultForMonth, lastMonth, lastYear);
        sb.append("\n").append(monthResultStr);

        return sb.toString();
    }

    /**
     * сформировать строку для записи результата по месяцу года
     */
    private String createStringResultForMonthOfYear(int transferResult, int month, int year) {
        StringBuilder sb = new StringBuilder();
        sb.append("За ").append(month).append(" месяц ");
        sb.append(year).append(" года : ");
        sb.append(transferResult).append(MoneyTransfer.CURRENCY);

        return sb.toString();
    }

    /**
     * проверка, подходит ли дата под месяц и год
     */
    private boolean doesDateMatchMonthAndYear(LocalDate date, int month, int year) {
        return date.getMonthValue() == month && date.getYear() == year;
    }

    /**
     * запись строки в файл: пересоздать файл для записи
     */
    private void writeToFile(String str) throws IOException {
        List<String> lines = Arrays.asList(str.split("\n"));

        Files.write(Paths.get(FILENAME),
                lines,
                StandardCharsets.UTF_8,
                StandardOpenOption.CREATE);
    }
}

Main.java:

import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.Scanner;

public class Main {
    private static final Scanner scanner = new Scanner(System.in);
    private static Account account;

    public static void main(String[] args) {
        final int INITIAL_VALUE = 10000;
        account = new Account(INITIAL_VALUE);

        boolean continuing = true;
        while (continuing) {
            printMenu();
            int choice = askUserForInputUntilItIsCorrect();

            try {
                continuing = handleMenuChoiceAndReturnContinuing(choice);
            } catch (IncorrectInputException ex) {
                System.out.println(ex.getMessage());
            } catch (IOException ex) {
                System.out.println("Ошибка при работе с файлами: " + ex.getMessage());
            }

            printAccountBalance();
        }
    }

    /**
     * обработать ввод пункта меню
     * и вернуть true - если нужно продолжать,
     * false - если пора выйти из программы
     */
    private static boolean handleMenuChoiceAndReturnContinuing(int choice) throws IncorrectInputException, IOException {
        switch (choice) {
            case 1:
                handleRevenue();
                break;
            case 2:
                handleExpense();
                break;
            case 3:
                if (doesUserReallyWantsToExit()) {
                    return false;
                }
                break;
        }
        return true;
    }

    /**
     * вывести баланс в консоль
     */
    private static void printAccountBalance() {
        System.out.println("Текущий баланс: " + account.calculateCurrentBalance() + MoneyTransfer.CURRENCY);
    }

    /**
     * обработать ввод и сохранение дохода
     */
    private static void handleRevenue() throws IncorrectInputException, IOException {
        System.out.print("Введите сумму дохода -> ");
        int revenueValue = getIntFromInputOrThrown();

        System.out.print("Введите описание дохода -> ");
        String description = scanner.nextLine();

        System.out.print("Дата (формат ГГГГ-ММ-ДД) -> ");
        LocalDate date = getDateFromInputOrThrown();

        Revenue revenue = new Revenue(revenueValue, date, description);
        account.addMoneyTransfer(revenue);
        account.saveToFile();
    }

    /**
     * обработать ввод и сохранение расхода
     */
    private static void handleExpense() throws IOException, IncorrectInputException {
        System.out.print("Введите сумму расхода -> ");
        int expenseValue = getIntFromInputOrThrown();

        System.out.print("Введите описание расхода -> ");
        String description = scanner.nextLine();

        System.out.print("Дата (формат ГГГГ-ММ-ДД) -> ");
        LocalDate date = getDateFromInputOrThrown();

        Expense expense = new Expense(expenseValue, date, description);
        account.addMoneyTransfer(expense);
        account.saveToFile();
    }

    /**
     * подвердить выход
     */
    private static boolean doesUserReallyWantsToExit() {
        System.out.println("Подтвердите выход? y/n");
        char answer = scanner.nextLine().charAt(0);

        final char EXIT_YES = 'y';
        return answer == EXIT_YES;
    }

    /**
     * спрашивать ввод у пользователя до тех пор,
     * пока он не напишет корректный пункт меню
     */
    private static int askUserForInputUntilItIsCorrect() {
        do {
            try {
                int number = getIntFromInputOrThrown();
                checkIsCorrectMenuOption(number);
                return number;
            } catch (IncorrectInputException ex) {
                System.out.println(ex.getMessage());
                System.out.print("Попробуйте ввести пункт меню ещё раз -> ");
            }
        } while (true);
    }

    /**
     * конвертация ввода пользователя в опцию меню,
     * может выбросить исключение, если ввод некорректный
     */
    private static void checkIsCorrectMenuOption(int number) throws IncorrectInputException {
        if (number < 1 || number > 3) {
            throw new IncorrectInputException("Нужно ввести число от 1 до 3");
        }
    }

    /**
     * получить число из ввода,
     * если некорректный ввод - выбросить исключение
     */
    private static int getIntFromInputOrThrown() throws IncorrectInputException {
        String input = scanner.nextLine();
        try {
            return Integer.parseInt(input);
        } catch (NumberFormatException ex) {
            throw new IncorrectInputException("Нужно ввести целое число");
        }
    }

    /**
     * получить дату из ввода,
     * если некорректный ввод - выбросить исключение
     */
    private static LocalDate getDateFromInputOrThrown() throws IncorrectInputException {
        String input = scanner.nextLine();
        try {
            return LocalDate.parse(input);
        } catch (DateTimeParseException ex) {
            throw new IncorrectInputException("Нужно ввести корректный формат даты");
        }
    }

    /**
     * вывод меню в консоль
     */
    private static void printMenu() {
        System.out.println("1. Доходы");
        System.out.println("2. Расходы");
        System.out.println("3. Выход");
        System.out.print("Пожалуйста, выберите пункт меню (1-3) -> ");
    }
}

IncorrectInputException.java:

/**
 * исключение для случая,
 * когда пользователь неправильно вводит информацию
 */
public class IncorrectInputException extends Exception {
    public IncorrectInputException(String message) {
        super(message);
    }
}

Результаты работы программы: введите сюда описание изображения

введите сюда описание изображения

→ Ссылка