Test 3 Prep¶
Topics & Study Guide¶
The following topics may appear on Test 3. Use the linked guides to review.
| Topic | Guide | Key Sections |
|---|---|---|
| Layout Managers (FlowLayout, BorderLayout, GridLayout, composite layouts) | Swing Layout Managers | All sections — defaults, region rules, nesting |
| JFrame setup (extend JPanel, pack, EXIT_ON_CLOSE, invokeLater) | Swing API Basics | JFrame boilerplate, EDT |
| 2D coordinate system (origin top-left, +Y down) | Custom Painting with Java 2D | Coordinate system, drawing off-screen |
| Shapes & bounding boxes (Rectangle2D, Ellipse2D, bounding box top-left corner) | Custom Painting with Java 2D | Shapes, bounding boxes, circle as ellipse |
| paintComponent (override, super call, cast to Graphics2D, never call directly) | Custom Painting with Java 2D | paintComponent pattern, anti-aliasing |
| Drawing operations (setPaint, fill, draw, setStroke, painter's algorithm) | Custom Painting with Java 2D | Fill vs draw, stroke, layering |
| Grid-to-pixel mapping | Custom Painting with Java 2D | Grid mapping section |
| Animation loop (render → change → rerender) | Animation with javax.swing.Timer | Animation concepts |
| Shapes as instance fields (not local variables in paintComponent) | Animation with javax.swing.Timer | Instance fields, shape mutability |
| javax.swing.Timer (not java.util.Timer; fires on EDT; start/stop/setDelay) | Animation with javax.swing.Timer | Timer API, game loop pattern, speed control |
| Pause/unpause (stop/start), Thread.sleep on EDT is bad | Animation with javax.swing.Timer | Pause pattern, EDT safety |
| Observer pattern (loose coupling, subject/observer, addObserver/removeObserver) | The Observer Pattern | Problem, solution, loose coupling |
| PropertyChangeListener & PropertyChangeSupport | The Observer Pattern | PCL interface, PCS composition, firePropertyChange |
| MVC pattern (Model, View, Controller roles and data flow) | Model-View-Controller (MVC) | Roles, data flow, pure views, container |
| Sealed interfaces & pattern matching (exhaustive switch, no default) | Model-View-Controller (MVC) | Sealed events, switch expressions |
| KeyListener / KeyAdapter (keyPressed vs keyTyped vs keyReleased) | Handling Key Events | Method distinctions, focus requirements |
| Focus requirements (setFocusable, requestFocusInWindow, buttons steal focus) | Handling Key Events | Focus section |
| Map-based key bindings (Map<Integer, Runnable>, getOrDefault) | The Strategy Pattern | Map-based dispatch, method references |
| Swing menus (setJMenuBar, JMenu extends JMenuItem, exit via WindowEvent) | Building Menus with JMenuBar | Menu bar setup, exit pattern, option dialogs |
| Design patterns overview (Gang of Four, three categories, Singleton) | Introduction to Design Patterns | History, categories, Singleton |
| Strategy pattern (encapsulate algorithms, Map<Key, Runnable>) | The Strategy Pattern | Pattern definition, modern Java implementation |
| Lambda expressions & method references (functional interfaces, SAM) | Introduction to Lambda Expressions | Functional interfaces, method references |
Practice Problems¶
Want More Practice?
These are sample problems — one of each type you'll see on the test. Want more? Form a study group! Each person writes their own version of one of these problem types, then exchange and challenge each other. Writing problems is one of the best ways to deepen your understanding.
Test 3 has 6 types of problems. Below is one example of each with a solution you can reveal when ready.
Section 1: Fill in the Blank¶
Instructions: Fill in the blank with the correct term.
The default layout manager for a JPanel is ______________, which arranges components in horizontal rows from left to right and wraps to the next row when space runs out.
Answer
FlowLayout
Section 2: Visual Bug Detective¶
Instructions: For the
paintComponentmethod below, a table describes the intended visual output. Predict whether each shape renders as described. Write YES if the shape appears exactly as described, or NO if the code produces something different. The described output represents the correct specification — if the answer is NO, the code has a bug.
The panel is 400×400. Assume anti-aliasing is enabled.
public class NextPiecePanel extends JPanel {
// T-piece stored as (col, row) pairs
// Model convention: row 0 is the BOTTOM of the piece
private final int[][] myPiece = { {0, 0}, {1, 0}, {2, 0}, {1, 1} };
private static final int BLOCK = 30;
private static final int MAX_ROW = 1; // highest row in this piece
@Override
protected void paintComponent(final Graphics theGraphics) {
super.paintComponent(theGraphics);
final Graphics2D g2d = (Graphics2D) theGraphics;
g2d.setPaint(Color.CYAN);
for (final int[] block : myPiece) {
final int x = block[0] * BLOCK;
final int y = (MAX_ROW - block[1]) * BLOCK;
g2d.fill(new Rectangle2D.Double(x, y, BLOCK, BLOCK));
}
}
}
The T-piece should render with the stem pointing UP:
| # | Intended Shape | Position | Size | Style | Your Answer |
|---|---|---|---|---|---|
| 1 | Three blocks in a horizontal row | y = 30 (second row from top) | 30×30 each | Filled cyan | ____ |
| 2 | One stem block centered above the row | (30, 0) — top row | 30×30 | Filled cyan | ____ |
| 3 | Overall T-shape points upward (stem on top) | Top-left area of panel | 90×60 total | — | ____ |
Answer
No bug. The code correctly flips the Y-axis using (MAX_ROW - block[1]) * BLOCK.
- Row 0 blocks (base):
y = (1 - 0) * 30 = 30→ second row from top - Row 1 block (stem):
y = (1 - 1) * 30 = 0→ top row
| # | Answer | Explanation |
|---|---|---|
| 1 | YES | Base blocks at row 0: (1 - 0) * 30 = 30. They appear at y=30, the second row from top. Three blocks side by side at x=0, 30, 60. |
| 2 | YES | Stem block at row 1: (1 - 1) * 30 = 0. Appears at (30, 0) — centered above the base row, top of the panel. |
| 3 | YES | Stem at top (y=0), base at bottom (y=30). T-shape points upward. Total footprint: 90 wide (3 blocks) × 60 tall (2 rows). |
Section 3: Bug Detective — Test Prediction¶
Instructions: For the method below, predict whether each JUnit 5 test will PASS or FAIL. The method may be correct or may contain bugs. All tests shown are correct specifications — if a test fails, it's because the code has a bug, not the test.
The
...in the parameterfinal Point... thePointsis Java's varargs syntax — it lets the method accept zero or morePointarguments, which are received as an array. If you're unfamiliar, see the Oracle varargs tutorial.
/**
* Converts grid positions to pixel-sized rectangles.
*
* @param theBlockSize the width and height of each block in pixels
* @param thePoints the grid positions to convert
* @return a list of Rectangle2D, one per point
*/
public static List<Rectangle2D> buildBlocks(final int theBlockSize,
final Point... thePoints) {
final List<Rectangle2D> result = new ArrayList<>();
for (final Point p : thePoints) {
result.add(new Rectangle2D.Double(
p.y() * theBlockSize,
p.x() * theBlockSize,
theBlockSize, theBlockSize));
}
return result;
}
Test 1:
PASS / FAIL: ____Test 2:
PASS / FAIL: ____Test 3:
PASS / FAIL: ____Test 4:
PASS / FAIL: ____Test 5:
final var blocks = buildBlocks(30, new Point(0, 0), new Point(1, 0));
assertEquals(2, blocks.size());
Answer
Bug: The method uses p.y() for the x-pixel and p.x() for the y-pixel — swapped.
| # | Answer | Explanation |
|---|---|---|
| 1 | PASS | Point(0,0): x = 0*30 = 0. Swap doesn't matter when both coordinates are 0. |
| 2 | FAIL | Point(2,5): actual x = p.y() * 30 = 5*30 = 150, not 60. |
| 3 | FAIL | Point(2,5): actual y = p.x() * 30 = 2*30 = 60, not 150. The other half of the swap. |
| 4 | PASS | Width is theBlockSize regardless of the x/y swap. Bug only affects position. |
| 5 | PASS | Two points in, two rectangles out. The swap doesn't affect list size. |
Section 4: Listener Trace — Key Events¶
Instructions: You are given a listener implementation and a sequence of user actions. In Part A, identify which listener method fires for each action (or "none"). In Part B, write the complete console output.
public class GameKeyListener extends KeyAdapter {
private static final Runnable DO_NOTHING = () -> { };
private final Map<Integer, Runnable> myKeyMap = new HashMap<>();
public GameKeyListener() {
myKeyMap.put(KeyEvent.VK_LEFT, () -> System.out.println("left"));
myKeyMap.put(KeyEvent.VK_RIGHT, () -> System.out.println("right"));
myKeyMap.put(KeyEvent.VK_SPACE, () -> System.out.println("drop"));
}
@Override
public void keyPressed(final KeyEvent theEvent) {
myKeyMap.getOrDefault(theEvent.getKeyCode(), DO_NOTHING).run();
}
}
Registration: panel.addKeyListener(new GameKeyListener());
Panel has setFocusable(true) and requestFocusInWindow().
Part A:
| User Action | Method(s) |
|---|---|
| Presses the left arrow key | ____ |
| Presses the up arrow key | ____ |
| Presses the spacebar | ____ |
| Presses the letter 'W' | ____ |
Part B: What is the complete console output?
Answer
Part A:
| User Action | Method(s) | Explanation |
|---|---|---|
| Presses the left arrow key | keyPressed |
Only method overridden; arrow key fires keyPressed |
| Presses the up arrow key | keyPressed |
keyPressed fires, but VK_UP is not in the map — default runs |
| Presses the spacebar | keyPressed |
keyPressed fires; VK_SPACE is in the map |
| Presses the letter 'W' | keyPressed |
keyPressed fires; VK_W is not in the map — default runs |
Part B — Console output:
Only 2 of the 4 keys are mapped. getOrDefault returns DO_NOTHING for unmapped keys, which runs silently.
Section 5: Listener Trace — Mouse Events¶
Instructions: You are given a listener implementation and a sequence of user actions. In Part A, identify which listener method fires for each action (or "none"). In Part B, write the complete console output.
public class MyMouseListener extends MouseAdapter {
private int myClickCount;
@Override
public void mousePressed(final MouseEvent theEvent) {
System.out.println("down:" + theEvent.getX() + "," + theEvent.getY());
}
@Override
public void mouseReleased(final MouseEvent theEvent) {
System.out.println("up:" + theEvent.getX() + "," + theEvent.getY());
}
@Override
public void mouseClicked(final MouseEvent theEvent) {
myClickCount++;
System.out.println("click#" + myClickCount);
}
}
Registration:
Part A:
| User Action | Method(s) |
|---|---|
| Presses mouse at (100, 50) | ____ |
| Releases mouse at (100, 50) | ____ |
| Presses mouse at (200, 100) | ____ |
| Drags mouse to (300, 150) | ____ |
| Releases mouse at (300, 150) | ____ |
Part B: What is the complete console output?
Answer
Part A:
| User Action | Method(s) | Explanation |
|---|---|---|
| Presses mouse at (100, 50) | mousePressed |
Button goes down |
| Releases mouse at (100, 50) | mouseReleased, mouseClicked |
Released at same position as pressed → click fires too |
| Presses mouse at (200, 100) | mousePressed |
Button goes down |
| Drags mouse to (300, 150) | none | mouseDragged requires addMouseMotionListener — not registered |
| Releases mouse at (300, 150) | mouseReleased |
Button comes up, but at different position than pressed → NO mouseClicked |
Part B — Console output:
Two key distinctions: (1) mouseClicked only fires when release is at same position as press, (2) mouseDragged requires addMouseMotionListener registration.
Section 6: Timer State Trace — Draw on Grid¶
Instructions: Trace the timer ticks to determine field values at the given time. Then draw the shapes that
paintComponentwould render on the provided grid. Shade each cell and label it with the color. The grid origin (0, 0) is the top-left corner. X increases to the right, Y increases downward.
public class DiagonalPanel extends JPanel {
private int myCol;
private int myRow;
private final Timer myTimer;
private static final int BLOCK = 40; // each grid cell is 40×40 pixels
public DiagonalPanel() {
myTimer = new Timer(100, e -> step());
myTimer.start();
}
private void step() {
myCol++;
myRow++;
repaint();
}
@Override
protected void paintComponent(final Graphics theGraphics) {
super.paintComponent(theGraphics);
final Graphics2D g2d = (Graphics2D) theGraphics;
g2d.setPaint(Color.RED);
g2d.fill(new Rectangle2D.Double(
myCol * BLOCK, myRow * BLOCK, BLOCK, BLOCK));
}
}
Part A: Fill in the field values.
| Ticks | myCol | myRow |
|---|---|---|
| 0 (start) | ____ | ____ |
| 1 | ____ | ____ |
| 2 | ____ | ____ |
| 3 | ____ | ____ |
Part B: After 300ms (3 ticks), draw the shape on the grid. Each cell is one block. Shade the cell(s) and label with the color.
0 1 2 3 4 5
+---+---+---+---+---+---+
0 | | | | | | |
+---+---+---+---+---+---+
1 | | | | | | |
+---+---+---+---+---+---+
2 | | | | | | |
+---+---+---+---+---+---+
3 | | | | | | |
+---+---+---+---+---+---+
4 | | | | | | |
+---+---+---+---+---+---+
5 | | | | | | |
+---+---+---+---+---+---+
Answer — Part A
| Ticks | myCol | myRow |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
Answer — Part B
One red block at grid position (col=3, row=3):
0 1 2 3 4 5
+---+---+---+---+---+---+
0 | | | | | | |
+---+---+---+---+---+---+
1 | | | | | | |
+---+---+---+---+---+---+
2 | | | | | | |
+---+---+---+---+---+---+
3 | | | | R | | |
+---+---+---+---+---+---+
4 | | | | | | |
+---+---+---+---+---+---+
5 | | | | | | |
+---+---+---+---+---+---+
R = Red filled. Pixel position: (120, 120), size 40×40. Only the current position is drawn — repaint() clears and redraws, it doesn't leave a trail.