Java Fundamentals

Project: A Console Calculator

15 min Lesson 10 of 14

Project: A Console Calculator

You have reached the end of Java Fundamentals. In this final lesson you will apply everything you have learned — variables, primitive types, operators, type casting, Scanner, constants, wrapper classes, and clean naming conventions — to build a real, interactive console calculator.

This is not a toy example. The calculator will read two numbers and an operator from the user, validate the input, perform the correct calculation, and display a formatted result. Along the way you will see how the small pieces you learned fit together into a working program.

What the Finished Program Does

  • Prompts the user for a first number, an operator (+, -, *, /), and a second number.
  • Parses both numbers as double so decimal values work.
  • Evaluates the expression and prints the result.
  • Guards against division by zero.
  • Handles an unrecognised operator gracefully.
  • Asks whether the user wants to calculate again (a loop).

Step 1 — Skeleton and Imports

Start with the class and the Scanner you will need throughout the program:

import java.util.Scanner; public class ConsoleCalculator { // A constant for the exit keyword — easy to change in one place static final String EXIT_WORD = "no"; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("=== Java Console Calculator ==="); System.out.println("Type '" + EXIT_WORD + "' when asked to continue to quit."); // The main loop lives here — see Step 3 scanner.close(); } }
Why static final? EXIT_WORD is a named constant (lesson 7). Using a constant instead of the bare string "no" scattered through the code means you only change one line if the exit word ever changes.

Step 2 — Reading and Parsing the Inputs

Numbers arrive from the keyboard as text. Double.parseDouble() (a wrapper-class method from lesson 9) converts that text into a double. Wrap the parse inside a helper so the conversion stays in one place:

// Reads one line from the scanner and converts it to a double. // Returns Double.NaN (Not a Number) if the input is not valid. static double readDouble(Scanner scanner, String prompt) { System.out.print(prompt); String input = scanner.nextLine().trim(); try { return Double.parseDouble(input); } catch (NumberFormatException e) { System.out.println(" [!] That is not a valid number. Using 0 instead."); return 0.0; } }
scanner.nextLine().trim() — reading the whole line and trimming whitespace is safer than nextDouble(), which leaves a leftover newline character in the buffer and can confuse the next read.

Step 3 — The Calculation Logic

A switch statement matches the operator string and returns the result. Division by zero is caught before the division happens:

static double calculate(double a, String op, double b) { switch (op) { case "+": return a + b; case "-": return a - b; case "*": return a * b; case "/": if (b == 0) { System.out.println(" [!] Cannot divide by zero."); return Double.NaN; } return a / b; default: System.out.println(" [!] Unknown operator: " + op); return Double.NaN; } }
Never compare floating-point results with == for equality checks in real math. For guard logic such as b == 0 the comparison is fine because we are checking whether the user literally typed 0, not whether a computed value equals zero after arithmetic.

Step 4 — The Main Loop

Put the pieces together inside a do-while loop so the user can calculate as many times as they like:

public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("=== Java Console Calculator ==="); String again; do { double firstNumber = readDouble(scanner, "Enter first number : "); System.out.print("Enter operator (+ - * /) : "); String operator = scanner.nextLine().trim(); double secondNumber = readDouble(scanner, "Enter second number : "); double result = calculate(firstNumber, operator, secondNumber); if (!Double.isNaN(result)) { // Cast result to long for display if it is a whole number if (result == (long) result) { System.out.println("Result: " + (long) result); } else { System.out.printf("Result: %.4f%n", result); } } System.out.print("Calculate again? (yes / no) : "); again = scanner.nextLine().trim().toLowerCase(); } while (!again.equals(EXIT_WORD)); System.out.println("Goodbye!"); scanner.close(); }

The cast (long) result is a narrowing cast (lesson 5): when the result has no fractional part it prints as a clean integer instead of 6.0000. When there is a fractional part, printf("%.4f") shows four decimal places.

Step 5 — Complete Program (Assembled)

Here is the full, runnable source file:

import java.util.Scanner; public class ConsoleCalculator { static final String EXIT_WORD = "no"; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("=== Java Console Calculator ==="); String again; do { double firstNumber = readDouble(scanner, "Enter first number : "); System.out.print("Enter operator (+ - * /) : "); String operator = scanner.nextLine().trim(); double secondNumber = readDouble(scanner, "Enter second number : "); double result = calculate(firstNumber, operator, secondNumber); if (!Double.isNaN(result)) { if (result == (long) result) { System.out.println("Result: " + (long) result); } else { System.out.printf("Result: %.4f%n", result); } } System.out.print("Calculate again? (yes / no) : "); again = scanner.nextLine().trim().toLowerCase(); } while (!again.equals(EXIT_WORD)); System.out.println("Goodbye!"); scanner.close(); } static double readDouble(Scanner scanner, String prompt) { System.out.print(prompt); String input = scanner.nextLine().trim(); try { return Double.parseDouble(input); } catch (NumberFormatException e) { System.out.println(" [!] Not a valid number. Using 0 instead."); return 0.0; } } static double calculate(double a, String op, double b) { switch (op) { case "+": return a + b; case "-": return a - b; case "*": return a * b; case "/": if (b == 0) { System.out.println(" [!] Cannot divide by zero."); return Double.NaN; } return a / b; default: System.out.println(" [!] Unknown operator: " + op); return Double.NaN; } } }

Key Concepts Revisited

  • Variables & types (lesson 3): double firstNumber, String operator — each variable has a clear type and a meaningful name.
  • Operators (lesson 4): +, -, *, / applied to double operands.
  • Type casting (lesson 5): (long) result narrows the double to remove a redundant .0 suffix.
  • Scanner (lesson 6): scanner.nextLine() reads a full line safely; scanner.close() releases the resource.
  • Constants (lesson 7): static final String EXIT_WORD centralises the exit keyword.
  • Wrapper classes (lesson 9): Double.parseDouble() converts a String to a primitive double; Double.isNaN() checks for an invalid result.
Ideas to extend the project: add a modulo operator (%), a square-root option, or a history list that stores the last five calculations in a String[] array. Each addition reinforces a skill from this tutorial.

Summary

A real program is just small pieces connected thoughtfully. You read input with Scanner, parsed text to numbers with wrapper-class methods, routed logic with switch, protected against bad input, formatted output with printf, and looped with do-while. These are the same building blocks used in every Java program — whether a console tool or a large enterprise application. You are ready for the next tutorial.