mattone
dopo mattoneTHE BOOK SERIES
IT/EN
← Java guide

Java 25 · 36/39

C25–C28 activities: choose, test, transfer

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 →

Perform activities on copies of the programs. A asks you to predict and explain behavior, C to correct or complete a case, G to recognize the technical boundary, D to decide in a variant. Solutions indicate observable reasoning; successful execution of programs does not replace tests of error cases. Activity gates remain open until reading with users.

C25-A01 – Bytes or text?

Task. One file contains the byte sequence of a photograph and another contains titles in UTF-8. For each, choose between InputStream and BufferedReader, explaining the charset's role. In the import program, predict what happens if a line contains book;error.

Reasoned solution. The photograph remains a byte sequence; attempting to interpret it as text may change it or make decoding fail. Titles require the declared UTF-8 conversion. The separator-containing line produces IOException before final publication. To demonstrate the requirement, the test must start with an existing destination and verify that its content does not change.

C25-C01 – A failure test

Task. Modify a copy of the program so the message indicates the physical line number, including blank lines. Prepare input with two blank lines before the erroneous data. Also verify that no temporary files remain when validation fails.

Reasoned solution. A counter incremented at every readLine is needed, separate from imported-title count. finally removes the temporary file on failure; the test must check the directory, rather than only the exception. If cleanup also fails, retain the main cause and report the cleanup problem separately.

C25-D01 – Millions of lines and duplicates

Task. The archive grows beyond available memory and must contain no duplicate titles. Draw a solution maintaining all-or-nothing publication. Specify how you discover duplicates and the limit of your choice. Repeat the decision if the source comes from a resource inside a JAR.

Reasoned solution. Reading and writing remain progressive. An in-memory Set works only if distinct elements fit; otherwise an external strategy is needed, such as partitioning or sorting on disk. The file in the JAR is a packaged source, rather than a destination updateable with Files.move; publish the result at an external path. The solution must name the provider's atomic-move limit.

C26-A01 – 02:30 twice

Task. In the invoice program, explain why 2026-10-25T02:30 in Rome produces two offsets. Identify the two represented instants and why LocalDateTime alone is insufficient for an irrevocable booking.

Reasoned solution. During the return to standard time, civil time repeats. Offsets +02:00 and +01:00 lead to instants sixty minutes apart. A booking must retain the chosen offset or instant, along with the zone needed for presentation and future rules.

C26-C01 – Rounding in the right place

Task. Calculate 22% tax on three lines of 0.03 euros. Compare HALF_UP rounding to two decimal places for each line with rounding once after summing the bases. Do not use double as the source of values.

Reasoned solution. Each line produces 0.0066, then 0.01 after rounding; the sum of rounded taxes is 0.03. The overall base is 0.09, its tax 0.0198, rounded to 0.02. The difference demonstrates that the rounding point is a domain rule, rather than a syntax detail.

C26-D01 – Thirty days or 720 hours?

Task. A library grants a loan “for thirty civil days”; a service grants a window of “720 actual hours”. Choose types and operations for both rules. Explain what changes across a daylight-saving transition and how to make a test repeatable.

Reasoned solution. The first rule uses LocalDate.plusDays(30) in the library's civil context. The second uses an Instant and Duration.ofHours(720). The two deadlines may show different civil times because zone rules change the offset. A Clock.fixed eliminates tests' dependence on current time.

C27-A01 – Absence or failure?

Task. In the local client, predict what the caller receives if the server responds 404 with a text body, and what happens if no process listens on the port. Which case may become Optional.empty() for a book lookup?

Reasoned solution. A 404 is an HTTP response with status and body; the client receives it and the service contract may define it as normal absence. A refused connection is a communication failure and does not demonstrate that the book is missing. Preserve the distinction even if the user interface shows a simple sentence.

C27-C01 – A controlled retry

Task. Change the local server so the first GET /books returns 503 and the second 200. Implement at most one retry for this request only and record attempt count. Verify separately that a POST creating a reservation is not repeated without an idempotency contract.

Reasoned solution. The test must observe two calls in the temporary-failure case and one in the immediately positive case. After two failures the logic returns an explicit error; it does not enter an indefinite loop. For POST, a timeout leaves uncertainty about whether the server already created the reservation: an application rule is needed, such as a server-supported idempotency key.

C27-D01 – Ten requests, three places

Task. Ten HTTP requests start from virtual threads, but the remote service permits three simultaneous jobs. Describe where to apply the limit and how to check it. Compare the decision with sendAsync if the process must compose results without occupying a thread for every wait.

Reasoned solution. A Semaphore(3) around service access limits requests in progress independently of virtual-thread count. The test measures maximum active requests on the local server, rather than merely total time. sendAsync returns composable futures; that model also requires a capacity limit. The choice concerns program structure, rather than promising greater speed for an individual request.

C28-A01 – Bounds and lifetime

Task. In the FFM program, allocate two integers and try writing three, in an isolated copy. In another copy, retain the segment after arena closure and try reading it. Classify the two errors.

Reasoned solution. The first access exceeds the segment's spatial bounds; the second uses a region whose lifetime has ended. The test must predict where the program will fail. Do not propose the global arena merely to avoid reasoning about closure.

C28-G01 – What does strlen say?

Task. Replace "Java" with "cafés" in the program. Predict whether strlen counts five characters or the number of bytes in the UTF-8 C string. Explain why the size_t result layout must be verified on the platform.

Reasoned solution. strlen counts bytes before the final zero; é needs more than one byte in UTF-8, so the result exceeds five. size_t follows the platform's ABI, rather than being Java long by definition. If architecture or library changes, an unverified copied descriptor may be incorrect even if Java code compiles.

C28-D01 – A pointer retained by the library

Task. A C library retains the received pointer and uses it in a later callback. Design arena lifetime, sharing between threads and closure point. Specify which information is missing if you know only the C function's name.

Reasoned solution. The arena must remain alive at least until the final native use of the pointer and callback stub. If different threads access the region, a confined arena is insufficient; a consistent sharing and synchronization choice is needed. Without a C signature, layout, ABI and pointer-ownership contract, a correct binding cannot be built. A valid answer knows to stop before inventing these guarantees.

Integrated test – A receipt arriving from outside

Situation. A library program queries a local HTTP service to obtain a loan receipt. The service returns a text body with book code, due date, deposit amount and currency. The program must save a copy staff can read. Some requests may fail; the previous file must not become a partial receipt. This test combines the boundaries of chapters 25, 26 and 27. Chapter 28 remains further reading: the presence of a C library in the system is not by itself a reason to introduce FFM.

Task. First design the received-data contract: which charset, which date format, which monetary unit, which required fields and which maximum size? Identify errors occurring before an HTTP response exists, HTTP statuses to interpret and body-validation errors. Choose the Java type for the due date and amount, then describe how you publish the final file. Write at least four tests: valid response, 404 under the service contract, malformed body and connection interrupted after sending the request. For each test, specify the expected state of the previous file.

Reasoned solution. A civil due date is a LocalDate if the contract says “valid until that day” and does not fix a precise instant. The amount is interpreted from decimal text with BigDecimal, retaining a Currency or validated currency code separately. The interchange format declares UTF-8, an ISO date and a body-size limit. A parser checks field presence and grammar before treating data as a receipt. A 200 status is insufficient: the body may be incomplete or invalid.

The client sets timeouts consistent with the service and distinguishes transport failure, status and validation. 404 may mean “receipt absent” only if the remote contract says so; an interrupted connection does not equal absence. Valid content is written into a temporary file in the destination directory and closed before attempting publication. If the required atomic move is unavailable, the operation fails leaving the previous file; the product may choose another policy, but must declare and test it separately. The positive test checks content and essential metadata; the three negative cases check that the previous file is byte-for-byte identical and the temporary file is removed.

Transfer variant. The service begins providing receipts many megabytes long. A solution using BodyHandlers.ofString and then building another complete string may consume too much memory. Move to progressive body handling and impose a byte limit while reading; validation and publication remain distinct steps. If the format requires seeing the entire document before validation, retain and reread the temporary file, still avoiding exposing it as final. The choice does not change HTTP error semantics.

Assessment criterion. The answer connects every risk to its boundary: transport, status, format, time, number or file system. It justifies API choices and proves that the previous archive stays protected. FFM is unnecessary without a native requirement.

Advanced extension: computation exists only in a C library. In a second version, the library inherits a native library calculating a file checksum. The function receives a byte pointer and length, returns a numerical code and does not retain the pointer after returning. Before writing an FFM binding, readers must obtain the C header, know the exact length type in the ABI used and know what return codes mean. The file has already been received and validated; native integration intervenes only to calculate the checksum, before publication. If the function returns an error, the previous file remains intact.

A justified solution allocates a segment large enough for the bytes to process, copies the block into it according to the chosen API and keeps it valid throughout the call. Because the library does not retain the pointer, a closeable arena around the call may suffice; if the contract changed and the pointer were retained, lifetime would need redesign. The FunctionDescriptor reflects the library's signature on that platform, rather than an assumption derived from an example found elsewhere. The return code is translated into a domain result distinguishing a calculated checksum from native failure. The checksum value, library name and version enter verification evidence, because a binary update may change the result even if Java source is identical.

Extension tests execute at least three cases: a known file with an expected checksum, an empty file and a call returning an error code. The empty-file test clarifies whether the function accepts a pointer to zero bytes or still requires a valid address; the Java program cannot invent the answer, which must come from the C contract. The error case confirms that the temporary file is not published. These tests run on the declared native platform, ideally in a process separate from other checks, because an incorrect binding may terminate the JVM. FFM is justified only by the new C-library constraint: the previous Java-API path remains the simplest solution while that constraint does not exist.

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 ↑