Session 1 · Programming Technology
Recap: classes, objects and a first window
What the semester builds towards, how a Java project is laid out, and the object-oriented basics underneath everything: private state, references and parameter passing, a class around a list, and a first Swing calculator with a listener that validates its input.
This first session is a warm-up. We look at what the course is about, how a Java project is laid out in the IDE, and we refresh the object-oriented basics every later session builds on: a class with private state, objects and references, and what really happens when you pass an object to a method. At the end we build a small window – a calculator – because from the middle of the semester everything you write will have a GUI.
The one idea to take away: a variable of a class type never holds an object, it holds a reference to one. Most surprises in Java – a list that changed "by itself", a method that changed the caller's object but not the caller's int – are this one fact.
What this course is about
Programming Technology is about writing whole programs in Java, not single algorithms. The first third of the semester is a recap of object-oriented programming – classes, UML diagrams, collections, files and exceptions – and ends with the first assignment. The other two thirds are GUI programs: first board games built from Swing buttons, then animated games that draw their own graphics and keep a leaderboard in a database (Fig. 1).
The order matters. A GUI program is an object-oriented program with a window on top – if the classes underneath are messy, the window only makes the mess visible. That is why we spend the first weeks on getting the model right, before a single button appears.
Projects in the IDE
The examples are NetBeans projects; any Java IDE (NetBeans, IntelliJ IDEA, VS Code with the Java extensions) can open them. You will meet two layouts (Fig. 2). The first examples are NetBeans Ant projects – the build is described in build.xml and the IDE keeps its settings in nbproject/. Later examples are Maven projects – the build, the Java language level and the libraries (for example JUnit) are listed in pom.xml, and the project opens the same way in every IDE and builds from the command line.
Classes and objects
A class describes what its objects know (fields) and what they can do (methods). An object is one concrete instance, created with new, with its own copy of every field. The People example has one class, Person, and creates two people from it (Fig. 3).
Person.java – private state, public methods
public class Person {
private String name; // state is private …
private int age;
public Person(String name, int age) {
this.name = name; // this.name = the field, name = the parameter
this.age = age;
}
public String getName() { // … and reachable only through methods
return name;
}
}The fields are private: nothing outside the class can read or overwrite them directly. This is encapsulation, and it is what lets a class guarantee its own rules. If age must never be negative, the only place that can break this rule is the class itself – so it is the only place you have to check.
- field
- A variable that belongs to an object (name, age). Each object has its own.
- constructor
- Runs once, at new. Its job is to put the object into a valid starting state.
- getter / setter
- Methods that read / change one field. Write only the ones you actually need.
this- The object the method is running on. this.name is the field, plain name is the nearest variable.
- static
- Belongs to the class, not to an object – main and helper methods such as changeName.
References and parameter passing
Java passes every argument by value: the method gets a copy. What is copied depends on the type. For an int, the copy is the number. For a Person, the copy is the reference – so the method's parameter points at the very same object as the caller's variable (Fig. 4).
People.java – the three helper methods
public static void changeName(Person p) {
p.setName("Frank"); // changes the object p points to
}
public static void changeInt(int param) {
param += 6; // changes the local copy only
}
// in main:
changeName(p2); // p2's name is now "Frank"
changeInt(i); // i is still 5
i = increaseInt(i); // the way to "change" an int: return the new valueThe same rule explains a lot of later surprises. Put a Person into two lists and rename it through one list – the other list "changes" too, because there was only ever one object. Assign p = new Person(…) inside a method – the caller's variable does not move, because only the copy was reassigned.
A class around a list: the Queue
The Queue example wraps an ArrayList<String> and exposes only queue operations: push at the back, top and pop at the front, isEmpty, empty. The list is private, so nobody can insert into the middle of the queue – the class decides what a queue may do (Fig. 5).
Queue.java – the core
public class Queue {
private final ArrayList<String> data = new ArrayList<>();
public void push(String e) {
data.add(e); // join at the back
}
public String pop() {
if (data.isEmpty()) {
return null; // nothing to take
}
return data.remove(0); // leave at the front
}
}A text menu drives it: a loop prints the options, reads a number and dispatches with a switch. The reading is done by Console, a small helper class that ships with the example (readInt, readLine, … wrapped around System.in). The loop runs until the user picks "exit" – a pattern you will use for any console program.
TestQueue.java – the menu loop (condensed)
boolean exit = false;
while (!exit) {
printMenu();
int choice = Console.readInt("Pick one: ");
switch (choice) {
case 1 -> queue.push(Console.readLine("String to store: "));
case 3 -> System.out.println("Removed: " + queue.pop());
case 7 -> exit = true;
default -> System.out.println("No such menu item.");
}
}A first window: the calculator
The Calc example is a calculator with two operand fields, a result field and five buttons (+, −, *, /, ^), plus a menu with an Exit item. A Swing window is a tree: a JFrame contains panels, panels contain components, and each panel has a layout manager that decides where its children go (Fig. 6).
CalcGUI.java – building the tree
frame = new JFrame("Calculator");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
numPanel = new JPanel();
numPanel.setLayout(new BoxLayout(numPanel, BoxLayout.Y_AXIS)); // a column
numPanel.add(operand1);
numPanel.add(operand2);
numPanel.add(result);
buttonPanel = new JPanel(new GridLayout(5, 1)); // 5 rows, 1 column
frame.getContentPane().add(BorderLayout.WEST, numPanel);
frame.getContentPane().add(BorderLayout.EAST, buttonPanel);
frame.pack(); // size the window to its contents
frame.setVisible(true); // only now does it appearA button does nothing until something listens to it. Each button gets its own CalcActionListener object that knows which operation it stands for. When the button is clicked, Swing calls the listener's actionPerformed (Fig. 7).
CalcGUI.java – the listener, an inner class
class CalcActionListener implements ActionListener { // inner class of CalcGUI
private final String operation;
CalcActionListener(String operation) {
this.operation = operation;
}
@Override
public void actionPerformed(ActionEvent ae) {
try {
double op1 = Double.valueOf(operand1.getText()); // fields of the outer CalcGUI
double op2 = Double.valueOf(operand2.getText());
result.setText(String.valueOf(compute(op1, op2)));
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(frame, "Invalid number!", "Error",
JOptionPane.ERROR_MESSAGE);
}
}
}CalcActionListener is an inner class: it is declared inside CalcGUI, and its objects can use the fields of the CalcGUI object that created them – that is how it reaches operand1 and result. For a one-line reaction, such as Exit, a lambda is shorter than a named class.
Wiring the listeners
JButton addButton = new JButton("+");
addButton.addActionListener(new CalcActionListener("add")); // one listener object per button
buttonPanel.add(addButton);
// a one-off listener does not need a named class – a lambda is enough
exitMenuItem.addActionListener(e -> System.exit(0));Practice
Build these three steps in one new project. They are small on purpose – the goal is to write a clean class and the two standard loop patterns without looking anything up.
Task 1
A Student class
A student has a name, a nationality and a grade average. Create a new project and write the Student class with a constructor, getters and setters.
Shape of the class
Three private fields, one constructor that sets all three, then the accessors.
public class Student {
private String name;
private String nationality;
private double gradeAverage;
public Student(String name, String nationality, double gradeAverage) { … }
public double getGradeAverage() { return gradeAverage; }
// … the other getters and setters follow the same pattern
}Task 2
Best and worst average
Keep several students in an ArrayList<Student>, then print the student with the best and the one with the worst grade average. Use the maximum-selection pattern (maximumkiválasztás), not a sort.
Maximum selection
Start from the first element and walk the list once, keeping the best so far. The minimum is the same loop with the comparison flipped – both fit in the same loop.
Student best = students.get(0); // assume the first one is the best …
for (Student s : students) {
if (s.getGradeAverage() > best.getGradeAverage()) {
best = s; // … and correct the assumption
}
}What if the list is empty?
students.get(0) throws on an empty list. Check isEmpty() first and print a message instead.
Task 3
Who gets a scholarship?
A student gets a scholarship if their average is at least 4.0. List those students.
Filtering
One loop, one condition. Name the limit as a constant so the rule is readable and changes in one place.
for (Student s : students) {
if (s.getGradeAverage() >= SCHOLARSHIP_LIMIT) { // a named constant, not a magic 4.0
System.out.println(s.getName());
}
}Task 4
Extend the calculator
Add a sixth operation to Calc – the remainder (%) – and make division by zero show an error dialog instead of printing Infinity.
Where the changes go
The grid needs one more row, one more button with its own listener, and one more case in the switch. For division by zero, check op2 == 0 before dividing and show the same kind of dialog the NumberFormatException branch shows.
Summary
- The semester goes from an OOP recap (assignment 1) through Swing board games (assignment 2) to animated games with a database (assignment 3).
- A class keeps its state private and offers methods; the constructor makes a valid object; this.field disambiguates a shadowed field.
- Variables of class type hold references. Java passes a copy of the reference – the method can change the object, never the caller’s variable.
- Wrap a collection in a class to expose only the operations that make sense (Queue: push, top, pop).
- A Swing window is a tree of components with a layout per panel; buttons do work through listeners, and invalid input becomes a dialog, not a crash.
| Example | What it shows |
|---|---|
| People | objects and references, passing objects vs ints |
| Queue | a class wrapping an ArrayList, a text menu loop |
| Calc | JFrame, panels and layouts, an inner-class listener, input validation |
| Grades | a Maven project; maximum selection and filtering over a list |