Crafting a Java Banking System: Lessons in OOP, SQLite, and Console UI Design

Table of contents
- Crafting a Java Banking System: Lessons in OOP, SQLite, and Console UI Design
- The Big Picture: Structure and Workflow of the Bank Account System
- Harnessing Java and OOP
- Adding Authentication with Login
- Integrating SQLite for Persistent Storage
- Handling User Input with Validation
- Designing the Console UI with ASCII Art
- Formatting Code for Clarity
- Adding Features Like Money Transfer
- Creating Cross-Platform Run Scripts
- Key Lessons Learned
- Conclusion
Crafting a Java Banking System: Lessons in OOP, SQLite, and Console UI Design
Hi there! I’m excited to share my journey of building the Bank Account System, a terminal-based application developed in Java using Object-Oriented Programming (OOP) and SQLite. This project allows users to create accounts, log in, deposit money, withdraw funds, transfer money to others, and check their balance—all through a clean console interface. It features persistent data storage, ASCII art for a polished look, and scripts for easy setup on any platform. In this article, I’ll start with the big picture of how the Bank Account System is structured, then walk you through key features, code snippets, and lessons learned. You can explore the full project on GitHub: https://github.com/Rehancodecraft/Banking-System. Let’s get started!
The Big Picture: Structure and Workflow of the Bank Account System
The Bank Account System is a modular console app built with Java, designed to manage user accounts and transactions efficiently. It uses SQLite for data storage and provides a user-friendly interface with ASCII art. Here’s how it’s organized and how its parts work together.
Project Structure
The project files are arranged for clarity and ease of use:
Banking-System/
├── Bank/ // Java source files
│ ├── BankAccount.java // Manages accounts and transactions
│ ├── Database.java // Handles SQLite operations
│ ├── Main.java // Entry point and main menu
│ ├── Utility.java // UI and input handling
├── Bank.db // SQLite database file
├── Images/ // Screenshots for documentation
│ ├── AccountNotFound.png
│ ├── CreateAccount.png
│ └── ... (other images)
├── Libraries/ // External dependencies
│ └── sqlite-jdbc-3.42.0.0.jar // SQLite JDBC driver
├── out/ // Compiled Java classes
├── README.md // Project documentation
├── run.bat // Windows script
├── run.sh // Linux/macOS script
Bank Package: All Java classes are in the
Bank
folder, organizing the app’s logic.Bank.db: Stores account data persistently using SQLite.
Libraries: Contains the SQLite driver for database connectivity.
Scripts:
run.sh
andrun.bat
make it easy to compile and run the app on different platforms.
How It Works
The Bank Account System connects its components to deliver a smooth experience:
Main.java starts the app, showing a menu (via
Utility.java
) for users to log in or create accounts.BankAccount.java handles core actions like deposits and withdrawals, calling
Database.java
to save or fetch data.Database.java manages the SQLite database (
Bank.db
), ensuring accounts and transactions are stored permanently.Utility.java takes care of user inputs and displays, using ASCII art to make menus and receipts look nice.
When a user runs the app (using run.sh
or run.bat
), Main.java
launches the menu, and their choices (e.g., deposit, withdraw) trigger methods in BankAccount.java
, which interact with the database and display results via Utility.java
.
Harnessing Java and OOP
I chose Java because it runs on any platform and supports Object-Oriented Programming (OOP). OOP let me model real-world concepts—like bank accounts—as objects, making the code easier to manage and expand.
Code Example: The BankAccount
Class
The BankAccount
class is the heart of the Bank Account System. It stores account details and handles actions like depositing money:
public class BankAccount {
private String name;
private String account_no;
private int acct_type;
private int balance;
private String password;
public void depositAmount(BankAccount account) {
Utility.UserInterface.depositAmountDisplay();
int depositAmount = Utility.GetInputWithStyles.getDepositAmountInput();
Database.depositToDatabase(depositAmount, account.account_no, account.name);
Utility.UserInterface.depositAmountReceiptDisplay(account.name, account.account_no, depositAmount);
}
}
Encapsulation: Private fields like
name
andbalance
keep data safe.Abstraction: Methods like
depositAmount()
hide complex logic behind simple calls.Modularity: Each action has its own method, making the code reusable.
Lesson Learned
OOP kept my code organized. Thinking of accounts and transactions as objects, and splitting tasks into classes like BankAccount
and Database
, made the project much easier to handle.
Adding Authentication with Login
When users launch the Bank Account System, they see a welcome screen with options to log in or create an account. I built a login system that checks the user’s name and password against the database for a secure experience.
Code Example: Handling Login in BankAccount
The loginAccount()
method manages the login process:
public static BankAccount loginAccount() {
Utility.UserInterface.loginAccountDisplay();
String fullName = Utility.GetInputWithStyles.getFullNameInputForLoginAccount();
String password = Utility.GetInputWithStyles.getPasswordInputForLoginAccount();
BankAccount account = Database.loginAccount(fullName, password);
return account;
}
User Interface:
loginAccountDisplay()
shows a menu with ASCII art for a clean look.Input Handling:
getFullNameInputForLoginAccount()
andgetPasswordInputForLoginAccount()
safely collect user input.Database Check:
Database.loginAccount()
verifies the name and password in the database, returning the account if valid or showing an error if not.
Here’s the login screen after a successful login:
Logging in with full name and password, followed by a success message
Lesson Learned
Building the login feature taught me how to connect user inputs with database checks. Seeing the “Logged in Successfully” message made the Bank Account System feel like a real banking app!
Integrating SQLite for Persistent Storage
Initially, the Bank Account System stored data in memory, so accounts vanished when the app closed. I added SQLite to save data in a Bank.db
file, ensuring accounts and transactions persist across sessions.
Code Example: Setting Up SQLite in Database
The Database
class connects to SQLite and sets up a table for accounts:
public class Database {
static Connection connection;
public static void connectToDatabase() {
try {
final String DB_FILE = "Bank.db";
final String URL = "jdbc:sqlite:" + DB_FILE;
File dbFile = new File(DB_FILE);
boolean isNew = !dbFile.exists();
connection = DriverManager.getConnection(URL);
connection.setAutoCommit(false);
if (isNew) {
initializeDatabase();
}
} catch (Exception e) {
System.out.println("Error in database Connection");
e.printStackTrace();
}
}
private static void initializeDatabase() {
try {
String createTableSQL = "CREATE TABLE IF NOT EXISTS Accounts (" +
"Account_No VARCHAR(20) PRIMARY KEY, " +
"AccountHolder_Name VARCHAR(50), " +
"Account_Password VARCHAR(20), " +
"Account_Type INT, " +
"Balance INT);";
PreparedStatement createStmt = connection.prepareStatement(createTableSQL);
createStmt.executeUpdate();
connection.commit();
} catch (Exception e) {
System.out.println("Error initializing database.");
}
}
}
Connecting to SQLite:
connectToDatabase()
links Java toBank.db
, creating the file if it doesn’t exist.Creating a Table:
initializeDatabase()
sets up theAccounts
table to store account details.Safe Updates:
connection.commit()
ensures changes are saved reliably.
Account Creation in Action
Users can select “Create Account,” enter their name, password, and account type (savings or current), and the Bank Account System saves the data with a unique account number.
Here’s the account creation process:
Creating a new account with a generated account number
Challenges and Solutions
Learning SQL: I learned to use
PreparedStatement
for safe data insertion.Error Handling: Try-catch blocks catch issues like connection failures.
Saving Data: SQLite ensured accounts persisted, making the app more realistic.
Lesson Learned
Using SQLite taught me how to store data permanently. Seeing accounts reappear after restarting the Bank Account System felt like a big win!
Handling User Input with Validation
To make the Bank Account System user-friendly, I focused on handling inputs properly, preventing crashes from invalid entries like letters instead of numbers.
Code Example: Validating Inputs in Utility
The Utility.GetInputWithStyles
class validates inputs, such as deposit amounts:
public static int getDepositAmountInput() {
while (true) {
System.out.print(green + " Enter Deposit amount: " + reset);
String userInput = input.nextLine().trim();
if (userInput.matches("\\d+")) {
int amount = Integer.parseInt(userInput);
if (amount > 0) {
return amount;
} else {
UserInterface.invalidDepositAmountDisplay();
}
} else {
UserInterface.invalidDepositAmountDisplay();
}
}
}
Validation Loop: A
while
loop ensures the input is a positive number using regex (\\d+
).Error Messages:
invalidDepositAmountDisplay()
alerts users to invalid inputs.User-Friendly Prompts: Green-colored prompts guide users clearly.
Handling Errors Like Insufficient Balance
The withdraw()
method checks the balance before allowing withdrawals, showing an error if funds are insufficient.
Here’s what happens when a user tries to withdraw too much:
Error message when trying to withdraw more than the available balance
Challenges
Invalid Inputs: Regex ensured inputs were numbers, avoiding crashes.
Edge Cases: I added checks for negative amounts and insufficient funds.
Lesson Learned
Input validation made the Bank Account System reliable. It was satisfying to see the app catch errors and guide users to correct them.
Designing the Console UI with ASCII Art
I wanted the Bank Account System to look appealing despite being a console app, so I added ASCII art for menus and receipts, along with colors for visual clarity.
Code Example: Creating a Receipt in Utility
The depositAmountReceiptDisplay()
method formats a deposit receipt:
public static void depositAmountReceiptDisplay(String Name, String AccountNo, int amount) {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
String formattedDateTime = now.format(formatter);
final int totalWidth = 50;
String divider = "-".repeat(totalWidth);
String formattedName = String.format("%-" + (totalWidth - 12) + "s", Name);
System.out.println(yellow + " *" + green + " " + divider + reset + yellow + " *");
System.out.println(yellow + " *" + green + " " + yellow + bold + "Deposit Receipt" + reset + green + " " + yellow + " *");
System.out.println(yellow + " *" + green + " Account Holder : " + reset + " " + formattedName + yellow + " *");
// ... other fields
}
ASCII Art: Characters like
*
and-
create borders, designed using patorjk.com/software/taag.Colors: ANSI codes (e.g., green, yellow) highlight key elements.
Formatting:
String.format
aligns receipt fields neatly.
A Deposit Receipt in Action
After depositing money, the Bank Account System displays a receipt with the user’s details, amount, and timestamp.
Here’s a deposit receipt:
Deposit receipt with formatted ASCII borders and timestamp
Challenges
Alignment: I adjusted spacing to fit different terminals.
Color Support: Tested on Linux and Windows CMD to ensure colors displayed correctly.
Lesson Learned
ASCII art and colors made the Bank Account System engaging. It showed me that even a console app can offer a great user experience with thoughtful design.
Formatting Code for Clarity
As the Bank Account System grew, I prioritized clean, readable code to simplify debugging and future updates.
Code Example: A Clear Menu in BankAccount
The handleAccountServices()
method manages the main menu and user choices:
public void handleAccountServices(BankAccount account) {
Utility.UserInterface.bankServicesDisplay();
int servicesOption = Utility.GetInputWithStyles.getBankServicesInput();
while (servicesOption != 6) {
switch (servicesOption) {
case 1:
account.depositAmount(account);
exitFromBank();
Utility.UserInterface.bankServicesDisplay();
servicesOption = Utility.GetInputWithStyles.getBankServicesInput();
break;
case 2:
account.withdraw(account);
exitFromBank();
Utility.UserInterface.bankServicesDisplay();
servicesOption = Utility.GetInputWithStyles.getBankServicesInput();
break;
// ... other cases
}
}
Utility.UserInterface.exiting();
}
Indentation: Used 4 spaces for readability.
Clear Names: Descriptive names like
depositAmount()
andservicesOption
explain their purpose.Modular Code: Each menu option is a separate case, keeping the code organized.
Checking Account Details
The “Check Balance” option displays account details in a neat format, reflecting the clarity of the code structure.
Here’s the account details screen:
Account details displayed after checking balance
Lesson Learned
Clean code saved time during debugging and made the Bank Account System easier to share, as others could quickly understand it.
Adding Features Like Money Transfer
A standout feature in the Bank Account System is money transfer between accounts. Users select “Send Money,” enter the recipient’s account number and amount, and the app processes the transaction.
How It Works
Input: Collects the recipient’s account number and amount.
Validation: Checks if the recipient exists and if the sender has enough funds.
Database Update: Updates both accounts’ balances using
sendMoneyToDatabaseToOtherAccount()
.
If the recipient isn’t found, an error appears (e.g., “Account Not Found”). On success, a receipt shows the transaction details.
Lesson Learned
Money transfer made the Bank Account System feel more like a real bank. It taught me how to safely handle transactions between database records, which was a rewarding challenge.
Creating Cross-Platform Run Scripts
I added scripts to make running the Bank Account System simple for everyone, without needing to set up Java manually.
run.sh (Linux/macOS)
#!/bin/bash
javac -cp Libraries/sqlite-jdbc-3.42.0.0.jar Bank/*.java
java -cp "Libraries/sqlite-jdbc-3.42.0.0.jar;." Bank.Main
run.bat (Windows)
@echo off
javac -cp Libraries\sqlite-jdbc-3.42.0.0.jar Bank\*.java
java -cp "Libraries\sqlite-jdbc-3.42.0.0.jar;." Bank.Main
pause
Purpose: These scripts compile and run the app, including the SQLite library.
Usage: On Linux/macOS, use
chmod +x run.sh
then./run.sh
. On Windows, double-clickrun.bat
or run it in CMD.
Lesson Learned
These scripts made the Bank Account System more accessible. I also learned to manage external libraries in the classpath, a new skill for me.
Key Lessons Learned
This project taught me a lot:
OOP: Classes and methods made the code scalable.
SQLite: Persistent storage made the app realistic.
User Input: Validation ensured stability.
UI Design: ASCII art and colors enhanced the experience.
Code Organization: Clean code made the project shareable.
Conclusion
Building the Bank Account System was a fantastic experience. From login systems to ASCII art receipts, every step brought new lessons. The project is on GitHub (https://github.com/Rehancodecraft/Banking-System), with a README and screenshots. Try it out and share your feedback!
If you’re new to Java or seeking a fun project, I hope this inspires you. You can create amazing things with code and creativity. Happy coding!
Subscribe to my newsletter
Read articles from Rehan Shafiq directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
