Lab #5: Packages, Serialization, and a Swing GUI
The final lab on the circuit simulator: organize it into real packages, make it persistable, and put a small graphical front end on top of it.
Objectives
- Split a project into packages, and feel the compiler’s package-visibility rules push back when access modifiers are wrong.
- Deliberately break and repair visibility to see exactly which modifier controls which kind of access.
- Persist and reload an object graph with serialization.
- Wire an existing domain model into a provided (partial) Swing GUI.
1. Packages and encapsulation
Difficulty: Rx- Move every class except your test class into a
circuitspackage:- create a
circuitsdirectory and move the relevant.javafiles into it, - add
package circuits;as the very first line of each of those files (before anyimport), - package directories must be reachable via
CLASSPATHor the-cpflag tojavac/java— the current directory (.) is included by default, so compiling from above thecircuitsdirectory is usually enough:javac ./circuits/*.java.
- create a
- Move your test class into its own, separate
testpackage:- create a
testdirectory (a sibling ofcircuits) and move yourCircuitTestthere, withpackage test;at the top, - import the
circuitspackage’s classes:import circuits.*;, - compile with
javac ./test/CircuitTest.java.
- create a
- At this point compilation will very likely fail on visibility grounds. Typical errors look like:
or, similarly, a method reported as not public when the caller sits outside the package. Fix these by making whatevertest/CircuitTest.java:23: circuits.Circuit is not public in circuits; cannot be accessed from outside packagecircuitsneeds to export — fortest.CircuitTestto use it — explicitlypublic, and leaving everything else unmarked orprotected. As a rule of thumb:- classes:
publicif used from outside the package, otherwise unmarked (“package-private”), - methods:
publicif part of the exported protocol, otherwise unmarked orprotected(“subclass-limited”), - instance fields: unmarked,
protected, orprivate— keeping in mind thatprivatefields aren’t visible even to subclasses in the same package.
- classes:
- Run it:
java -cp . test.CircuitTest(adjust the classpath if needed). - Now deliberately experiment with visibility, to see the rules from both directions:
- Remove
publicfrom theCircuitclass, recompilecircuits, then recompileCircuitTest— you should hit the “not public” class error from step 3. Restore it, then do the same for one exported method (e.g.getSwitches()), and observe the analogous method-level error. TwoInputGateis an internal factoring class insidecircuits, never referenced fromtest. Make it non-public and recompile both packages — does anything break? Do the same check forProbeTable, an internal helper for the probe/unprobe machinery — confirm it, too, can safely stay package-private.- Make
TwoInputGate’sin1/in2fieldsprivateand recompilecircuitsalone. What happens to its subclasses,AndandOr, which read those fields directly? Circuit’scomponentslist should be encapsulated (anything butpublic— at leastprotected, sinceCircuititself is responsible for keeping it sorted and consistent). Try, fromCircuitTest, reaching into it directly and corrupting it — e.g.circuit.components.add(new And())— and see what the compiler says. Then temporarily make the fieldpublic, repeat the same line, and run your test again: notice thatpartList()— and everything that depends on the list staying sorted — is now silently wrong.
- Remove
- Before moving on, double-check every modifier is back to its correct, final value:
Circuitpublic,getSwitches()public,TwoInputGateandProbeTablenon-public,TwoInputGate’sin1/in2non-private,componentsprotected (not public). Sections 2 and 3 below build directly on this codebase and assume it compiles cleanly with correct encapsulation — carrying over any of the deliberately-broken states from step 5 will cause confusing failures later that have nothing to do with serialization or the GUI.
2. Saving circuits with serialization
Difficulty: RxSerialization will let you save a circuit’s configuration to a file and reload it later. Add a pair of methods to Circuit:
public void save(String fileName) throws IOException— writes the circuit’s name (aString, already serializable) and itscomponentslist tofileName. This requires theComponentclass to implementSerializable— every subclass inherits that automatically, so you only need theimplements Serializableclause onComponentitself, not repeated onSwitch,Gate,And, and so on.public void load(String fileName) throws IOException, ClassNotFoundException— the inverse: reads the name and thecomponentslist back from the file.
Both methods should simply declare their checked exceptions with throws and let them propagate — no need to catch anything inside Circuit itself; the calling code (below) will handle it.
Test it
- From
CircuitTest, save your example circuit’s state to a file (ask the user for a filename, e.g.circuit.bin). - Write a second, separate test class,
SerializationTest, whosemaintakes a saved filename as an argument, builds a circuit from it, and callstraceStates()on it to confirm the reloaded configuration matches:Circuit circuit = new Circuit(); circuit.load(fileName); circuit.traceStates();
3. A graphical front end
Difficulty: HardThe last piece: a small Swing application, Tester, that loads a previously-saved circuit and lets you interact with it graphically. From top to bottom, the window shows:
- an image of the circuit (a static picture, loaded from a
.pngfile), - a non-editable text area with the circuit’s description,
- a dropdown listing the circuit’s switches,
- an on/off toggle for whichever switch is currently selected,
- a text area showing switch states,
- a text area showing valve (output) states.
The whole thing lives in a single JFrame, titled with the loaded circuit’s name, with its components stacked vertically (BoxLayout along the Y axis). The application takes a base filename as its argument (e.g. circuit, with no extension) and expects to find both circuit.bin (the serialized circuit) and circuit.png (its picture) alongside it.
You’ll be given a starter GUI to complete rather than writing Swing wiring from scratch:
- Download the provided GUI starter package,
ihm.jar. It contains a circuit image and a partially-implementedTesterclass in aguipackage. Unpack it withjar xvf ihm.jar. - Read through
Testerand identify how it uses yourcircuitspackage. In particular, notice that displaying the circuit’s description requires two methods onCircuityou haven’t written yet:String getName()(returns the circuit’s name) and aString-returning variant ofdescription()(rather than one that prints directly) — add both. - Compile
gui.Testerand run it against your previously-saved circuit:java gui.Tester circuit. At this point you should see the window described above, but incomplete: the on/off toggle doesn’t do anything yet, and the valve-state text area is missing entirely. - Follow the guide comments in the provided code to:
- add the missing text area for valve/output state,
- make flipping the selected switch actually update both state text areas.
- Recompile and try it again — you should now have a fully interactive tester for your circuit.