| InputStream/OutputStream | Byte-based; blocking; classic I/O |
| Reader/Writer | Character-based; Unicode; wraps byte streams |
| NIO Channel + Buffer | Bidirectional; non-blocking capable; direct memory |
| NIO2 Path/Files | Modern file API; Java 7; replaces File class |
| AsynchronousFileChannel | Fully async file I/O; CompletionHandler callback |
| Serialization | ObjectInputStream/ObjectOutputStream; implements Serializable |
Java I/O Streams
Q1. What is the difference between InputStream and Reader?
A: InputStream: byte-oriented (8-bit). Subclasses: FileInputStream, BufferedInputStream, ByteArrayInputStream. Reader: character-oriented (16-bit Unicode). Subclasses: FileReader, BufferedReader, StringReader. Bridge: InputStreamReader wraps InputStream with a specified Charset for byte-to-char conversion. Always specify encoding explicitly: new InputStreamReader(stream, StandardCharsets.UTF_8).
Q2. What is the Decorator pattern in Java I/O?
A: Java I/O uses the Decorator pattern: streams wrap other streams to add functionality. BufferedInputStream(new FileInputStream()) — adds buffering. GZIPInputStream(new FileInputStream()) — adds decompression. DataInputStream(new BufferedInputStream(new FileInputStream())) — adds primitive reading. Each decorator adds one responsibility. This enables composable I/O pipelines.
// Buffered, gzip-compressed, binary reading:
try (DataInputStream dis = new DataInputStream(
new BufferedInputStream(
new GZIPInputStream(
new FileInputStream("data.gz"))))) {
int value = dis.readInt();
}
Q3. What is BufferedInputStream and why is it important?
A: Without buffering: each read() calls OS system call — very expensive (microseconds per call). BufferedInputStream reads a large chunk (8KB default) from OS in one system call, then serves reads from memory buffer. For sequential file reading, buffering improves throughput by 10-100x. Always wrap FileInputStream in BufferedInputStream for text/binary file reading.
Q4. What is the difference between flush() and close()?
A: flush(): forces any buffered data to be written to the underlying stream/file — does NOT close the stream. close(): flushes and then releases the resource (file descriptor, socket). Use flush() when you want to send data without closing (streaming responses). close() calls flush() internally. Always close streams (or use try-with-resources) to prevent file descriptor leaks.
Q5. What is try-with-resources for I/O?
// Automatic close — no finally needed:
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} // reader.close() called automatically, even on exception
// Multiple resources:
try (InputStream in = new FileInputStream("in.txt");
OutputStream out = new FileOutputStream("out.txt")) {
// copy
} // both closed in reverse order
Q6. What is the difference between FileReader and InputStreamReader?
A: FileReader: convenience class wrapping FileInputStream + InputStreamReader. Uses the platform default charset — DANGEROUS (differs across OS, may corrupt non-ASCII data). InputStreamReader: explicit charset. ALWAYS prefer new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8) over FileReader for portable, reliable file reading.
Q7. What is a PrintStream vs PrintWriter?
A: PrintStream (System.out, System.err): byte-based, converts to platform default charset, auto-flush option. PrintWriter: character-based, explicit charset support, better for text output. PrintStream.println() may corrupt non-ASCII output on platforms with non-UTF-8 default charset. For writing text files, prefer PrintWriter with explicit charset.
Q8. What is ByteArrayInputStream and ByteArrayOutputStream?
A: In-memory streams — no file I/O. ByteArrayOutputStream: writes to byte array in memory; toByteArray() returns result. ByteArrayInputStream: reads from byte array. Use cases: serializing objects to byte[], testing stream-based code without files, intermediate processing. No close() needed (no resources to release).
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(myObject);
}
byte[] bytes = baos.toByteArray();
// Deserialize:
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
try (ObjectInputStream ois = new ObjectInputStream(bais)) {
MyObject obj = (MyObject) ois.readObject();
}
Q9. What is PipedInputStream/PipedOutputStream?
A: Piped streams connect two threads: one writes to PipedOutputStream, another reads from connected PipedInputStream. Provides inter-thread byte pipe with blocking semantics. Internal buffer (1024 bytes). Rarely used today — prefer BlockingQueue for inter-thread data transfer. Important: must be connected before use (constructor or connect() method).
Q10. What is ObjectInputStream and ObjectOutputStream?
A: ObjectOutputStream: serializes Java objects to a byte stream. writeObject(obj) serializes an object (and its entire object graph). ObjectInputStream: deserializes back. readObject() returns Object (cast needed). The class must implement Serializable. Streams can be wrapped: ObjectOutputStream(new GZIPOutputStream(new FileOutputStream())) for compressed serialization.
Java NIO Interview Questions
Q11. What is Java NIO and how does it differ from classic I/O?
A: NIO (java.nio, Java 1.4): Channel + Buffer model. Channels: bidirectional, can be non-blocking. Buffers: typed containers (ByteBuffer, CharBuffer, etc.) for data. Selectors: multiplex multiple channels on one thread. NIO2 (Java 7): Path, Files, AsynchronousChannel. Classic I/O: stream-based, one-direction, blocking. NIO: better for high-concurrency servers handling many connections. NIO2 provides better file API.
Q12. What is a Channel in NIO?
A: Channel: represents a connection to an entity (file, socket, pipe). Bidirectional (read + write). Types: FileChannel (file I/O), SocketChannel (TCP client), ServerSocketChannel (TCP server), DatagramChannel (UDP). Channels work with Buffers — read from channel into buffer, write from buffer to channel. FileChannel supports mmap and file locking.
Q13. What is a ByteBuffer and its position/limit/capacity?
A: ByteBuffer has three key state fields: capacity (total buffer size), limit (end of readable/writable data), position (next read/write position). flip(): after writing, switches to read mode (limit=position, position=0). clear(): switch back to write mode (position=0, limit=capacity). compact(): compacts unread data to front, ready for more writing. rewind(): re-read from beginning (position=0, limit unchanged).
ByteBuffer buf = ByteBuffer.allocate(1024);
channel.read(buf); // write into buffer
buf.flip(); // switch to read mode
while (buf.hasRemaining()) {
byte b = buf.get(); // read from buffer
}
buf.clear(); // ready for next write
Q14. What is the difference between allocate() and allocateDirect()?
A: ByteBuffer.allocate(n): heap buffer — managed by JVM GC, faster allocation, copy needed for OS I/O. ByteBuffer.allocateDirect(n): direct (off-heap) buffer — OS can directly use it for I/O without copy (zero-copy), slower allocation, not GCed directly (Cleaner). For frequent large I/O: allocateDirect is faster. Reuse direct buffers — don't create per-request.
Q15. What is a Selector in NIO?
A: Selector: allows one thread to monitor multiple channels for I/O readiness (non-blocking). Register channels with Selector with interest ops (OP_READ, OP_WRITE, OP_CONNECT, OP_ACCEPT). selector.select() blocks until at least one channel is ready. Returns Set<SelectionKey> for ready channels. Foundation of NIO-based servers (Netty, Tomcat NIO, Undertow). Enables C10K problem solution: 10K connections on one thread.
Selector selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select(); // block until events
for (SelectionKey key : selector.selectedKeys()) {
if (key.isAcceptable()) { /* accept client */ }
if (key.isReadable()) { /* read from client */ }
}
selector.selectedKeys().clear();
}
Q16. What is memory-mapped file I/O?
A: Memory-mapped file: maps a file directly into virtual memory address space. FileChannel.map(MapMode, position, size) returns MappedByteBuffer. OS page cache manages loading pages on demand — no explicit read() calls. Very fast for large file random access (no extra copy between kernel and user space). Used by databases (SQLite, chronicle-map) for fast persistent storage.
try (RandomAccessFile raf = new RandomAccessFile("bigfile.dat", "r");
FileChannel channel = raf.getChannel()) {
MappedByteBuffer mbb = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
byte b = mbb.get(10000); // random access — no read() call
}
Q17. What is FileChannel.transferTo() and zero-copy?
A: transferTo(position, count, targetChannel): transfers data from file channel to another channel without reading into JVM heap buffer. On Linux, uses sendfile() syscall — kernel directly copies from page cache to socket buffer (zero-copy). Dramatic performance improvement for file serving. Used by: Kafka for log segment delivery, file download endpoints.
try (FileChannel fileChannel = FileChannel.open(Path.of("large.file"))) {
SocketChannel socket = ...; // client socket
long transferred = fileChannel.transferTo(0, fileChannel.size(), socket);
}
NIO2 Path and Files API
Q18. What is the Path API (Java 7)?
A: java.nio.file.Path: modern replacement for java.io.File. Immutable representation of a file path. Advantages: works with symbolic links, relative paths, cross-platform path handling, integration with Files utility class. Create with Path.of("file.txt") (Java 11) or Paths.get("file.txt") (Java 7).
Path path = Path.of("/home/user/docs", "report.pdf");
Path parent = path.getParent(); // /home/user/docs
Path filename = path.getFileName(); // report.pdf
Path absolute = path.toAbsolutePath();
Path normalized = path.normalize(); // resolve .., .
boolean exists = Files.exists(path);
Q19. What are the key Files utility methods (Java 7)?
Path p = Path.of("data.txt");
// Read/Write:
String content = Files.readString(p, StandardCharsets.UTF_8); // Java 11
Files.writeString(p, "Hello", StandardCharsets.UTF_8); // Java 11
byte[] bytes = Files.readAllBytes(p);
List<String> lines = Files.readAllLines(p, StandardCharsets.UTF_8);
Stream<String> lineStream = Files.lines(p); // lazy stream
// File ops:
Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
Files.move(src, dest, StandardCopyOption.ATOMIC_MOVE);
Files.delete(p); // throws if not exists
Files.deleteIfExists(p);
Files.createDirectories(p); // mkdir -p
Files.createTempFile("prefix", ".tmp");
Q20. What is Files.walk() and how do you use it?
// Walk directory tree:
try (Stream<Path> paths = Files.walk(Path.of("/home/user"), 3)) { // max depth 3
paths.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".java"))
.forEach(System.out::println);
}
// Find files matching glob:
try (Stream<Path> paths = Files.find(startPath, 10,
(p, attr) -> attr.isRegularFile() && p.toString().endsWith(".xml"))) {
paths.forEach(System.out::println);
}
Q21. What is WatchService for directory monitoring?
A: WatchService monitors file system events asynchronously. Register paths with events: ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE. poll() or take() returns WatchKey when event occurs. Used by: IDEs (hot reload), Spring Boot DevTools (class reloading), log tailer implementations.
WatchService watcher = FileSystems.getDefault().newWatchService();
Path dir = Path.of("/watch/this");
dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY);
WatchKey key = watcher.take(); // blocks until event
for (WatchEvent<?> event : key.pollEvents()) {
System.out.println(event.kind() + ": " + event.context());
}
key.reset();
Java Serialization Interview Questions
Q22. What is Java serialization?
A: Serialization: converting an object to a byte stream for storage or transmission. Deserialization: reconstructing the object from bytes. Class must implement java.io.Serializable (marker interface). Use ObjectOutputStream to serialize, ObjectInputStream to deserialize. The serialized form includes class name, serial version UID, and field values.
Q23. What is serialVersionUID and why is it important?
A: serialVersionUID: long constant that identifies the class version for serialization compatibility. If not declared, JVM auto-generates based on class structure — adding/removing fields changes the UID, making existing serialized data incompatible (InvalidClassException). Declare explicitly to maintain backward compatibility: private static final long serialVersionUID = 1L;
public class User implements Serializable {
private static final long serialVersionUID = 1L; // explicit, stable
private String name;
private int age;
// Adding new fields with default values is backward-compatible
private String email = ""; // new field — OK with explicit serialVersionUID
}
Q24. What is the transient keyword?
A: transient fields are excluded from serialization — they're written as their default value (null/0/false) on deserialization. Use for: sensitive data (passwords, tokens), fields that can be recomputed (derived values), non-serializable objects (Thread, Socket, File). Always mark connection objects, caches, and security credentials as transient.
public class Session implements Serializable {
private static final long serialVersionUID = 1L;
private String userId; // serialized
private transient String token; // NOT serialized (sensitive)
private transient Connection db; // NOT serialized (not serializable)
private transient int cachedHash; // NOT serialized (recomputable)
}
Q25. What are writeObject() and readObject() custom serialization?
A: Override private void writeObject(ObjectOutputStream oos) and readObject(ObjectInputStream ois) to customize serialization. Use for: encrypting fields before serialization, validating on deserialization, writing fields not in default format, handling transient fields that need custom save/restore.
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject(); // write regular fields
oos.writeObject(encrypt(token)); // write encrypted sensitive data
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject(); // read regular fields
token = decrypt((String) ois.readObject()); // read and decrypt
}
Q26. What is the Externalizable interface?
A: Externalizable extends Serializable and requires implementing writeExternal(ObjectOutput) and readExternal(ObjectInput). Complete control over serialization format — no default field serialization. More efficient for custom formats but requires explicit writing/reading of all fields. Requires public no-arg constructor (for deserialization instantiation).
Q27. What are the security risks of Java serialization?
A: Deserialization attacks: crafted byte streams can trigger arbitrary code execution via gadget chains in libraries (Apache Commons Collections, Spring Framework). readObject() can invoke methods on attacker-controlled objects. Mitigations: 1) validateObject() override. 2) ObjectInputFilter (Java 9) to whitelist classes. 3) Avoid Java serialization entirely — use JSON (Jackson), Protobuf, or custom binary formats. 4) Never deserialize untrusted data.
// Java 9+ ObjectInputFilter:
ObjectInputStream ois = new ObjectInputStream(stream);
ois.setObjectInputFilter(info -> {
if (info.serialClass() == null) return ObjectInputFilter.Status.ALLOWED;
return allowedClasses.contains(info.serialClass().getName())
? ObjectInputFilter.Status.ALLOWED
: ObjectInputFilter.Status.REJECTED;
});
Q28. What is the readResolve() method?
A: readResolve(): called after deserialization to replace the deserialized object with another. Use for: maintaining singleton invariant (deserialization would create second instance), returning the correct enum constant. Without readResolve(), deserializing a singleton creates a NEW instance — breaks the singleton contract.
public class Singleton implements Serializable {
private static final Singleton INSTANCE = new Singleton();
private Object readResolve() {
return INSTANCE; // prevent new instance on deserialization
}
}
Q29. What is writeReplace() in serialization?
A: writeReplace(): called during serialization to replace the object being written. Returns the object that actually gets serialized (may be a different type). Use case: serializing a proxy object instead of the actual object, converting to a simpler serializable form. Complement of readResolve() which handles deserialization-side replacement.
Q30. What serialization alternatives exist for Java?
A: 1) JSON (Jackson, Gson): human-readable, language-neutral, widely used for REST APIs. 2) Protocol Buffers (Protobuf): Google's binary format, compact and fast, schema-defined. 3) Avro: schema evolution, Kafka-friendly, binary. 4) MessagePack: compact binary JSON alternative. 5) Kryo: fast Java-specific binary serialization (no security concerns of Java native serialization). 6) JSON with Jackson + @JsonProperty annotations: standard for modern Java services.
Q31. How does Jackson serialization differ from Java native serialization?
A: Jackson: serializes to JSON/XML/CBOR; language-agnostic; uses annotations (@JsonProperty, @JsonIgnore, @JsonSerialize); no serialVersionUID; safe from deserialization attacks; works with records (Java 16+). Java native: binary; Java-only; Serializable marker; security risk; harder to evolve. Always prefer Jackson for REST APIs and data exchange; use Kryo for Java-only fast serialization.
Q32. What is DataInputStream/DataOutputStream?
A: DataInputStream/DataOutputStream: read/write Java primitives in a portable binary format. Methods: readInt/writeInt, readLong/writeLong, readUTF/writeUTF, readDouble/writeDouble, etc. Data written with DataOutputStream can be read exactly with DataInputStream. Uses big-endian byte order. Useful for custom binary file formats and network protocols.
Q33. What is RandomAccessFile?
A: RandomAccessFile: read and write at any position in a file (random access). seek(position) moves file pointer. Methods: read/write primitives, readFully, skipBytes. Opens with mode "r" (read), "rw" (read/write), "rwd" (rw + flush metadata to disk), "rws" (rw + flush content+metadata). Use for: binary file formats, implementing databases, updating specific records in a file. NIO FileChannel is generally preferred for new code.
Q34. What is a Scanner and when to use it?
A: Scanner: tokenizes input using delimiters (default whitespace). Reads ints, doubles, strings, etc. from InputStream, File, or String. Use for: parsing simple structured input, reading from stdin (System.in). NOT for high-performance parsing or non-trivial formats. BufferedReader.readLine() is faster for line-by-line processing. Scanner internally uses regex — use with simple delimiters.
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
String s = sc.next(); // next token
String line = sc.nextLine();
sc.close();
Q35. What is StreamTokenizer?
A: StreamTokenizer: simple lexer that reads a stream and breaks it into tokens (numbers, words, quoted strings, comments). More powerful than Scanner for parsing structured text. ttype indicates token type: TT_NUMBER (numeric), TT_WORD (word), TT_EOF (end), TT_EOL (end-of-line). Used for simple custom file format parsing without a full parser library.
Q36. How do you efficiently read a large file line by line?
// Option 1: BufferedReader (classic, memory-efficient)
try (BufferedReader reader = Files.newBufferedReader(Path.of("large.txt"), StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
process(line);
}
}
// Option 2: Files.lines() stream (Java 8+, lazy)
try (Stream<String> lines = Files.lines(Path.of("large.txt"))) {
lines.filter(l -> l.startsWith("ERROR"))
.forEach(System.out::println);
} // MUST close stream to close file
Q37. What is the difference between Files.lines() and Files.readAllLines()?
A: Files.readAllLines(): reads ALL lines into memory as List<String> — bad for large files (OOM). Files.lines(): returns lazy Stream — reads one line at a time, memory-efficient for large files. MUST close the stream (try-with-resources) to release the file handle. For large files (>100MB), always use Files.lines() or BufferedReader.readLine().
Q38. What is AsynchronousFileChannel?
A: AsynchronousFileChannel: fully async file I/O. read(ByteBuffer, position, attachment, CompletionHandler) returns immediately; callback invoked when complete. Or returns Future<Integer> for future-based usage. Useful for: non-blocking I/O on file operations, serving large files asynchronously without blocking thread pool threads. More complex than standard I/O — use only when necessary.
AsynchronousFileChannel channel = AsynchronousFileChannel.open(Path.of("big.file"));
ByteBuffer buf = ByteBuffer.allocate(4096);
channel.read(buf, 0, buf, new CompletionHandler<Integer, ByteBuffer>() {
public void completed(Integer result, ByteBuffer attachment) {
attachment.flip();
processData(attachment);
}
public void failed(Throwable exc, ByteBuffer attachment) { /* handle error */ }
});
Q39. What is file locking in Java?
A: FileChannel.lock() / tryLock(): acquires OS-level file lock. Shared (read) or exclusive (write). Cross-process file coordination — prevents concurrent file access from different JVM instances (unlike Java synchronized, which is intra-JVM only). FileLock must be released explicitly. Advisories locks on most OS — process crash releases locks automatically.
try (FileChannel channel = FileChannel.open(lockFile, StandardOpenOption.WRITE);
FileLock lock = channel.tryLock()) { // null if already locked
if (lock != null) {
// exclusive access to lockFile
}
}
Q40. What is the difference between SequenceInputStream and multiple streams?
A: SequenceInputStream concatenates two or more InputStreams. Reads from first stream until EOF, then seamlessly continues from second. No code needed to switch between streams. Useful for: reading multiple file parts as one logical stream, concatenating header + body + footer streams for HTTP response assembly.
Q41. What are StandardOpenOption flags in NIO2?
// Open modes for Files.newOutputStream, FileChannel.open:
StandardOpenOption.READ // read access
StandardOpenOption.WRITE // write access
StandardOpenOption.CREATE // create if not exists
StandardOpenOption.CREATE_NEW // fail if exists
StandardOpenOption.APPEND // append to end
StandardOpenOption.TRUNCATE_EXISTING // clear file on open
StandardOpenOption.DELETE_ON_CLOSE // delete when closed
StandardOpenOption.SYNC // flush to OS on each write
// Example:
Path p = Path.of("output.log");
try (OutputStream os = Files.newOutputStream(p,
StandardOpenOption.CREATE, StandardOpenOption.APPEND)) {
os.write(logData);
}
Q42. How do you read a resource file from classpath?
// Option 1: Class loader
InputStream is = MyClass.class.getResourceAsStream("/config/app.properties");
// Option 2: ClassLoader
InputStream is2 = MyClass.class.getClassLoader().getResourceAsStream("config/app.properties");
// Note: getResourceAsStream returns null if not found (no exception)
// In Spring: @Value("classpath:config/app.yml") Resource resource;
// Or: ResourceLoader.getResource("classpath:...")
// Read content:
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
String content = reader.lines().collect(java.util.stream.Collectors.joining("\n"));
}
Q43. What is the difference between text mode and binary mode I/O?
A: Text mode (Reader/Writer): handles line endings (\r\n on Windows, \n on Unix), character encoding conversion. readLine() strips line endings. Text mode streams: FileReader, BufferedReader, PrintWriter. Binary mode (InputStream/OutputStream): raw bytes, no conversion. Use for images, archives, compressed data, custom binary protocols. Use text mode for text files, binary mode for everything else.
Q44. What are character encodings and why do they matter in Java I/O?
A: Java strings are UTF-16 internally. When reading/writing text files, encoding specifies byte-to-char conversion. Mismatch causes data corruption. Always specify: new InputStreamReader(stream, StandardCharsets.UTF_8). StandardCharsets: UTF_8, UTF_16, ISO_8859_1, US_ASCII. Use StandardCharsets (no checked exception) vs Charset.forName() (throws UnsupportedEncodingException). UTF-8 is the universal default for all new code.
Q45. What is console I/O in Java?
A: System.in: InputStream for stdin. System.out, System.err: PrintStreams for stdout/stderr. System.console(): returns Console object (null if no console — e.g., when running in IDE). Console.readLine(): read line without echo. Console.readPassword(): read password without echo (security). Console is null in non-interactive environments — always check before use.
Q46. What is the Charset class and important charsets?
A: Charset represents a named character encoding. StandardCharsets: guaranteed available on all Java platforms. UTF_8 (default for most use), US_ASCII (7-bit), ISO_8859_1 (Latin-1, HTTP default), UTF_16 (Java internal), UTF_16BE/LE (BOM-explicit). Use StandardCharsets.UTF_8 everywhere. Avoid platform default charset (Charset.defaultCharset()) — not portable.
Q47. How do you copy files efficiently in Java?
// Option 1: Files.copy (simplest, uses OS optimization)
Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
// Option 2: FileChannel.transferTo (zero-copy for large files)
try (FileChannel in = FileChannel.open(src);
FileChannel out = FileChannel.open(dest, StandardOpenOption.CREATE,
StandardOpenOption.WRITE)) {
in.transferTo(0, in.size(), out);
}
// Avoid: InputStream read loop — slow, creates byte[] buffers
Q48. What is Java's support for compressed streams?
// GZIP write:
try (GZIPOutputStream gzos = new GZIPOutputStream(new FileOutputStream("data.gz"))) {
gzos.write(data);
}
// GZIP read:
try (GZIPInputStream gzis = new GZIPInputStream(new FileInputStream("data.gz"))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = gzis.read(buffer)) != -1) {
process(buffer, bytesRead);
}
}
// ZIP:
try (ZipFile zipFile = new ZipFile("archive.zip")) {
ZipEntry entry = zipFile.getEntry("file.txt");
InputStream is = zipFile.getInputStream(entry);
}
Q49. What is the difference between FileInputStream.read() and read(byte[], int, int)?
A: read(): reads ONE byte — very slow in a loop (one system call per byte without buffering). read(byte[] buf, int off, int len): reads up to len bytes into array — one system call for many bytes, much faster. Always use array version for performance. BufferedInputStream provides automatic batching over the one-byte API.
Q50. What are the best practices for Java I/O?
A: 1) Always use try-with-resources for stream/channel lifecycle. 2) Always specify charset explicitly (UTF_8). 3) Wrap FileInputStream in BufferedInputStream for sequential reads. 4) Use Files.readString/writeString (Java 11) for small text files. 5) Use Files.lines() for large text files (lazy). 6) Use FileChannel.transferTo() for large file copies. 7) Avoid Java native serialization — use Jackson/Protobuf. 8) Never catch and swallow IOException silently. 9) Use Path/Files (NIO2) over File class for new code.
Q51. What is a FileDescriptor?
A: FileDescriptor: handle to an open file or socket at OS level. FileInputStream/FileOutputStream internally use a FileDescriptor. sync() forces all OS buffers to physical storage. Special instances: FileDescriptor.in (stdin), FileDescriptor.out (stdout), FileDescriptor.err (stderr). Rarely used directly — it's an implementation detail exposed for special cases like forcing fsync.
Q52. What is the Java NIO2 file system provider SPI?
A: NIO2 FileSystem is pluggable via FileSystemProvider SPI. Default provider handles OS filesystems. Other providers: ZipFileSystemProvider (read/write ZIP as filesystem), S3FileSystemProvider (S3 as filesystem), SFTP filesystem. This allows Files.copy(), Files.walk() etc. to work uniformly across different storage backends by just changing the Path's filesystem.
// Access ZIP file as filesystem:
try (FileSystem zip = FileSystems.newFileSystem(Path.of("archive.zip"))) {
Path inside = zip.getPath("/dir/file.txt");
String content = Files.readString(inside);
}
Q53. What is the File.separator and its portability issue?
A: File.separator: "/" on Unix, "\\" on Windows. Hard-coding "/" in paths fails on Windows. Fix: use Path.of("dir", "subdir", "file.txt") — NIO2 handles separator automatically. File.pathSeparator: ":" (Unix) or ";" (Windows) for PATH-style lists. Always use Path.of() or Paths.get() with separate components — never concatenate with hard-coded separators.
Q54. What is the difference between FileOutputStream with append and without?
// Truncate and write from start (default):
new FileOutputStream("log.txt") // overwrites existing
new FileOutputStream("log.txt", false) // same — truncate
// Append to existing file:
new FileOutputStream("log.txt", true) // append
// NIO2 equivalent:
Files.newOutputStream(Path.of("log.txt"), StandardOpenOption.APPEND)
Q55. What is the CharBuffer and how does it relate to NIO?
A: CharBuffer: NIO buffer for char data. Channels don't work directly with CharBuffer — you must encode to ByteBuffer first (CharBuffer → Charset.encode() → ByteBuffer → Channel). Useful for: working with CharSequence streams, NIO-based text parsing, Charset encoding/decoding. Charset.newDecoder().decode(byteBuffer) returns CharBuffer.
Q56. What is Files.probeContentType()?
A: Files.probeContentType(path): returns MIME type of file based on file extension and/or content (OS-dependent). Returns null if unknown. Platform-specific — may vary between OS. For reliable MIME detection: use Apache Tika or URLConnection.getFileNameMap().getContentTypeFor(filename). Spring's MediaTypeFactory detects MIME from extension list.
Q57. What is the SequenceInputStream use case?
// Merge two files for reading as one stream:
InputStream part1 = new FileInputStream("part1.dat");
InputStream part2 = new FileInputStream("part2.dat");
try (InputStream merged = new SequenceInputStream(part1, part2)) {
// read from merged seamlessly
}
// Or with Enumeration for many streams:
Enumeration<InputStream> streams = Collections.enumeration(List.of(s1, s2, s3));
InputStream all = new SequenceInputStream(streams);
Q58. What are the key serialization interview questions?
A: Top questions: 1) What is Serializable and serialVersionUID? 2) What does transient do? 3) How do you customize serialization (writeObject/readObject)? 4) What is Externalizable vs Serializable? 5) Security risks of deserialization. 6) readResolve() for singleton protection. 7) Why avoid Java native serialization (prefer Jackson)? 8) What is ObjectInputFilter (Java 9)?
Q59. What is NIO2 FileAttribute and BasicFileAttributes?
Path path = Path.of("file.txt");
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
System.out.println("Size: " + attrs.size());
System.out.println("Created: " + attrs.creationTime());
System.out.println("Modified: " + attrs.lastModifiedTime());
System.out.println("Is dir: " + attrs.isDirectory());
System.out.println("Is regular: " + attrs.isRegularFile());
Q60. What is the recommended approach for file I/O in modern Java?
A: 1) Small files: Files.readString()/writeString() (Java 11). 2) Large text files: Files.lines() with try-with-resources. 3) Large binary files: FileChannel with ByteBuffer, or transferTo(). 4) Memory-mapped files for random access: FileChannel.map(). 5) Directory operations: Files.walk(), Files.find(). 6) Serialization: Jackson JSON/Protobuf (not Java native). 7) File paths: Path.of() (not java.io.File). Always use try-with-resources and explicit UTF-8 charset.
Post a Comment
Add