mattone
dopo mattoneTHE BOOK SERIES
IT/EN
← Java guide

Java 25 · 32/39

Phase 3 activities

Java 25 · Complete guide · Draft under review

This guide retains the book’s draft status. Editorial review and comprehension checks with independent readers remain to be completed.

Search the whole guide →

This collection accompanies the phase 3 drafts. Activities still absent will be introduced together with their respective chapters and verified sources. The reference version is Java 25 without preview.

C07-A01 – Class, object and reference

Assignment. Without running Book.java, indicate how many class declarations, how many Book variables and how many Book objects are created in its main. Explain why first == second is true.

Criterion. The answer distinguishes the class declaration from the two variables and the single instance created with new. Comparison is explained as reference identity, not title comparison.

Hints. Find lines containing new Book. Then read the assignment to second from right to left: which value does it receive? Figure 16 offers the logical model.

Commented solution. The file declares one Book class. In main there are two variables of type Book, first and second. Only the first line contains new Book, so one object is created. The second assigns second the same reference held by first; this is why comparison with == produces true.

C07-G01 – Adding a method to the book

Assignment. In a copy of Book.java, add public void recordLoans(int count) that calls recordLoan() as many times as the parameter indicates. In main replace the single call with first.recordLoans(2). Predict output before compiling.

Criterion. The method uses the parameter as a loop bound; the printed title stays the same and the loan count becomes 2. first == second remains true, because a second book was not created.

Hints. Start with for (int i = 0; i < count; i++). The new method can reuse the existing one without directly modifying the field in main.

Commented solution. recordLoans executes for (int i = 0; i < count; i++) recordLoan();. With count equal to two, the same object increments its counter twice; second.description() prints Java, Brick by Brick - loans: 2. Reference comparison stays true. In chapter 8 we will discuss rejecting a negative parameter if the operation becomes part of a public contract.

C07-P01 – Two books with the same title

Assignment. Modify a copy of Book.java so second receives the result of a new expression new Book("Java, Brick by Brick"). Before compiling, predict the two printed lines. Then compile with javac --release 25 -Xlint:all -d build Book.java from the copy's folder and launch with java -cp build Book. Explain why the same title string is not enough to make references compared with == equal.

Criterion. The program prints Java, Brick by Brick - loans: 0 and false: the loan recorded on first does not change the other object's counter. The explanation distinguishes identity, state and title content. Compilation finishes without warnings.

Hints. Count new expressions. Draw two boxes, one for each object, and write the initial counter in both. The call first.recordLoan() has a precise receiver: mark the box it acts on.

Commented solution. After the modification, first and second point to two different instances. Both have the same title, but only the first counter becomes one. second.description() reads the second counter and prints zero; first == second is false because it compares identity. Chapter C10 will show how to define content equality with equals, but that must not be used to answer the identity question.

C07-D01 – An absent reference

Assignment. In a copy of main, insert Book absent = null; followed by System.out.println(absent.description());. Predict whether the compiler or execution reports the error, then test. Restore the original copy at the end.

Criterion. Compilation succeeds; the call on reference null causes NullPointerException at runtime. The reader can identify the line attempting the call and explain that null identifies no Book object.

Hints. absent's type is valid and assigning null is allowed. Ask which object should execute description(). Chapter C12 will show how to read a stack trace without hiding the problem.

Commented solution. The compiler can verify that Book has description, but cannot guarantee a reference variable is not null in every execution. When the JVM reaches the call, the receiver instance is missing and it throws NullPointerException. An explicit check or a contract excluding null can prevent the error; the choice depends on data meaning in the program.

C08-A01 – Identifying the invariant

Assignment. Read GradeRegister.java. Write the three conditions that must be true after constructing a valid register. Indicate the line preventing a caller from changing grades through the constructor's input array and the line preventing changes through the result of grades().

Criterion. The student's name is neither absent nor empty, at least one grade exists and each grade is between zero and thirty. Both copying directions are recognized separately.

Hints. Find statements throwing IllegalArgumentException. Then follow the array from parameter to field and from field to returned value. Figure 17 represents these steps.

Commented solution. The constructor rejects a null or whitespace-only student, a null or empty array and any out-of-range grade. this.grades = grades.clone() protects entry; return grades.clone() protects exit. private and final fields help, but do not replace the two copies.

C08-P01 – Demonstrating defensive copying

Assignment. Compile and run GradeRegister.java from the source folder with javac --release 25 -Xlint:all -d build GradeRegister.java and java -cp build GradeRegister. In a copy, remove only the constructor copy and predict the average; restore it, remove only the copy in grades() and predict the average again. Test both variants separately. Finally pass grade 31 to the single-grade constructor and describe when and why the program stops.

Criterion. The original program prints Ada: 27.0; without the entry copy it prints Ada: 19.0; without the exit copy it prints Ada: 18.0. Grade 31 is rejected before a valid register is created. Variants are isolated and original source restored.

Hints. Calculate three sums: 24+27+30, 0+27+30, 24+0+30. Ask which assignment in main acts on the shared array in each variant. If two changes are present together, you cannot confidently attribute the difference to one missing copy.

Commented solution. Copies make the register array independent of arrays the caller can modify. Without the first copy, originals[0] = 0 changes internal data and average becomes 57/3, or 19.0. Without the second, received[1] = 0 changes internal data and average becomes 54/3, or 18.0. The alternative constructor checks 31 and throws IllegalArgumentException before delegation; chapter C12 will show how to read and handle the exception correctly.

C08-R01 – Removing duplicated validation

Assignment. In a copy of GradeRegister.java, extract the 0–30 range check into a method private static void validateGrade(int grade). Use it both in the array constructor's loop and in the single-grade constructor before this(...). Keep output Ada: 27.0 and rejection of grade 31 unchanged.

Criterion. The program compiles on Java 25 without preview; both constructors apply the same rule. Revision does not remove defensive copies or change valid-data averaging.

Hints. The support method must not read instance fields; it can therefore be static and called in the flexible constructor's prologue.

Commented solution. validateGrade throws IllegalArgumentException("grade out of range") when grade < 0 || grade > 30. The loop calls validateGrade(grade); in the single-grade constructor the call precedes this(student, new int[] {grade}). The array is still checked again in the delegated constructor: the first check rejects the argument before delegation, the second preserves the array constructor's contract.

C09-A01 – Reading two relationships

Assignment. In the vehicle program, indicate which classes have an “is a” relationship with Vehicle and which object Car keeps as a part. Explain why Engine does not extend Vehicle.

Criterion. The answer identifies Car and Bicycle as subclasses and Engine as the car component; it connects the choice to domain meaning.

Hint. Read extends, then the engine field's type. Compare the two arrows in figure 19.

Commented solution. Car and Bicycle specialize Vehicle. Car keeps an Engine because an engine is one of its parts; it is not a vehicle type.

C09-P01 – Dispatch and closed cases

Assignment. Predict the three lines printed by DemoVehicles.java. Then replace car construction with new Bicycle("B"), run again and describe why the instanceof Car branch prints no line. In a separate copy remove the Bicycle case from the switch and compile.

Criterion. First execution prints Car A with electric engine, electric, car. The bicycle variant prints its description and bicycle. The missing-case variant does not compile because it is not exhaustive.

Hint. vehicle's declared type remains Vehicle; follow the created object's class. Read the instanceof check and switch separately.

Commented solution. The overridden method is chosen based on the concrete object. With Bicycle, the Car pattern fails and the branch is skipped; switch chooses Bicycle. Removing that case leaves a subtype permitted by the sealed hierarchy uncovered, so the compiler rejects the expression.

C09-D01 – Constructing base and child

Assignment. Predict the two lines of the constructor-order program. Then change 7 to 12 and verify which lines change.

Criterion. Order remains base, then child: 12: the base part is constructed before the child completes.

Hint. Start from new OrderChild(12) and follow super() before assignment to the value field.

Commented solution. The base constructor prints base; the child's assigns the field and prints child: 12. The value changes only the second line.

C09-R01 – Protecting a valid hierarchy

Assignment. Revise a copy of DemoVehicles.java so Vehicle rejects a null or empty name and Car rejects a null Engine. Preserve the valid execution's three lines and separately verify both new error cases. Explain which constructor owns each check.

Criterion. new Bicycle(" ") and new Car("A", null) do not create valid objects; execution with Car("A", new Engine("electric")) stays unchanged. The subclass does not try to directly assign the base's private name field.

Hints. The base class is responsible for its own name. Engine checking is specific to Car. Remember super(name) precedes assignment of the subclass field.

Commented solution. The Vehicle constructor can throw IllegalArgumentException when name == null || name.isBlank(), then assign the field. In Car's constructor, after super(name), check engine == null before assigning it. The positive path uses the same data and prints the same three lines; new checks make the hierarchy contract explicit.

C10-A01 – Identity and value

Assignment. Before running DemoValue.java, predict the first four lines. Explain why the third line alone is not enough to say two labels are equal.

Criterion. The lines are false, true, true, Label[Java]. The answer distinguishes identity, domain equality and hash, and knows different objects can have equal hashes.

Hint. Count new Label expressions; then read which fields calculate equals and hashCode.

Commented solution. Two constructions produce two objects, so == is false. Texts are equal and equals true. Hashes must match for equal objects, but a collision can make unequal objects' hashes match. toString produces the readable description.

C10-D01 – A broken contract

Assignment. In a source copy replace the body of hashCode() with return System.identityHashCode(this);. Explain which contract rule is violated independently of numbers observed in one run. Restore the source.

Criterion. The reader recognizes that two instances equal according to equals must have the same hash, while identity-based hash does not guarantee this.

Hint. Do not base the conclusion on one pair of numbers seen once: compare the two method definitions.

Commented solution. equals compares text, but identityHashCode uses object identity. Code no longer guarantees that equal objects produce the same hash; a possible numeric coincidence does not repair the contract.

C10-P01 – An enumeration knowing a rule

Assignment. Run DemoValue.java and observe the last line. Add a print of Status.ON_LOAN.allowsLoan() in main. Predict the result, then modify the method to also accept lending from a possible new RETURNED status and explain why the change stays inside the enumeration.

Criterion. Before modification the method returns true for NEW and false for ON_LOAN. After adding RETURNED, it returns true for that value too, without comparing strings in the caller.

Hints. The method can return a boolean condition's result; there is no need to construct a new object for each call.

Commented solution. With two statuses, return this == NEW; distinguishes the new book from an already-lent one. After the third is added, return this == NEW || this == RETURNED; expresses the new rule in the status type. External code asks allowsLoan() without knowing constant names internally.

C11-A01 – Two measures of a string

Assignment. Explain lines 2 and 1 printed by DemoStrings.java for U+1F600. Indicate what each method counts and why neither is generally a counter of symbols perceived by the user.

Criterion. length() counts two UTF-16 units; codePointCount counts one code point. The answer recognizes possible multi-code-point sequences for one visible symbol.

Hint. Use figure 21 to follow the unit pair. Then think of a letter and a combining accent.

Commented solution. The example's supplementary code point is encoded with two UTF-16 units. The distinction among units, code points and perceived symbols emerges with text including combinations or compound emoji.

C11-P01 – Observable immutability

Assignment. Replace printing text.strip() with only the call text.strip();, then print text again. In a second test write text = text.strip(); and print again. Explain the difference.

Criterion. The first test keeps spaces; the second prints cleaned text because the variable receives the new value. strip() is not described as modifying the original object.

Hint. Follow which reference is assigned to text after the call.

Commented solution. A String is immutable. Calling strip() without using its result leaves text as it was; assigning the result makes the variable refer to the cleaned string.

C11-D01 – Text-block lines

Assignment. Compile DemoTextBlock.java and observe the two printed lines. Move the closing delimiter immediately after Java! and compare output as a character sequence, including any final line break.

Criterion. The answer distinguishes visible text from the end-of-line character; it recognizes that delimiter position affects the string value.

Hint. Ordinary printing can hide the final difference: add System.out.print("[end]") after the message to make it visible.

Commented solution. In the original source closing is on a new line and the value ends with a line break; [end] appears on the next line. With closing immediately after Java!, the value does not contain that last break and [end] follows Java! on the same line.

C12-A01 – Event order

Assignment. Predict output of the resource program before launching it. Identify primary and suppressed exceptions.

Criterion. The four lines are work, closing, primary: error during work, secondary: error during closing. The reader explains why close() is called even after the first error.

Hint. Follow the try body first, then automatic closing, finally catch.

Commented solution. The body calls use() and then throws the work error. Before catch, Java closes the resource; close() also throws an error, preserved as suppressed. The catch receives the body error and prints both.

C12-P01 – If only closing fails

Assignment. In a source copy remove only throw new IOException("error during work") from the try body. Predict output and suppressed-exception count; then verify.

Criterion. Output is work, closing, primary: error during closing; there are no suppressed exceptions.

Hint. Ask which exception exists when the body finishes normally before calling close().

Commented solution. The body no longer throws errors. The automatic close() call fails and that error becomes primary; getSuppressed() returns an empty array.

C12-D01 – Domain exception and cause

Assignment. Run DemoGrade.java. For each of three inputs indicate whether the method returns a grade or throws InvalidGradeException. Explain why only one error also shows a cause.

Criterion. 27 produces grade: 27; 31 produces error: out of range: 31; xx produces error: not a number: xx and cause: NumberFormatException.

Hint. Follow Integer.parseInt first, then range checking. Distinguish an exception created directly from conversion of an error received from the library.

Commented solution. 27 passes both checks. 31 is an integer but not an allowed grade, so the method directly creates the domain exception. xx cannot be converted; catch receives NumberFormatException and preserves it as the new exception's cause.

C12-D02 – Reading a trace

Assignment. Return to the variant with Book absent = null in C07-D01 and run it without a catch. In the stack trace identify exception type, message and the first line from your source. Describe which reference is missing.

Criterion. Diagnosis cites NullPointerException and the call absent.description(), distinguishing the manifestation point from the origin of null.

Hint. Start from the trace's first line, then find your class name on subsequent lines.

Commented solution. The type is NullPointerException: the variable absent contains null, so no object can receive description(). The call's line identifies where the error emerges; the preceding assignment explains why the reference is absent.

C12-D03 – Following the stack's error

Assignment. Compile IntStack.java and DemoPropagation.java together. Without running, predict the message and four printed method names. Then insert stack.push(9); before the try: which path will be followed and why will catch print nothing?

Criterion. With an empty stack output is empty stack, pop, read, show, main. With one element 9 is printed; popping succeeds and no exception reaches the handler.

Hints. Figure 23 shows the exception path, not the returned value's path. Start from the throwing method and travel back through callers.

Commented solution. pop rejects the empty stack; read and show have no handler, so the exception reaches main. The trace lists the throw point first, then callers. After pushing, pop returns 9 and the chain finishes normally: the catch branch does not execute.

Try the examples

Running the programs requires JDK 25. Download the individual Java files linked in the chapter or the complete example project, which includes instructions and a launcher script. The explanations also compare expected output: predict it before running the program.

Massimiliano Tarquini · CC BY-NC 4.0

Back to the top ↑