AP Computer Science A study materials
AP Computer Science A study resources

Static Counter vs. Instance Field in a Ticket Class

AP Computer Science A · Put your knowledge to work in context.

All case studies

Static Counter vs. Instance Field in a Ticket Class

A student builds a Ticket class that should assign sequential IDs starting at 1:

class Ticket {
    private int id;
    private static int nextId = 1;
    public Ticket() {
        id = nextId;
        nextId++;
    }
    public int getId() { return id; }
}

Creating three tickets gives IDs 1, 2, 3 - correct. A teammate "simplifies" it by making id static too:

private static int id;
public Ticket() { id = nextId; nextId++; }

Now three tickets all report ID 3 via getId(). The teammate is confused because the constructor still increments. A second issue: a test creates 1000 tickets, then a new JVM run in the same test suite expects IDs to restart at 1, but they continue from 1001 because nextId persists.

1. Explain why making `id` static causes all tickets to report `3`, and why `nextId` should stay static while `id` should not.

Read the worked answer

A static field belongs to the class, not to any object - there is exactly one id shared by all Ticket instances. Each constructor overwrites that single slot, so after three constructions id == 3, and every getId() reads the same shared value.

nextId should be static: it is class-wide state that must persist across instances to produce a sequence. id should be an instance field: each ticket has its own identity, so it must live per object.

The rule of thumb: a field is static when its value is a property of the class as a whole (a counter, a constant, a shared registry); it is an instance field when each object needs its own copy. Making id static collapses all tickets into one identity - the constructor still "works," but every object points at the same memory.

2. The test suite expects IDs to restart at 1 in a fresh run but they continue from 1001. Explain the cause and describe a reset strategy, including its risk.

Read the worked answer

static fields are initialized once when the class is loaded and live for the life of the JVM (or classloader). Within one test process, constructing 1000 tickets advances nextId to 1001; a "new run" that reuses the same loaded class does not re-run static initialization, so numbering continues.

Reset strategies:

  1. Add a package-private static void resetCounter() that sets nextId = 1, called in test setup (@BeforeEach).
  2. Use reflection to reset the field - works but is brittle and hides the design problem.
  3. Avoid static state: pass an IdGenerator into the constructor, so each test gets a fresh generator.

Risk: a public reset method lets production code accidentally rewind IDs, causing duplicate tickets. The safer design is dependency injection (option 3), which removes global mutable state entirely. If a reset method is used, keep it package-private and document that it is test-only. The judgment call is testability versus encapsulation - global static counters trade clean tests for hidden coupling.