Building a Personal Finance Manager Project in Java

In this tutorial, we will create a basic Personal Finance Manager (PFM) using Core Java. This application will allow users to add, edit, delete, list, and summarize financial transactions, helping them to track their income and expenses effectively.

Overview

Our finance manager will consist of the following components:
  • Transaction.java: Represents a financial transaction with attributes such as id, type, amount, and description.
  • FinanceManager.java: Manages the list of transactions, including add, edit, delete, and summarize functionalities.
  • Main.java: Contains the application's entry point, providing a simple text-based user interface for interacting with the finance manager.

Step 1: Creating the Transaction Class

Start by defining the Transaction class in the net.javaguides.pfm package. This class will store the details of a financial transaction.
package net.javaguides.pfm;

import java.io.Serializable;

public class Transaction implements Serializable {
    private static final long serialVersionUID = 1L;

    private int id;
    private String type;
    private double amount;
    private String description;

    public Transaction(int id, String type, double amount, String description) {
        this.id = id;
        this.type = type;
        this.amount = amount;
        this.description = description;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public double getAmount() {
        return amount;
    }

    public void setAmount(double amount) {
        this.amount = amount;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "Transaction{" +
                "id=" + id +
                ", type='" + type + '\'' +
                ", amount=" + amount +
                ", description='" + description + '\'' +
                '}';
    }
}

Step 2: Implementing the FinanceManager Class 

Next, create the FinanceManager class. This class manages transactions by providing methods to add, edit, delete, list, and summarize them. It uses serialization to persist transactions to a file.
package net.javaguides.pfm;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class FinanceManager {
    private List<Transaction> transactions = new ArrayList<>();
    private final String filePath = "transactions.dat";

    public FinanceManager() {
        loadTransactions();
    }

    public void addTransaction(Transaction transaction) {
        transactions.add(transaction);
        saveTransactions();
    }

    public void editTransaction(int id, String type, double amount, String description) {
        transactions.stream()
                .filter(transaction -> transaction.getId() == id)
                .findFirst()
                .ifPresent(transaction -> {
                    transaction.setType(type);
                    transaction.setAmount(amount);
                    transaction.setDescription(description);
                    saveTransactions();
                });
    }

    public void deleteTransaction(int id) {
        transactions.removeIf(transaction -> transaction.getId() == id);
        saveTransactions();
    }

    public void listTransactions() {
        transactions.forEach(System.out::println);
    }

    private void saveTransactions() {
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filePath))) {
            out.writeObject(transactions);
        } catch (IOException e) {
            System.out.println("Error saving transactions.");
        }
    }

    @SuppressWarnings("unchecked")
    private void loadTransactions() {
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(filePath))) {
            transactions = (List<Transaction>) in.readObject();
        } catch (FileNotFoundException e) {
            System.out.println("No previous transactions found. Starting fresh.");
        } catch (IOException | ClassNotFoundException e) {
            System.out.println("Error loading transactions.");
        }
    }

    public void summarizeTransactions() {
        double incomeTotal = transactions.stream()
                .filter(t -> t.getType().equalsIgnoreCase("Income"))
                .mapToDouble(Transaction::getAmount)
                .sum();

        double expenseTotal = transactions.stream()
                .filter(t -> t.getType().equalsIgnoreCase("Expense"))
                .mapToDouble(Transaction::getAmount)
                .sum();

        System.out.println("Total Income: INR " + incomeTotal);
        System.out.println("Total Expenses: INR " + expenseTotal);
        System.out.println("Net Flow: INR " + (incomeTotal - expenseTotal));
    }

    public int getNextId() {
        return transactions.isEmpty() ? 1 : transactions.get(transactions.size() - 1).getId() + 1;
    }

}

addTransaction(): Adds a new transaction to the transactions list and then saves the updated list to the file using saveTransactions().

editTransaction(): Finds a transaction by its ID and updates its type, amount, and description if found. After updating, it saves the changes to the file.

deleteTransaction(): Removes a transaction from the list based on its ID and then saves the updated list to the file.

listTransactions(): Iterates and prints all transactions in the list to the console.

saveTransactions(): Serializes the transactions list to the specified file (transactions.dat) using ObjectOutputStream, allowing transactions to be persisted across sessions.

loadTransactions(): Deserializes the transactions list from the file using ObjectInputStream at the start of the program. If the file doesn't exist or cannot be read, it handles exceptions gracefully, either by indicating that no previous transactions were found or by reporting an error in loading transactions.

summarizeTransactions(): Calculates and displays the total income, total expenses, and net flow (income - expenses). It uses streams to filter transactions by type and to sum up amounts.

getNextId(): Generates the next transaction ID by finding the highest current ID in the list and adding one to it. If there are no transactions, it starts from 1.

Step 3: Creating the Main Class with UI Methods

The Main class provides a simple text-based UI for the application. It includes methods for each operation (add, edit, delete, list, summarize transactions) and handles user input through a menu system.

package net.javaguides.pfm;

import java.util.InputMismatchException;
import java.util.Scanner;

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

    public static void main(String[] args) {
        FinanceManager manager = new FinanceManager();

        while (true) {
            try {
                System.out.println("\nPersonal Finance Manager");
                System.out.println("1. Add Transaction");
                System.out.println("2. Edit Transaction");
                System.out.println("3. Delete Transaction");
                System.out.println("4. List Transactions");
                System.out.println("5. Summarize Transactions");
                System.out.println("6. Exit");
                System.out.print("Enter your choice: ");
                int choice = scanner.nextInt();
                scanner.nextLine(); // Consume newline

                switch (choice) {
                    case 1:
                        addTransactionUI(manager);
                        break;
                    case 2:
                        editTransactionUI(manager);
                        break;
                    case 3:
                        deleteTransactionUI(manager);
                        break;
                    case 4:
                        manager.listTransactions();
                        break;
                    case 5:
                        manager.summarizeTransactions();
                        break;
                    case 6:
                        System.out.println("Exiting...");
                        return;
                    default:
                        System.out.println("Invalid choice. Please select again.");
                        break;
                }
            } catch (InputMismatchException e) {
                System.out.println("Invalid input. Please try again.");
                scanner.nextLine(); // Consume the invalid input
            }
        }
    }

    private static void addTransactionUI(FinanceManager manager) {
        System.out.print("Enter type (Income/Expense): ");
        String type = scanner.nextLine();
        System.out.print("Enter amount: ");
        double amount = scanner.nextDouble();
        scanner.nextLine(); // Consume newline
        System.out.print("Enter description: ");
        String description = scanner.nextLine();
        manager.addTransaction(new Transaction(manager.getNextId(), type, amount, description));
    }

    private static void editTransactionUI(FinanceManager manager) {
        System.out.print("Enter transaction ID to edit: ");
        int id = scanner.nextInt();
        scanner.nextLine(); // Consume newline
        System.out.print("Enter new type (Income/Expense): ");
        String type = scanner.nextLine();
        System.out.print("Enter new amount: ");
        double amount = scanner.nextDouble();
        scanner.nextLine(); // Consume newline
        System.out.print("Enter new description: ");
        String description = scanner.nextLine();
        manager.editTransaction(id, type, amount, description);
    }

    private static void deleteTransactionUI(FinanceManager manager) {
        System.out.print("Enter transaction ID to delete: ");
        int id = scanner.nextInt();
        scanner.nextLine(); // Consume newline
        manager.deleteTransaction(id);
    }
}

Scanner Object: A Scanner instance named scanner is created for reading user input from the console. It's used throughout the class to gather inputs such as the user's menu choice, transaction details (type, amount, description), and transaction ID for editing or deletion.

FinanceManager Instance: An instance of the FinanceManager class named manager is created. This object is responsible for the actual management of transactions, including adding, editing, and deleting transactions, as well as listing and summarizing them.

Main Loop: The application runs inside an infinite loop, displaying a menu of options until the user decides to exit. This design ensures that the user can perform multiple operations without restarting the application.

Error Handling: The input operations within the loop are wrapped in a try-catch block to handle InputMismatchException, which may occur if the user enters an invalid input (e.g., entering a letter where a number is expected). Upon catching such an exception, the application prompts the user to enter their input again.

Menu System: The application presents a simple text-based menu with options to add, edit, delete, and list transactions, summarize transactions, and exit the program. The user selects an option by entering the corresponding number.

User Input for Operations: For actions that require further details (adding or editing a transaction), the application prompts the user for the necessary information (type, amount, description for adding; ID, new type, new amount, and new description for editing).

Delegating Actions to FinanceManager: Each operation selected by the user triggers a corresponding method call on the manager object to perform the actual operation. For example, choosing to add a transaction will call addTransactionUI(manager), which gathers transaction details from the user and then calls manager.addTransaction() with these details.

Utility Methods for UI Operations: The addTransactionUI(), editTransactionUI(), and deleteTransactionUI() methods serve as interfaces between the user and the FinanceManager. They handle specific UI logic, such as prompting the user for input and invoking the corresponding methods on the FinanceManager object.

Step 4: Running the Application 

To run the application: 
  •  Compile the Java files. 
  • Execute the Main class. 
You'll be presented with a menu to add, edit, delete, list, and summarize transactions. Follow the prompts to manage your financial transactions. 

Here is the output for your reference:
Personal Finance Manager
1. Add Transaction
2. Edit Transaction
3. Delete Transaction
4. List Transactions
5. Summarize Transactions
6. Exit
Enter your choice: 1
Enter type (Income/Expense): Income
Enter amount: 50000
Enter description: Salary

Personal Finance Manager
1. Add Transaction
2. Edit Transaction
3. Delete Transaction
4. List Transactions
5. Summarize Transactions
6. Exit
Enter your choice: 1
Enter type (Income/Expense): Expense
Enter amount: 10000
Enter description: Shopping

Personal Finance Manager
1. Add Transaction
2. Edit Transaction
3. Delete Transaction
4. List Transactions
5. Summarize Transactions
6. Exit
Enter your choice: 1
Enter type (Income/Expense): Expense
Enter amount: 10000
Enter description: Rent

Personal Finance Manager
1. Add Transaction
2. Edit Transaction
3. Delete Transaction
4. List Transactions
5. Summarize Transactions
6. Exit
Enter your choice: 4
Transaction{id=1, type='Income', amount=50000.0, description='Salary'}
Transaction{id=2, type='Expense', amount=10000.0, description='Shopping'}
Transaction{id=3, type='Expense', amount=10000.0, description='Rent'}

Personal Finance Manager
1. Add Transaction
2. Edit Transaction
3. Delete Transaction
4. List Transactions
5. Summarize Transactions
6. Exit
Enter your choice: 5
Total Income: INR 50000.0
Total Expenses: INR 20000.0
Net Flow: INR 30000.0

Personal Finance Manager
1. Add Transaction
2. Edit Transaction
3. Delete Transaction
4. List Transactions
5. Summarize Transactions
6. Exit
Enter your choice: 6
Exiting...

Conclusion

Congratulations! You've built a basic Personal Finance Manager using Core Java. This application demonstrates essential Java programming concepts such as class design, serialization, file I/O, and handling user input. Feel free to expand this project by adding features like categorizing transactions, filtering transactions by date or type, or even integrating it with a database for more robust data management. Happy coding!

Comments