Skip to lab content

WEEK 11 · OBJECT ORIENTED PROGRAMMING LAB

Swing calculator using GridLayout

EXPERIMENT 22Java 11+Swing desktop

THE SYLLABUS TASK

What you need to build

Write a Java program that works as a simple calculator. Use a grid layout to arrange buttons for the digits and for the +, -,*, /, % operations. Add a text field to display the result.
Full syllabus requirements: classes, fields and methods
Write a Java program that works as a simple calculator. Use a grid layout to arrange buttons for the digits and for the +, -,*, /, % operations. Add a text field to display the result.
Interaction format

Desktop buttons: digits, decimal point, + − * / %, = and C. Binary operations evaluate left-to-right; repeated = is not supported. Start a negative value by subtracting it from zero.

UNDERSTAND THE IDEA

Explanation

A four-by-four GridLayout arranges digit and main operator buttons, with % and C below. A read-only text field shows the current operand or result.

The Calculator controller keeps a stored operand, pending operator and fresh-entry flag. Pressing an operator can finish an earlier operation, so this is immediate left-to-right evaluation rather than a full expression parser. Zero divisor and nonfinite results show Error; C resets state.

TURN THE IDEA INTO STEPS

Step-by-step algorithm

  1. Append a digit or decimal point to the current entry.
  2. Store the first operand and selected operator.
  3. Read the second operand as a fresh entry.
  4. On =, validate and evaluate the pending operation.
  5. Show the result, or reset pending state after an error.

SEE THE CORE OPERATION

Flowchart

This chart focuses on the named operation or core OOP decision. Diamonds show conditions; labeled branches show outcomes. Read it with the complete algorithm and source for input handling and other operations. Scroll within the diagram on a phone.

Calculator button event

Calculator button event

Open full-size flowchart ↗

Java 11+

Complete Java source

Main.java

Download .java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Locale;

public class Main {
    static JPanel root(String title) {
        for(String key:new String[]{"Label.font","Button.font","RadioButton.font","CheckBox.font","TextField.font","TextArea.font","ComboBox.font"})UIManager.put(key,new Font(Font.SANS_SERIF,Font.PLAIN,18));
        JPanel panel=new JPanel(new BorderLayout(12,12));panel.setBorder(BorderFactory.createEmptyBorder(20,20,20,20));
        JLabel heading=new JLabel(title);heading.setFont(new Font(Font.SANS_SERIF,Font.BOLD,24));panel.add(heading,BorderLayout.NORTH);return panel;
    }
    static void show(String title,JPanel panel) {
        JFrame frame=new JFrame(title);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setContentPane(panel);frame.pack();frame.setLocationRelativeTo(null);frame.setVisible(true);
    }

    static double calculate(double a,double b,String operator){
        double result;
        switch(operator){case "+":result=a+b;break;case "-":result=a-b;break;case "*":result=a*b;break;case "/":if(b==0)throw new ArithmeticException("Division by zero");result=a/b;break;case "%":if(b==0)throw new ArithmeticException("Remainder by zero");result=a%b;break;default:throw new IllegalArgumentException("Choose an operation");}
        if(!Double.isFinite(result))throw new ArithmeticException("Non-finite result");return result;
    }
    static class Calculator {
        final JTextField display=new JTextField("0",22);
        private double stored;private String operator="";private boolean fresh=true;
        Calculator(){display.setEditable(false);display.setHorizontalAlignment(JTextField.RIGHT);display.setName("display");}
        void press(String key){
            try{
                if(key.equals("C")){stored=0;operator="";fresh=true;display.setText("0");return;}
                if(key.matches("[0-9.]")){
                    String current=fresh?"":display.getText();if(key.equals(".")&&current.contains("."))return;
                    if(current.length()>=16)return;if(key.equals(".")&&current.isEmpty())current="0";
                    display.setText(current+key);fresh=false;return;
                }
                double value=Double.parseDouble(display.getText());
                if(key.equals("=")){
                    if(operator.isEmpty()||fresh)throw new IllegalArgumentException("Enter both operands");
                    display.setText(Double.toString(calculate(stored,value,operator)));operator="";fresh=true;
                }else{
                    if(!operator.isEmpty()&&!fresh)value=calculate(stored,value,operator);
                    stored=value;operator=key;display.setText(Double.toString(value));fresh=true;
                }
            }catch(IllegalArgumentException|ArithmeticException e){display.setText("Error");operator="";fresh=true;}
        }
    }
    public static JPanel createPanel(){
        JPanel panel=root("Simple Calculator");Calculator calculator=new Calculator();calculator.display.setFont(new Font(Font.MONOSPACED,Font.PLAIN,24));
        JPanel center=new JPanel(new BorderLayout(10,10));center.add(calculator.display,BorderLayout.NORTH);JPanel keys=new JPanel(new GridLayout(4,4,8,8));
        for(String key:new String[]{"7","8","9","/","4","5","6","*","1","2","3","-","0",".","=","+"}){JButton button=new JButton(key);button.addActionListener(e->calculator.press(key));keys.add(button);}center.add(keys,BorderLayout.CENTER);panel.add(center,BorderLayout.CENTER);
        JPanel extras=new JPanel();for(String key:new String[]{"%","C"}){JButton button=new JButton(key);button.addActionListener(e->calculator.press(key));extras.add(button);}panel.add(extras,BorderLayout.SOUTH);return panel;
    }
    public static void main(String[] args){SwingUtilities.invokeLater(()->show("Simple Calculator",createPanel()));}

}

Copy a source file or download the project to preserve its file names.

USE THE RIGHT ENVIRONMENT

How to compile and run

Install a JDK (Java 11 or newer; JDK 17 is suitable), extract this project’s ZIP into its own folder, then open a terminal in that folder.

javac -encoding UTF-8 Main.java
java Main

This program opens a Swing desktop window. Run on your computer with a graphical desktop. A browser compiler or headless server cannot normally display this window. No console sample input is required.

Each experiment has its own folder because many use the public class Main. Compile and run one experiment at a time.

UNDERSTAND THE JAVA CODE

Line-by-line explanation

These steps follow the exact Java source above. Related statements are grouped; line numbers count blank lines and restart for each file. Start with main to follow execution, then open the constructors, methods or event handlers it uses.

Start with main →

Main.java

Lines 1–6Import Swing components, layouts and event types
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Locale;

public class Main {

javax.swing supplies buttons, labels, panels and frames. java.awt supplies layouts, colours and fonts. java.awt.event supplies listener/event types. Locale is available where text formatting needs it. Main matches the source filename.

Lines 7–8Set readable component fonts in a reusable panel helper
    static JPanel root(String title) {
        for(String key:new String[]{"Label.font","Button.font","RadioButton.font","CheckBox.font","TextField.font","TextArea.font","ComboBox.font"})UIManager.put(key,new Font(Font.SANS_SERIF,Font.PLAIN,18));

root configures the selected UIManager font keys to an 18-point sans-serif font before creating the form’s components. This affects the Java application, not the website’s CSS.

Lines 9–11Create a padded root panel with a heading
        JPanel panel=new JPanel(new BorderLayout(12,12));panel.setBorder(BorderFactory.createEmptyBorder(20,20,20,20));
        JLabel heading=new JLabel(title);heading.setFont(new Font(Font.SANS_SERIF,Font.BOLD,24));panel.add(heading,BorderLayout.NORTH);return panel;
    }

BorderLayout places the heading in NORTH and leaves CENTER for each experiment’s form. The empty border adds padding; the heading uses a larger bold font. Return the JPanel for further assembly.

Lines 12–14Show the completed panel in a desktop window
    static void show(String title,JPanel panel) {
        JFrame frame=new JFrame(title);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setContentPane(panel);frame.pack();frame.setLocationRelativeTo(null);frame.setVisible(true);
    }

show creates a JFrame, installs its content, packs it to component sizes, centres it and makes it visible. EXIT_ON_CLOSE ends this demonstration application when its window closes. The desktop main calls this helper on the event-dispatch thread.

Lines 16–20Calculate one checked floating-point operation
    static double calculate(double a,double b,String operator){
        double result;
        switch(operator){case "+":result=a+b;break;case "-":result=a-b;break;case "*":result=a*b;break;case "/":if(b==0)throw new ArithmeticException("Division by zero");result=a/b;break;case "%":if(b==0)throw new ArithmeticException("Remainder by zero");result=a%b;break;default:throw new IllegalArgumentException("Choose an operation");}
        if(!Double.isFinite(result))throw new ArithmeticException("Non-finite result");return result;
    }

The switch handles +, -, *, / and %. The explicit zero checks turn invalid division/remainder into ArithmeticException. Reject a non-finite final result. This method uses double arithmetic, unlike the separate integer-division exercise.

Lines 21–24Keep display, stored operand and entry state in one object
    static class Calculator {
        final JTextField display=new JTextField("0",22);
        private double stored;private String operator="";private boolean fresh=true;
        Calculator(){display.setEditable(false);display.setHorizontalAlignment(JTextField.RIGHT);display.setName("display");}

Calculator owns a read-only display, stored first operand, pending operator and fresh flag. fresh indicates whether the next digit should begin a new entry rather than append to the current one.

Lines 25–27Start a button action and handle Clear immediately
        void press(String key){
            try{
                if(key.equals("C")){stored=0;operator="";fresh=true;display.setText("0");return;}

press receives the key label. C resets stored value, pending operation, entry state and display, then returns so the remaining cases do not run.

Lines 28–32Build a bounded decimal entry
                if(key.matches("[0-9.]")){
                    String current=fresh?"":display.getText();if(key.equals(".")&&current.contains("."))return;
                    if(current.length()>=16)return;if(key.equals(".")&&current.isEmpty())current="0";
                    display.setText(current+key);fresh=false;return;
                }

For digits or a decimal point, start blank when fresh. Ignore a second decimal point and entries beyond 16 characters. Prefix a leading dot with zero. Update display, mark entry active and return.

Lines 33–36Evaluate equals only after both operands are present
                double value=Double.parseDouble(display.getText());
                if(key.equals("=")){
                    if(operator.isEmpty()||fresh)throw new IllegalArgumentException("Enter both operands");
                    display.setText(Double.toString(calculate(stored,value,operator)));operator="";fresh=true;

Parse the displayed value. Equals requires a pending operator and a completed second entry. Calculate using stored and value, display the result, clear the pending operator and mark the next entry fresh.

Lines 37–40Store an operator or finish a prior operation left to right
                }else{
                    if(!operator.isEmpty()&&!fresh)value=calculate(stored,value,operator);
                    stored=value;operator=key;display.setText(Double.toString(value));fresh=true;
                }

If a previous operator and second entry are ready, calculate that pending operation first. Then save the resulting value and new operator. This state machine evaluates button sequences, not a full expression parser with precedence.

Lines 41–43Recover from a calculation or input-format error
            }catch(IllegalArgumentException|ArithmeticException e){display.setText("Error");operator="";fresh=true;}
        }
    }

Catch the two runtime exception types, show Error and reset the operator/fresh state. The next numeric entry can start a new value. These braces close press and Calculator.

Lines 44–46Assemble the display and keypad containers
    public static JPanel createPanel(){
        JPanel panel=root("Simple Calculator");Calculator calculator=new Calculator();calculator.display.setFont(new Font(Font.MONOSPACED,Font.PLAIN,24));
        JPanel center=new JPanel(new BorderLayout(10,10));center.add(calculator.display,BorderLayout.NORTH);JPanel keys=new JPanel(new GridLayout(4,4,8,8));

Create the shared root, one Calculator object, a larger monospaced display and a BorderLayout center. A 4-by-4 grid holds the main keys.

Lines 47–49Connect every button to the same calculator state
        for(String key:new String[]{"7","8","9","/","4","5","6","*","1","2","3","-","0",".","=","+"}){JButton button=new JButton(key);button.addActionListener(e->calculator.press(key));keys.add(button);}center.add(keys,BorderLayout.CENTER);panel.add(center,BorderLayout.CENTER);
        JPanel extras=new JPanel();for(String key:new String[]{"%","C"}){JButton button=new JButton(key);button.addActionListener(e->calculator.press(key));extras.add(button);}panel.add(extras,BorderLayout.SOUTH);return panel;
    }

Each button listener calls calculator.press with its label. Add % and C separately below. Returning the panel preserves the listener references to calculator for later events.

Lines 50–52Create and show Swing UI on the event-dispatch thread
    public static void main(String[] args){SwingUtilities.invokeLater(()->show("Simple Calculator",createPanel()));}

}

SwingUtilities.invokeLater schedules the lambda on Swing’s event-dispatch thread. The lambda creates the panel and calls show. This main needs a graphical desktop; the panel and its event handlers are separate from console input.

FOLLOW THE STATE

Dry run

  1. Click 7, /, 2, = to display 3.5.
  2. Click C, 8, %, 3, = to display 2.0.
  3. Click C, 7, /, 0, = to display Error.
  4. Entering a new number or pressing C permits a fresh calculation.

CHECK THE BEHAVIOUR

GUI preview & checks

These panels were rendered from this exact Java source. Event handlers were checked without a desktop window; window decorations and native error-dialog appearance can differ on your computer.

Initial panel
Swing calculator using GridLayout — Initial panel
Example result
Swing calculator using GridLayout — Example result

Try these checks

  • Click 7, /, 2, = to display 3.5.
  • Click C, 8, %, 3, = to display 2.0.
  • Click C, 7, /, 0, = to display Error.
  • Entering a new number or pressing C permits a fresh calculation.

Common mistakes

  • This calculator does not implement operator precedence.
  • Reject a second decimal point in one operand.
  • Handle / and % zero divisors explicitly for doubles.

WHY THIS GROWTH RATE?

Time and space complexity

O(L) bounded parsing/display work per button; O(1) stored calculator state.

Numeric arithmetic uses native doubles, while parsing and concatenating the displayed operand depend on its L characters. Entry length is capped at 16, so the actual handler work is bounded.

The controller stores two numeric values/flags rather than a growing expression tree. Swing layout and painting are framework costs.

These bounds describe the implemented data processing. Input/output text length, scheduling, framework rendering and runtime/library internals are identified separately where they matter. Educational limits bound the actual examples.

PREPARE FOR YOUR LAB VIVA

Related viva questions & answers

6 focused questions

Try answering aloud, then expand to check your reasoning. Questions focus on this program’s classes, state and behavior.

What does GridLayout contribute to the calculator?

It arranges the main buttons in equal-sized grid cells. The controller handles calculations separately from that visual placement.

What does the fresh flag represent?

It indicates that the next digit starts a new operand instead of appending to the displayed prior value or result.

How are chained operators evaluated here?

When another operator follows an entered second operand, the pending operation is completed first. The new operator then uses that result, producing left-to-right evaluation.

Why does this double calculator explicitly reject divisor zero?

Floating-point division normally produces infinity or NaN instead of an ArithmeticException. The handler deliberately treats zero division and remainder as user errors.

Why is the display field not editable directly?

Buttons drive the controller’s state transitions. Restricting direct edits keeps operand text and the pending-operation state synchronized.

What does the C button reset?

It clears the display, stored operand, pending operator and fresh-entry state. Clearing only the text would leave hidden calculation state behind.