HomeBytesheetsRoadmapsUI TemplatesUI ToolsStack Discussion
4 Topics
Chapter•80 Minutes

Date-Time, I/O, NIO, and Serialization

Work correctly with time zones, files, paths, channels, buffers, streams, serialization concepts, and data boundaries.

The java.time API

Modern Java date-time APIs separate date, local date-time, instant, offset, and time zone.

Instant now = Instant.now();
LocalDate today = LocalDate.now();
ZonedDateTime india =
    ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));

Duration duration = Duration.between(
    now, now.plusSeconds(30)
);

Use Instant for machine-oriented points in time, LocalDate for a date without a zone, and ZonedDateTime when location matters.

For financial systems, BigDecimal is generally preferred when decimal arithmetic must be exact.

Time zones are business data

A timestamp without its intended time-zone semantics can be ambiguous.

Key Takeaways
  • Use java.time for new code.
  • Use Instant for absolute points in time.
  • Use explicit zones for human/business-local schedules.

Files, Paths, and I/O Streams

NIO.2 provides Path and Files for modern file operations.

Path path = Path.of("data", "users.txt");

Files.writeString(
    path,
    "Alice\nBob\n",
    StandardOpenOption.CREATE,
    StandardOpenOption.TRUNCATE_EXISTING
);

String content = Files.readString(path);

Byte streams handle binary data; character readers/writers handle text. Buffered APIs reduce the cost of many small operations. For large files, stream line-by-line instead of loading the entire file.

Key Takeaways
  • Path and Files are modern NIO.2 APIs.
  • Choose byte vs character APIs based on data semantics.
  • Avoid loading huge files into memory when streaming is sufficient.

Channels, Buffers, and Non-Blocking I/O

NIO channels provide a lower-level I/O abstraction. Buffers hold data being transferred between application code and channels.

try (FileChannel channel =
         FileChannel.open(Path.of("data.bin"), StandardOpenOption.READ)) {

    ByteBuffer buffer = ByteBuffer.allocate(4096);
    int read = channel.read(buffer);
    System.out.println("Read: " + read);
}

A ByteBuffer has position, limit, capacity, and remaining bytes. Calling flip() changes a buffer from writing mode to reading mode.

Key Takeaways
  • Channels transfer data through buffers.
  • flip() prepares a written buffer for reading.
  • Use NIO directly when its control is valuable; otherwise prefer higher-level abstractions.

Serialization and Data Boundaries

Java's built-in object serialization has compatibility and security concerns. For network APIs, JSON, Protobuf, Avro, or other explicit formats are often better because the wire contract is visible and language interoperability is easier.

public record UserDto(String id, String name) {}

DTOs separate external representations from internal domain models. If a system consumes untrusted serialized data, treat deserialization as a security boundary and avoid arbitrary object construction.

Untrusted deserialization

Never treat untrusted serialized object data as harmless input. Prefer explicit, constrained data formats.

Key Takeaways
  • Wire formats are API contracts.
  • DTOs protect internal models from external coupling.
  • Security must be considered at every deserialization boundary.