'Java - ATM Banking login with 3 times try pin lock

I'm new to java programming. I'm trying to make a banking system log-in that locks the user after entering the wrong pin 3 times. I'm trying to merge 2 pieces of code. Any ideas why "Wrong Pin. Try Again" shows up twice in the console?

ATM class

import java.util.Scanner;

public class ATM {
    public static void useATM(BankAccount bankAccount) {
        Scanner scanner = new Scanner(System.in);
        char option = '\0';
        do {
            Menu.showMenu();
            option = scanner.next().charAt(0);
            switch (option) {
                case 'A':
                    System.out.println(bankAccount.getBalance());
                    break;
                case 'B':
                    System.out.println("Enter an amount to deposit: ");
                    int amountToDeposit = scanner.nextInt();
                    bankAccount.deposit(amountToDeposit);
                    break;
                case 'C':
                    System.out.println("Enter an amount to withdraw: ");
                    int amountToWithdraw = scanner.nextInt();
                    bankAccount.withdraw(amountToWithdraw);
                    break;
                case 'X':
                    System.out.println("The transaction is over. ");
                    break;
                default:
                    System.out.println("Not a valid option. Choose another option.");
                    break;
            }
        } while (option != 'X');
    }
}

BankAccount class

public class BankAccount {
    String IBAN;
    int balance;
    String pin;

    public BankAccount(String IBAN, int balance, String pin) {
        this.IBAN = IBAN;
        this.balance = balance;
        this.pin = pin;
    }

    public int getBalance() {
        return this.balance;
    }

    public void deposit(int amount) {
        this.balance = this.balance + amount;
    }

    public void withdraw(int amount) {
        if (amount <= this.balance) {
            this.balance = this.balance - amount;
            System.out.println("You withdrew " + amount);
        } else {
            System.out.println("Not enough money. ");
        }
    }
}

BankingSystemWithPin main class

import java.util.Scanner;

public class BankingSystemWithPin {
    public static void main(String[] args) {
        BankAccount bankAccount1 = new BankAccount("RO29455302311322", 200, "1234");
        BankAccount bankAccount2 = new BankAccount("RO43593530521134", 600, "7530");
        BankAccount[] bankAccounts = {bankAccount1, bankAccount2};
        Scanner scanner = new Scanner(System.in);
        Menu.welcome();
        String pin = scanner.nextLine();
        BankAccount currentBankAccount = BankingSystemWithPin.getBankAccountByPin(bankAccounts, pin);
        ATM.useATM(currentBankAccount);
    }
        public static BankAccount getBankAccountByPin(BankAccount[] bankAccounts, String pin) {
        for (BankAccount bankAccount : bankAccounts) {
            if (bankAccount.pin.equals(pin)) {
                return bankAccount;
            } else if (!bankAccount.pin.equals(pin) ) {
                System.out.println("Wrong Pin. Try Again.");
            }
        }
        return null;
    }
}

Menu class

public class Menu {
    public static void showMenu() {
        System.out.println("Enter an option: ");
        System.out.println("A. Check balance ");
        System.out.println("B. Deposit ");
        System.out.println("C. Withdraw ");
        System.out.println("X. Exit");
    }
        public static void welcome() {
        System.out.println("Welcome! ");
        System.out.println("Please insert your pin: ");
    }
}

For the next piece of code, I'm trying to merge it with my login system. What is the proper way to merge it into the main code? Should I try to paste it in BanckAccount class, or make a method to call it in main?

import java.util.Scanner;

public class PinLockout {
   
            Scanner scanner = new Scanner(System.in);
            int pin = 1234;
            int tries = 0;
            System.out.println("ENTER YOUR PIN: ");
            int entry = scanner.nextInt();
            tries++;
            while (entry != pin && tries < 3) {
                System.out.println("\nINCORRECT PIN. TRY AGAIN.");
                System.out.println("ENTER YOUR PIN: ");
                entry = scanner.nextInt();
                tries++;
            }
            if (entry == pin)
                System.out.println("\nPIN ACCEPTED. ACCESS GRANTED.");
            else if (tries >= 3)
                System.out.println("\nYOU HAVE RUN OUT OF TRIES. ACCOUNT LOCKED.");
        }
    }

}


Solution 1:[1]

Any idea why the "Wrong Pin. Try Again" shows up twice in console?

Yes...In your BankingSystemWithPin.getBankAccountByPin() method you have your for loop used to iterate through the different bank accounts to locate a related pin number. This is fine for your specific use case at this point however you do need to get rid of that else if{} code block and here is why:

If for example you have ten specific accounts and each account contains a unique pin number and the pin number entered was related to the fifth account then upon each iteration up to the 5th one the else if condition is met and therefore outputs Wrong Pin. Try Again.. The fifth iteration holds the correct PIN and then exits the method with a return bankAccount;. What you would see within the console window is:

Wrong Pin. Try Again.
Wrong Pin. Try Again.
Wrong Pin. Try Again.
Wrong Pin. Try Again.
Enter an option: 
A. Check balance 
B. Deposit 
C. Withdraw 
X. Exit

You don't want that else if code block in that for loop so just delete it. Instead, place the following code line directly after for for loop block:

System.out.println("Wrong Pin. Try Again.");

Remember, because you are exiting the method anyways within the for loop if a correct pin number can be found related to a bank account, the above line will never be reached. It will only be reached if the supplied PIN number can not be found. Your getBankAccountByPin() method should look something like this:

public static BankAccount getBankAccountByPin(BankAccount[] bankAccounts, String pin) {
    for (BankAccount bankAccount : bankAccounts) {
        if (bankAccount.pin.equals(pin)) {
            System.out.println();
            System.out.println("Account Number: " + bankAccount.IBAN);
            return bankAccount;
        } 
    }
    System.err.println("Wrong PIN. Try Again.");
    System.err.println();
    return null;
}

That's not all...before carrying on after the call to the above method you need to ensure that it actually supplied a bank account number. After all, if the pin number supplied is wrong then there will be no bank account number, null will be returned instead and you don't want to carry on with a null. You need to validate that a bank account number was indeed acquired before showing the menu. To do this you will need to place the call to the getBankAccountByPin() method within a while loop like this:

import java.util.Scanner;

public class BankingSystemWithPin {
    
    private static int pinAttempts = 0;
    
    public static void main(String[] args) {
        BankAccount bankAccount1 = new BankAccount("RO29455302311322", 200, "1234");
        BankAccount bankAccount2 = new BankAccount("RO43593530521134", 600, "7530");
        BankAccount[] bankAccounts = {bankAccount1, bankAccount2};
        Scanner scanner = new Scanner(System.in);
        BankAccount currentBankAccount = null;
        while (currentBankAccount == null) {
            pinAttempts++;
            if (pinAttempts > 3) {
                System.err.println();
                System.err.println("You have exceeded the allowable number of login attempts!");
                System.err.println("The transaction is over. ");
                System.exit(0);
            }
            Menu.welcome();
            String pin = scanner.nextLine();
            currentBankAccount = BankingSystemWithPin.getBankAccountByPin(bankAccounts, pin);
        }
        
        // PIN correct and account number acquired!
        ATM.useATM(currentBankAccount);
    }
    
    public static BankAccount getBankAccountByPin(BankAccount[] bankAccounts, String pin) {
        for (BankAccount bankAccount : bankAccounts) {
            if (bankAccount.pin.equals(pin)) {
                System.out.println();
                System.out.println("Account Number: " + bankAccount.IBAN);
                return bankAccount;
            } 
        }
        System.err.println("Wrong Pin. Try Again.");
        System.err.println();
        return null;
    }
}

Now, for locking out a User. How will you ever know which User to Lock-out? There is no way of locking-out a User for the simple reason that you will never know who to lock out since there is no ATM card involved. You can however shut down the application and force the User to restart it in order to try again after three failed attempts.

This is obviously best done where you try to validate the PIN and acquire the bank account number related to it...the BankingSystemWithPin class. The above code also demonstrates this by adding a private integer class member variable named pinAttempts and wrapping the call to the getBankAccountByPin() method within a while loop. This keeps things compact and simple.

Solution 2:[2]

Because you are creating two constructors and also you are using a for loop inside validation, the loop runs two times and you get two warning messages. So I tried the below code.

import java.util.Scanner;

public class BankingSystemWithPin {
    public static void main(String[] args) {
        BankAccount bankAccount1 = new BankAccount("RO29455302311322", 200, "1234");
        BankAccount bankAccount2 = new BankAccount("RO43593530521134", 600, "7530");
        BankAccount[] bankAccounts = {bankAccount1, bankAccount2};
        Scanner scanner = new Scanner(System.in);
        Menu.welcome();
        String pin = scanner.nextLine();
        BankAccount currentBankAccount = BankingSystemWithPin.getBankAccountByPin(bankAccounts, pin);
        ATM.useATM(currentBankAccount);
    }
        public static BankAccount getBankAccountByPin(BankAccount[] bankAccounts, String pin) {
        for (BankAccount bankAccount : bankAccounts) {
            if (bankAccount.pin.equals(pin)) {
                return bankAccount;
            }
        }
        System.out.println("Wrong Pin. Try Again.");
        return null;
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 DevilsHnd
Solution 2 karel