Read and write console and file data using I/O Streams.
Serialize and de-serialize Java objects.
Create, traverse, read, and write Path objects and their properties using java.nio.file API.
Files
ClassLet’s start by defining some concepts.
As we know, data is organized into files, directories, and file systems in a computer.
A file is a group of related data stored on a disk or other storage device. Files can contain programs, documents, images or any other types of data.
A directory, also called a folder, is a collection of files and other directories that are stored under the same name. Directories allow you to organize files in a hierarchical structure. For example:
documents/
work/
report.pdf
presentation.ppt
personal/
resume.doc
family.jpg
The directory at the very top of the structure is known as the root directory. In Unix systems it is represented by a forward slash (/
), while on Windows it is identified by a drive letter followed by a colon, like C:
.
To locate a specific file or directory, you need to specify its path, the route from the root directory to that particular item in the hierarchy. Path separators differ between operating systems. Unix uses a forward slash (/
) while Windows uses a backslash (\
).
A path can be absolute, specifying the complete route from the root:
/home/steve/documents/work/report.pdf
C:\Users\Steve\Documents\Work\report.pdf
Or it can be relative, specifying the route from the current directory, also known as the working directory:
documents/work/report.pdf
..\personal\resume.doc
Two special symbols are commonly used in relative paths:
.
) represents the current directory..
) represent the parent of the current directory (one level up in the hierarchy)So for example, if the current directory is /home/steve/documents
, then:
./work/report.pdf
is equivalent to work/report.pdf
../downloads/file.zip
refers to /home/steve/downloads/file.zip
In Java, you can work with the file system in two main ways:
java.io.File
class (legacy I/O API)java.nio.file.Path
interface (NIO.2 API)To create a File
instance, simply pass a file or directory path to its constructor:
File file = new File("/home/steve/documents/work/report.pdf");
File dir = new File("C:\\Users\\Steve\\Documents");
Note that this does not actually create the file or directory on the disk, it just creates an object that represents that path. You can then call various methods on the File
object to get information about the file or directory or to manipulate it.
In the newer NIO.2 (New Input/Output) API, paths are represented by the Path
interface rather than the File
class. You can get a Path
instance in several ways:
// 1. Using the Paths helper class
Path p1 = Paths.get("/home/steve/documents/work/report.pdf");
// 2. From a File object
File file = new File("C:\\Users\\Steve\\Documents");
Path p2 = file.toPath();
// 3. By joining path strings
Path p3 = Paths.get("documents", "work", "report.pdf");
Path p4 = Paths.get("/home", "steve").resolve("documents");
// 4. From the default FileSystem
Path p5 = FileSystems.getDefault().getPath("documents/work/report.pdf");
You can easily convert between File
and Path
using the toFile()
and toPath()
methods:
File file = path.toFile();
Path path = file.toPath();
The Path
interface provides similar methods to File
but offers more flexibility and additional features for working with paths.
For example, you can extract specific parts of a path:
Path path = Paths.get("/home/steve/documents/work/report.pdf");
Path parent = path.getParent(); // /home/steve/documents/work
Path root = path.getRoot(); // /
Path name = path.getFileName(); // report.pdf
Or construct paths by joining elements:
Path documents = Paths.get("/home/steve/documents");
Path file = documents.resolve("work/report.pdf");
The resulting path doesn’t have to exist, it is just an abstract representation that can be used for further processing.
In the next sections, we’ll focus on the Path
interface and the NIO.2 API.
Let’s explore in more detail some of the key methods and concepts related to Path
.
This interface provides the following methods for retrieving basic path information:
String toString()
: Returns the string representation of the path.int getNameCount()
: Returns the number of name elements in the path.Path getName(int index)
: Returns the name element at the specified index.Here’s an example:
Path path = Paths.get("/home/user/documents/file.txt");
System.out.println(path.toString()); // Output: /home/user/documents/file.txt
System.out.println(path.getNameCount()); // Output: 4
System.out.println(path.getName(0)); // Output: home
System.out.println(path.getName(2)); // Output: documents
Additionally, there are methods for accessing the path elements:
Path getFileName()
: Returns the filename (the last element) of the path.Path getRoot()
: Returns the root component of the path, or null
if the path is relative.Path getParent()
: Returns the parent path, or null
if there is no parent.Here’s an example:
Path path = Paths.get("/home/user/documents/file.txt");
System.out.println(path.getFileName()); // Output: file.txt
System.out.println(path.getRoot()); // Output: /
System.out.println(path.getParent()); // Output: /home/user/documents
The relativize
method constructs a relative path between the current path and a given path. For example:
Path base = Paths.get("/home/user");
Path path = Paths.get("/home/user/documents/file.txt");
Path relativePath = base.relativize(path);
System.out.println(relativePath); // Output: documents/file.txt
The normalize
method returns a path that is a normalized version of the original path, eliminating any redundant elements such as .
(current directory) and ..
(parent directory):
Path path = Paths.get("/home/user/./documents/../file.txt");
Path normalizedPath = path.normalize();
System.out.println(normalizedPath); // Output: /home/user/file.txt
The toRealPath
method returns the real path of an existing file in the file system, resolving any symbolic links:
Path path = Paths.get("/path/to/symlink");
Path realPath = path.toRealPath();
System.out.println(realPath); // Output: /actual/path/to/file
The resolve
method resolves a path against the current path, allowing you to mix absolute and relative paths:
Path base = Paths.get("/home/user");
Path relativePath = Paths.get("documents/file.txt");
Path resolvedPath = base.resolve(relativePath);
System.out.println(resolvedPath); // Output: /home/user/documents/file.txt
However, if the path to be resolved is already an absolute path, it will be returned as-is:
Path base = Paths.get("/home/user");
Path absolutePath = Paths.get("/other/path/file.txt");
Path resolvedPath = base.resolve(absolutePath);
System.out.println(resolvedPath); // Output: /other/path/file.txt
Files
ClassThe java.nio.file.Files
class is part of the NIO.2 API. It provides a rich set of static utility methods for working with files and directories in a more concise and efficient manner compared to the legacy File
class.
These are some of its key features:
Improved Exception Handling:
Many of the Files
methods throw more specific exceptions like NoSuchFileException
, DirectoryNotEmptyException
, etc., making it easier to handle different error scenarios. In contrast, the File
class methods typically return boolean values or throw more generic exceptions.
Symbolic Links Support:
The Files
class has built-in support for symbolic links. You can create, detect, and resolve symbolic links using methods like createSymbolicLink()
, isSymbolicLink()
, and readSymbolicLink()
.
Atomic Operations:
The Files
class provides methods for performing atomic file operations. For example, move()
with the StandardCopyOption.ATOMIC_MOVE
option ensures that a file move operation is performed atomically.
File Attributes:
The Files
class makes it easy to read and modify file attributes, such as file permissions, owners, timestamps, etc. You can use methods like readAttributes()
, setOwner()
, setLastModifiedTime()
, etc.
Directory Walking:
The Files.walkFileTree()
method allows you to recursively traverse a directory tree and perform actions on each file and directory encountered. This is more efficient and flexible than manually traversing the tree using the File
class.
Streams and Buffers:
The Files
class provides methods to open files as streams (newInputStream()
, newOutputStream()
) or buffered readers/writers (newBufferedReader()
, newBufferedWriter()
), making I/O operations more convenient.
Path Operations:
Since the Files
class works with Path
objects, it can perform path-related operations like resolving, normalizing, and getting path components, etc.
File Content Operations:
The Files
class has methods to read and write file content in a single line of code, such as readAllBytes()
, readAllLines()
, write()
, etc. This eliminates the need for manual file I/O boilerplate.
Here are some of the key methods provided by the Files
class:
static Path createFile(Path path)
: Creates a new file.static Path createDirectory(Path path)
: Creates a new directory.static Path createDirectories(Path path)
: Creates a directory and all nonexistent parent directories.static void delete(Path path)
: Deletes a file or directory.static boolean deleteIfExists(Path path)
: Deletes a file or directory if it exists.static Path copy(Path source, Path target, CopyOption... options)
: Copies a file or directory.static Path move(Path source, Path target, CopyOption... options)
: Moves or renames a file or directory.static byte[] readAllBytes(Path path)
: Reads all bytes from a file.static String readString(Path path)
: Reads a file as a string.static List<String> readAllLines(Path path)
: Reads all lines from a file.static Path write(Path path, byte[] bytes, OpenOption... options)
: Writes bytes to a file.static Path writeString(Path path, CharSequence csq, OpenOption... options)
: Writes a string to a file.static Path write(Path path, Iterable<? extends CharSequence> lines, OpenOption... options)
: Writes lines of text to a file.static boolean exists(Path path, LinkOption... options)
: Checks if a file or directory exists.static boolean notExists(Path path, LinkOption... options)
: Checks if a file or directory does not exist.static boolean isReadable(Path path)
: Checks if a file is readable.static boolean isWritable(Path path)
: Checks if a file is writable.static boolean isExecutable(Path path)
: Checks if a file is executable.static boolean isDirectory(Path path, LinkOption... options)
: Checks if a path is a directory.static boolean isRegularFile(Path path, LinkOption... options)
: Checks if a path is a regular file.static long size(Path path)
: Returns the size of a file.static Stream<Path> list(Path dir)
: Returns a stream of entries in a directory.static Stream<Path> walk(Path start, FileVisitOption... options)
: Returns a stream that is lazily populated with Path
by walking the file tree rooted at a given starting file.static Path walkFileTree(Path start, FileVisitor<? super Path> visitor)
: Walks a file tree.static Path createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs)
: Creates a symbolic link.static Path readSymbolicLink(Path link)
: Reads the target of a symbolic link.static Path setPosixFilePermissions(Path path, Set<PosixFilePermission> perms)
: Sets POSIX file permissions.static Set<PosixFilePermission> getPosixFilePermissions(Path path, LinkOption... options)
: Reads POSIX file permissions.Several methods of the Files
class take optional arguments that control how the operation is performed. Here are some common ones:
java.nio.file.LinkOption
: Specifies how symbolic links are handled. A common value is LinkOption.NOFOLLOW_LINKS
, which indicates that symbolic links should not be followed.java.nio.file.StandardCopyOption
: Controls how a file copy operation should be done. Values include REPLACE_EXISTING
(replace the target if it exists), COPY_ATTRIBUTES
(copy file attributes as well), ATOMIC_MOVE
(perform an atomic move operation).java.nio.file.StandardOpenOption
: Specifies options for opening a file. Common values are CREATE
(create a new file if it doesn’t exist), APPEND
(append to the end of the file), TRUNCATE_EXISTING
(truncate the file if it exists).java.nio.file.FileVisitOption
: Used with the Files.walkFileTree()
method to control how the file tree traversal is done. The FOLLOW_LINKS
value indicates that symbolic links should be followed during traversal.In the next sections, we’ll review some of the methods and optional arguments of this class in more detail. But first, let’s talk about I/O streams.
In Java, I/O (Input/Output) streams provide a way to read data from a source or write data to a destination.
Here’s an analogy to explain I/O streams. Imagine you have a water tank and you want to transfer the water to another container. You can connect a pipe between the tank and the container, and the water will flow from the tank to the container through the pipe. Similarly, I/O streams act as the pipe, allowing data to flow from a source (a file, network, or memory) to a destination (another file, network, or memory).
I/O streams can be classified into several categories.
First of all, Java provides two types of I/O streams: byte streams and character streams.
Byte streams, as the name suggests, read and write data in the form of bytes (8-bit data). They are suitable for handling raw binary data, such as images, audio files, or any other type of non-text data. Examples include InputStream
and OutputStream
.
Character streams are designed to read and write data in the form of characters (16-bit Unicode data). They are useful for handling text-based data, such as reading from or writing to text files. Examples include Reader
and Writer
.
I/O streams can also be classified into input streams and output streams.
Input streams are used to read data from a source. They provide methods like read()
to read bytes or characters from the input source. Examples of input stream classes in Java include FileInputStream
, BufferedInputStream
, FileReader
, and BufferedReader
.
Output streams are used to write data to a destination. They provide methods like write()
to write bytes or characters to the output destination. Examples of output stream classes in Java include FileOutputStream
, BufferedOutputStream
, FileWriter
, and BufferedWriter
.
Finally, I/O streams can be categorized into low-level streams and high-level streams.
Low-level streams, also known as node streams, are directly connected to the data source or destination. They are the building blocks of I/O operations and provide basic functionality for reading from or writing to a specific source or destination. Examples of low-level streams include FileInputStream
, FileOutputStream
, FileReader
, and FileWriter
.
High-level streams, also known as processing streams or filter streams, are built on top of low-level streams. They provide additional functionality and features, such as buffering, filtering, or transforming the data as it passes through the stream. Examples of high-level streams include BufferedInputStream
, BufferedOutputStream
, BufferedReader
, BufferedWriter
, ObjectInputStream
, and ObjectOutputStream
.
The java.io
library defines four abstract classes that serve as the parents of all I/O stream classes:
InputStream
: The base class for all byte input streams.OutputStream
: The base class for all byte output streams.Reader
: The base class for all character input streams.Writer
: The base class for all character output streams.These abstract classes provide the fundamental methods for reading from or writing to a stream, such as read()
, write()
, close()
, and more. Concrete stream classes extend these base classes to provide specific functionality.
Java provides a wide range of concrete I/O stream classes in the java.io
package. Some commonly used classes include:
FileInputStream
and FileOutputStream
: Used for reading from and writing to files as byte streams.FileReader
and FileWriter
: Used for reading from and writing to files as character streams.BufferedInputStream
and BufferedOutputStream
: Provide buffering capabilities to improve performance of byte streams.BufferedReader
and BufferedWriter
: Provide buffering capabilities and additional methods for reading and writing character streams.ObjectInputStream
and ObjectOutputStream
: Used for reading and writing Java objects to streams.PrintStream
and PrintWriter
: Provide methods for writing formatted data to a stream.These concrete classes extend the appropriate base classes (InputStream
, OutputStream
, Reader
, or Writer
) and implement specific functionality for handling different types of data sources and destinations.
FileInputStream
FileInputStream
reads bytes from a file. It inherits from InputStream
.
It can be created either with a File
object or a String path
:
FileInputStream(File file)
FileInputStream(String path)
Here’s how you use it:
try (InputStream in = new FileInputStream("/file.txt")) {
int b;
// -1 indicates the end of the file
while((b = in.read()) != -1) {
// Do something with the byte read
}
} catch(IOException e) {
/** ... */
}
There’s also a read()
method that reads bytes into an array of bytes:
byte[] data = new byte[1024];
int numberOfBytesRead;
while((numberOfBytesRead = in.read(data)) != -1) {
// Do something with the array data
}
All the classes we’ll review should be closed. Fortunately, they implement java.lang.AutoCloseable
so they can be used in a try-with-resources
.
Also, almost all methods of these classes throw IOExceptions
or one of its subclasses (such as FileNotFoundException
, which is pretty descriptive).
FileOutputStream
FileOutputStream
writes bytes to a file. It inherits from OutputStream
.
It can be created either with a File
object or a String
path and an optional boolean
that indicates whether you want to overwrite or append to the file if it exists (it’s overwritten by default):
FileOutputStream(File file)
FileOutputStream(File file, boolean append)
FileOutputStream(String path)
FileOutputStream(String path, boolean append)
Here’s how you use it:
try (OutputStream out = new FileOutputStream("/file.txt")) {
int b;
// Made up method to get some data
while((b = getData()) != -1) {
// Writes b to the file output stream
out.write(b);
out.flush();
}
} catch(IOException e) {
/** ... */
}
When you write to an OutputStream
, the data may get cached internally in memory and written to disk at a later time. If you want to make sure that all data is written to disk without having to close the OutputStream
, you can call the flush()
method every once in a while.
FileOutputStream
also contains overloaded versions of write()
that allow you to write data contained in a byte array.
FileReader
FileReader
reads characters from a text file. It inherits from Reader
.
It can be created either with a File
object or a String
path:
FileReader(File file)
FileReader(String path)
Here’s how you use it:
try (Reader r = new FileReader("/file.txt")) {
int c;
// -1 indicates the end of the file
while((c = r.read()) != -1) {
char character = (char)c;
// Do something with the character
}
} catch(IOException e) {
/** ... */
}
There’s also a read()
method that reads characters into an array of char
s:
char[] data = new char[1024];
int numberOfCharsRead = r.read(data);
while((numberOfCharsRead = r.read(data)) != -1) {
// Do something with the array data
}
FileReader
assumes that you want to decode the characters in the file using the default character encoding of the machine your program is running on.
FileWriter
FileWriter
writes characters to a text file. It inherits from Writer
.
It can be created either with a File
object or a String
path and an optional boolean
that indicates whether you want to overwrite or append to the file if it exists (it’s overwritten by default):
FileWriter(File file)
FileWriter(File file, boolean append)
FileWriter(String path)
FileWriter(String path, boolean append)
Here’s how you use it:
try (Writer w = new FileWriter("/file.txt")) {
w.write('-'); // writing a character
// writing a string
w.write("Writing to the file...");
} catch(IOException e) {
/** ... */
}
Just like an OutputStream
, the data may get cached internally in memory and written to disk at a later time. If you want to make sure that all data is written to disk without having to close the FileWriter
, you can call the flush()
method every once in a while.
FileWriter
also contains overloaded versions of write()
that allow you to write data contained in a char
array, or in a String
.
FileWriter
assumes that you want to encode the characters in the file using the default character encoding of the machine your program is running on.
BufferedReader
BufferedReader
reads text from a character stream. Rather than read one character at a time, BufferedReader
reads a large block at a time into a buffer. It inherits from Reader
.
This is a wrapper class that is created by passing a Reader
to its constructor, and optionally, the size of the buffer:
BufferedReader(Reader in)
BufferedReader(Reader in, int size)
BufferedReader
has one extra read method (in addition to the ones inherited by Reader
), readLine()
. Here’s how you use it:
try (BufferedReader br = new BufferedReader(new FileReader("/file.txt"))) {
String line;
// null indicates the end of the file
while((line = br.readLine()) != null) {
// Do something with the line
}
} catch(IOException e) {
/** ... */
}
When the BufferedReader
is closed, it will also close the Reader
instance it reads from.
BufferedWriter
BufferedWriter
writes text to a character stream, buffering characters for efficiency. It inherits from Writer
.
This is a wrapper class that is created by passing a Writer
to its constructor, and optionally, the size of the buffer:
BufferedWriter(Writer out)
BufferedWriter(Writer out, int size)
BufferedWriter
has one extra write method (in addition to the ones inherited by Writer
), newLine()
. Here’s how you use it:
try (BufferedWriter bw = new BufferedWriter(new FileWriter("/file.txt"))) {
bw.write("Writing to the file...");
bw.newLine();
} catch(IOException e) {
/** ... */
}
Since data is written to a buffer first, you can call the flush()
method to make sure that the text written until that moment is indeed written to the disk.
When the BufferedWriter
is closed, it will also close the Writer
instance it writes to.
ObjectInputStream
and ObjectOutputStream
The process of converting an object to a data format that can be stored (in a file, for example) is called serialization and converting that stored data format into an object is called deserialization.
If you want to serialize an object, its class must implement the java.io.Serializable
interface, which has no methods to implement, it only tags the objects of that class as serializable.
We’ll cover this process in more detail later, but right now, you have to know that ObjectOutputStream
allows you to serialize objects to an OutputStream
while ObjectInputStream
allows you to deserialize objects from an InputStream
. So both are considered wrapper classes.
Here’s the constructor of the ObjectOutputStream
class:
ObjectOutputStream(OutputStream out)
This class has methods to write many primitive types, like:
void writeInt(int val)
void writeBoolean(boolean val)
But the most useful is writeObject(Object)
. Here’s an example:
class Box implements java.io.Serializable {
/** ... */
}
...
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.dat"))) {
Box box = new Box();
oos.writeObject(box);
} catch(IOException e) {
/** ... */
}
To deserialize the file obj.dat
, we use ObjectInputStream
class. Here’s its constructor:
ObjectInputStream(InputStream in)
This class has methods to read many data types, among them:
Object readObject() throws IOException, ClassNotFoundException
Notice that it returns an Object
type. Thus, we have to cast the object explicitly. This can lead to a ClassCastException
thrown at runtime. Note that this method also throws a ClassNotFoundException
(a checked exception), in case the class of a serialized object cannot be found.
Here’s an example:
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.dat"))) {
Box box = null;
Object obj = ois.readObject();
if(obj instanceof Box) {
box = (Box)obj;
}
} catch(IOException ioe) {
/** ... */
} catch(ClassNotFoundException cnfe) {
/** ... */
}
PrintStream
PrintStream
is a subclass of OutputStream
that adds functionality for printing various data types in a human-readable format. It is similar to PrintWriter
, but it works with OutputStreams
only. Here’s a look at its constructors:
PrintStream(OutputStream out)
PrintStream(OutputStream out, boolean autoFlush)
PrintStream(OutputStream out, boolean autoFlush, String encoding) throws UnsupportedEncodingException
PrintStream(File file) throws FileNotFoundException
PrintStream(File file, String encoding) throws FileNotFoundException, UnsupportedEncodingException
PrintStream(String fileName) throws FileNotFoundException
PrintStream(String fileName, String encoding) throws FileNotFoundException, UnsupportedEncodingException
By default, it uses the default charset of the machine you’re running the program, but you can specify a charset if needed.
PrintStream
has the write()
method like other OutputStream
subclasses, but it overrides them to avoid throwing an IOException
.
It also adds methods such as print()
, println()
, format()
, and printf()
for convenient output. Here’s how you use this class:
// Opens or creates the file without automatic line flushing
// and using the default character encoding
try (PrintStream ps = new PrintStream("file.txt")) {
ps.write("Hi".getBytes()); // Writing a String as bytes
ps.write(100); // Writing a character as bytes
// write the string representation of the argument
// it has versions for all primitives, char[], String, and Object
ps.print(true);
ps.print(10);
// same as print() but it also writes a line break as defined by
// System.getProperty("line.separator") after the value
ps.println(); // Just writes a new line
ps.println("A new line...");
// format() and printf() are the same methods
// They write a formatted string using a format string,
// its arguments and an optional Locale
ps.format("%s %d", "Formatted string ", 1);
ps.printf("%s %d", "Formatted string ", 2);
ps.format(Locale.GERMAN, "%.2f", 3.1416);
ps.printf(Locale.GERMAN, "%.3f", 3.1416);
} catch (FileNotFoundException e) {
// if the file cannot be opened or created
}
You can learn more about format strings for format()
and printf()
in the documentation of the java.util.Formatter
class.
PrintWriter
PrintWriter
is a subclass of Writer
that writes formatted data to another (wrapped) stream, even an OutputStream
. Just look at its constructors:
PrintWriter(File file) throws FileNotFoundException
PrintWriter(File file, String charset) throws FileNotFoundException, UnsupportedEncodingException
PrintWriter(OutputStream out)
PrintWriter(OutputStream out, boolean autoFlush)
PrintWriter(String fileName) throws FileNotFoundException
PrintWriter(String fileName, String charset) throws FileNotFoundException, UnsupportedEncodingException
PrintWriter(Writer out)
PrintWriter(Writer out, boolean autoFlush)
By default, it uses the default charset of the machine you’re running the program, but this class accepts the following charsets (there are other optional charsets):
US-ASCII
ISO-8859-1
UTF-8
UTF-16BE
UTF-16LE
UTF-16
As any Writer
, this class has the write()
method we’ve seen in other Writer
subclasses, but it overwrites them to avoid throwing an IOException
.
It also adds the methods format()
, print()
, printf()
, println()
.
Here’s how you use this class:
// Opens or creates the file without automatic line flushing
// and converting characters by using the default character encoding
try(PrintWriter pw = new PrintWriter("/file.txt")) {
pw.write("Hi"); // Writing a String
pw.write(100); // Writing a character
// write the string representation of the argument
// it has versions for all primitives, char[], String, and Object
pw.print(true);
pw.print(10);
// same as print() but it also writes a line break as defined by
// System.getProperty("line.separator") after the value
pw.println(); // Just writes a new line
pw.println("A new line...");
// format() and printf() are the same methods
// They write a formatted string using a format string,
// its arguments and an optional Locale
pw.format("%s %d", "Formatted string ", 1);
pw.printf("%s %d", "Formatted string ", 2);
pw.format(Locale.GERMAN, "%.2f", 3.1416);
pw.printf(Locale.GERMAN, "%.3f", 3.1416);
} catch(FileNotFoundException e) {
// if the file cannot be opened or created
}
Just like with PrintWriter
, you can learn more about format strings for format()
and printf()
in the documentation of the java.util.Formatter
class.
Java initializes and provides three stream objects as public static
fields of the java.lang.System
class:
InputStream System.in
PrintStream System.out
PrintStream System.err
Remember, PrintStream
does exactly the same and has the same features that PrintWriter
, it just works with OutputStreams
only.
The following example shows how to read a single character (a byte) from the command line:
System.out.print("Enter a character: ");
try {
int c = System.in.read();
} catch(IOException e) {
System.err.println("Error: " + e);
}
Or to read strings:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = br.readLine();
// Or using the java.util.Scanner class
Scanner scanner = new Scanner(System.in);
String line = scanner.nextLine();
These streams (System.in
, System.out
, System.err
) are used for basic input and output in many Java programs.
You have previously learned that the java.nio.file.Files
class provides various methods for file operations like copying, moving, deleting, and comparing files. Let’s explore these operations in detail.
The Files.copy()
method allows you to copy a file from one location to another. It takes a source path and a target path as parameters.
If the target file already exists, you can specify how to handle the copy operation using the StandardCopyOption
enum.
Path source = Paths.get("path/to/source/file.txt");
Path target = Paths.get("path/to/target/file.txt");
// Copy the file, replacing the target file if it exists
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
The StandardCopyOption.REPLACE_EXISTING
option indicates that if the target file already exists, it should be replaced with the source file.
You can also copy files using I/O streams. This is useful when you need more control over the copying process or when working with large files:
try (InputStream inputStream = new FileInputStream("source.txt");
OutputStream outputStream = new FileOutputStream("target.txt")) {
byte[] buffer = new byte[1024]; // Buffer size can be adjusted for performance
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
In this example, we create an InputStream
to read from the source file and an OutputStream
to write to the target file. We use a buffer to read and write the data in chunks.
To copy a file into a directory, you can specify the target directory path and the filename.
Path sourceFile = Paths.get("path/to/source/file.txt");
Path targetDirectory = Paths.get("path/to/target/directory");
// Copy the file into the target directory
Files.copy(sourceFile, targetDirectory.resolve(sourceFile.getFileName()));
The targetDirectory.resolve(sourceFile.getFileName())
expression creates the target path by combining the target directory path with the file name of the source file.
The Files.move()
method allows you to move or rename a file or directory.
Path source = Paths.get("path/to/source/file.txt");
Path target = Paths.get("path/to/target/file.txt");
// Move the file
Files.move(source, target);
If the target file already exists, an exception will be thrown. You can use the StandardCopyOption.REPLACE_EXISTING
option to replace the target file if it exists:
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
An atomic move ensures that the move operation is performed as a single indivisible operation. It either completes successfully or fails without any partial changes:
Path source = Paths.get("path/to/source/file.txt");
Path target = Paths.get("path/to/target/file.txt");
// Perform an atomic move
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
The StandardCopyOption.ATOMIC_MOVE
option guarantees that the move operation is atomic, preventing data corruption during the move process.
The Files.delete()
method allows you to delete a file or an empty directory:
Path path = Paths.get("path/to/file.txt");
// Delete the file
Files.delete(path);
If the file does not exist, a NoSuchFileException
will be thrown.
The Files.deleteIfExists()
method deletes the file if it exists and returns a boolean indicating whether the file was deleted:
Path path = Paths.get("path/to/file.txt");
// Delete the file if it exists
boolean deleted = Files.deleteIfExists(path);
This method does not throw an exception if the file does not exist.
The Files.isSameFile()
method allows you to determine if two paths locate the same file in the file system:
Path path1 = Paths.get("path/to/file1.txt");
Path path2 = Paths.get("path/to/file2.txt");
// Check if the paths refer to the same file
boolean isSame = Files.isSameFile(path1, path2);
It returns true
if the paths refer to the same file, and false
otherwise.
The Files.mismatch()
method compares the content of two files and returns the position of the first mismatched byte:
Path file1 = Paths.get("path/to/file1.txt");
Path file2 = Paths.get("path/to/file2.txt");
// Compare the content of the files
long mismatchPosition = Files.mismatch(file1, file2);
If the files have identical content, it returns -1. If the files have different sizes, it returns the size of the smaller file.
These are some of the key methods provided by the Files
class for copying, moving, deleting, and comparing files in Java. They offer convenient ways to perform common file operations without the need for manual I/O stream handling.
Java provides several methods in the java.nio.file.Files
class for reading from and writing to files. Let’s explore some commonly used methods and techniques.
The Files
class offers two convenient methods for reading the contents of a file: readAllLines()
and lines()
.
The Files.readAllLines()
method reads all the lines of a file into a List<String>
:
Path path = Paths.get("path/to/file.txt");
try {
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
This method is suitable when you need to process all the lines of a file at once. However, note that it reads the entire file into memory, so it may not be efficient for large files.
On the other hand, the Files.lines()
method returns a Stream<String>
that allows you to process the lines of a file lazily:
Path path = Paths.get("path/to/file.txt");
try (Stream<String> lines = Files.lines(path)) {
lines.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
This method is more memory-efficient as it reads the lines on-demand and does not load the entire file into memory at once. It is especially useful when you need to process large files or perform operations like filtering or mapping on the lines.
However, for more control over the reading process, you can use the Files.newBufferedReader()
method to create a BufferedReader
instance:
Path path = Paths.get("path/to/file.txt");
try (BufferedReader reader = Files.newBufferedReader(path)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
The BufferedReader
provides methods such as readLine()
to read the file line by line, allowing you to process the lines as needed.
The Files
class provides methods for writing content to files, such as write()
and newBufferedWriter()
.
The Files.write()
method allows you to write content to a file in a single operation:
Path path = Paths.get("path/to/file.txt");
String content = "Hello, World!";
try {
Files.write(path, content.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
e.printStackTrace();
}
This method writes the specified byte array to the file. If the file already exists, it will be overwritten by default.
Additionally, you can specify additional options using the StandardOpenOption
enum.
Path path = Paths.get("path/to/file.txt");
String content = "Appended content";
try {
Files.write(path, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND);
} catch (IOException e) {
e.printStackTrace();
}
The StandardOpenOption.APPEND
option specifies that the content should be appended to the end of the file instead of overwriting it.
Other useful options include:
StandardOpenOption.CREATE
: Creates a new file if it doesn’t exist.StandardOpenOption.CREATE_NEW
: Creates a new file, failing if it already exists.StandardOpenOption.TRUNCATE_EXISTING
: Truncates the file to zero bytes if it exists.These options give you more control over how the file is opened and written to.
However, you can also use the Files.newBufferedWriter()
method to create a BufferedWriter
instance:
Path path = Paths.get("path/to/file.txt");
try (BufferedWriter writer = Files.newBufferedWriter(path)) {
writer.write("Hello, World!");
writer.newLine();
writer.write("This is a new line.");
} catch (IOException e) {
e.printStackTrace();
}
The BufferedWriter
provides methods such as write()
and newLine()
to write content to the file, allowing you to write line by line or in chunks.
The java.nio.file
package provides classes and methods for working with file attributes. File attributes are metadata associated with a file or directory, such as size, modification time, permissions, and more. You can retrieve and modify file attributes using the NIO.2 API.
Java defines several attribute and view types that represent different sets of file attributes:
BasicFileAttributes
The BasicFileAttributes
interface provides basic file attributes that are common across different file systems. It includes attributes like:
FileTime creationTime()
: Returns the creation time of the file.FileTime lastModifiedTime()
: Returns the last modification time of the file.FileTime lastAccessTime()
: Returns the last access time of the file.long size()
: Returns the size of the file in bytes.boolean isRegularFile()
, isDirectory()
, isSymbolicLink()
: Checks the type of the file.DosFileAttributes
The DosFileAttributes
interface extends BasicFileAttributes
and provides additional attributes specific to DOS/Windows file systems. It includes attributes like:
boolean isReadOnly()
: Checks if the file is read-only.boolean isHidden()
: Checks if the file is hidden.boolean isArchive()
: Checks if the file is an archive.boolean isSystem()
: Checks if the file is a system file.PosixFileAttributes
The PosixFileAttributes
interface extends BasicFileAttributes
and provides additional attributes specific to POSIX-compliant file systems. It includes attributes like:
UserPrincipal owner()
: Returns the owner of the file.GroupPrincipal group()
: Returns the group owner of the file.Set<PosixFilePermission> permissions()
: Returns the file permissions as a set of PosixFilePermission
.To retrieve file attributes, you can use the Files.readAttributes()
method, specifying the attribute type you want to retrieve:
Path path = Paths.get("path/to/file.txt");
try {
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
System.out.println("Creation Time: " + attrs.creationTime());
System.out.println("Last Modified Time: " + attrs.lastModifiedTime());
System.out.println("Size: " + attrs.size());
} catch (IOException e) {
e.printStackTrace();
}
In this example, we retrieve the BasicFileAttributes
of the file and access its creation time, last modified time, and size.
Additionally, you can specify the LinkOption
to control how symbolic links are handled:
Path path = Paths.get("path/to/symlink.txt");
try {
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
boolean isSymLink = attrs.isSymbolicLink();
} catch (IOException e) {
e.printStackTrace();
}
By passing LinkOption.NOFOLLOW_LINKS
, the readAttributes()
method will not follow symbolic links and will instead return the attributes of the symbolic link itself.
To quickly check if a file is accessible for reading, writing, or executing, you can use the Files.isReadable()
, Files.isWritable()
, and Files.isExecutable()
methods:
Path path = Paths.get("path/to/file.txt");
boolean isReadable = Files.isReadable(path);
boolean isWritable = Files.isWritable(path);
boolean isExecutable = Files.isExecutable(path);
These methods return true
if the file is accessible for the respective operation, and false
otherwise.
To modify file attributes you can use the Files.setAttribute()
method, specifying the attribute name and value:
Path path = Paths.get("path/to/file.txt");
try {
Files.setAttribute(path, "dos:readonly", true);
Files.setAttribute(path, "dos:hidden", true);
} catch (IOException e) {
e.printStackTrace();
}
In this example, we set the readonly
and hidden
attributes of a file on a DOS/Windows file system.
Traversing a directory tree, also known as walking a directory tree, refers to the process of recursively visiting all the subdirectories and files within a given directory. The NIO.2 API, in particular, the Files
class, provides methods to simplify this process and allows you to perform actions on each visited file and directory.
The Files.walk()
method is a convenient way to traverse a directory tree. It returns a Stream<Path>
that represents the file tree rooted at the given starting directory:
Path startingDir = Paths.get("/path/to/directory");
try (Stream<Path> stream = Files.walk(startingDir)) {
stream.forEach(path -> {
// Process each path
System.out.println(path);
});
} catch (IOException e) {
e.printStackTrace();
}
The Files.walk()
method visits all the files and directories in the tree, including the starting directory itself. You can perform various operations on each path using the stream API, such as filtering, mapping, or collecting the paths.
You can control the depth of the traversal by passing a maximum depth value to the Files.walk()
method:
Path startingDir = Paths.get("/path/to/directory");
int maxDepth = 3;
try (Stream<Path> stream = Files.walk(startingDir, maxDepth)) {
// ...
} catch (IOException e) {
e.printStackTrace();
}
In this example, the traversal will go up to a maximum depth of 3 levels below the starting directory. A depth of 0 means only the starting directory itself is visited.
By default, Files.walk()
follows symbolic links. If you want to control this behavior, you can pass a FileVisitOption
to the method:
Path startingDir = Paths.get("/path/to/directory");
try (Stream<Path> stream = Files.walk(startingDir, FileVisitOption.FOLLOW_LINKS)) {
// ...
} catch (IOException e) {
e.printStackTrace();
}
The FileVisitOption.FOLLOW_LINKS
option specifies that symbolic links should be followed during the traversal.
However, when traversing a directory tree in Java, it’s important to be aware of circular paths caused by symbolic links. A circular path occurs when a symbolic link points to a directory that is an ancestor of the link, creating an infinite loop.
To avoid circular paths, you can use the FileVisitOption.FOLLOW_LINKS
option and implement your own cycle detection logic. Here’s a complete example:
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
public class DirectoryTraversal {
public static void main(String[] args) {
Path startingDir = Paths.get("/path/to/directory");
Set<Path> visitedPaths = new HashSet<>();
try {
Files.walkFileTree(
startingDir,
EnumSet.of(FileVisitOption.FOLLOW_LINKS),
Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(
Path file, BasicFileAttributes attrs) throws IOException {
if (visitedPaths.contains(file)) {
// Circular path detected, skip processing
return FileVisitResult.CONTINUE;
}
visitedPaths.add(file);
// Process the file
System.out.println(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(
Path dir, BasicFileAttributes attrs) throws IOException {
if (visitedPaths.contains(dir)) {
// Circular path detected, skip processing
return FileVisitResult.SKIP_SUBTREE;
}
visitedPaths.add(dir);
// Process the directory
System.out.println(dir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(
Path file, IOException exc) throws IOException {
System.err.println(
"Error visiting file: " + file + " - " + exc.getMessage()
);
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
This program demonstrates how to traverse a directory tree while avoiding circular paths caused by symbolic links. It begins by defining the starting directory specified by the startingDir
variable and uses the Files.walkFileTree
method to traverse the directory tree. This method is recommended because it visits all files and directories and can follow symbolic links when specified by the FileVisitOption.FOLLOW_LINKS
option. To prevent infinite loops caused by circular paths, the program maintains a set of visited paths (visitedPaths
). This set is used to keep track of all the directories and files that have already been visited during the traversal.
The core of the program is the implementation of a SimpleFileVisitor
, which overrides several methods to define custom behaviors for visiting files and directories. When the program finds a file or directory, it checks the visitedPaths
set to see if the path has already been visited. If the path is found in the set, this indicates a circular path, and the program skips further processing for that path. If the path is not in the set, it is added to the visitedPaths
set, and the path is processed (in this case, printed to the console). This ensures that each path is processed only once, effectively preventing infinite loops.
We talked about serialization before. It is the process of converting an object into a byte stream, which can be saved to a file or transmitted over a network. Deserialization is the reverse process, where the byte stream is converted back into an object. Let’s explore the key concepts and techniques related to serialization in Java.
Serialization allows you to persist the state of an object and recreate it later. This is useful for:
The Java Object Serialization API provides a standard mechanism for developers to handle this process.
To make a class serializable, it must implement the java.io.Serializable
interface. This is a marker interface (it has no methods) that tells the Java runtime that the class can be serialized:
public class Employee implements Serializable {
private String name;
private int age;
// Constructor, getters, and setters
}
If you try to serialize a class that doesn’t implement that interface, a java.io.NotSerializableException
(a subclass of IOException
) will be thrown at runtime.
The serialVersionUID
is a unique identifier for the serialized class. It’s used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization:
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
// ... rest of the class
}
If you don’t explicitly declare a serialVersionUID
, the Java runtime will generate one based on various aspects of your class. However, it’s recommended to declare one explicitly to maintain control over class versioning.
If you have fields in your class that you don’t want to be serialized (for example, sensitive data or derived data), you can mark them with the transient
keyword:
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private transient String password; // This won't be serialized
// ... rest of the class
}
In summary, to ensure a class is serializable:
Serializable
.null
value at the time of serialization.After that, you can use ObjectOutputStream
and ObjectInputStream
for the serialization/deserialization process as shown in a previous section. Here’s a complete example demonstrating this process:
import java.io.*;
public class SerializationDemo {
public static void main(String[] args) {
Person person = new Person("John Doe", 30);
// Serialization
try (ObjectOutputStream out =
new ObjectOutputStream(new FileOutputStream("person.ser"))) {
out.writeObject(person);
System.out.println("Person object serialized");
} catch (IOException e) {
e.printStackTrace();
}
// Deserialization
try (ObjectInputStream in =
new ObjectInputStream(new FileInputStream("person.ser"))) {
Person deserializedPerson = (Person) in.readObject();
System.out.println("Person object deserialized");
System.out.println("Name: " + deserializedPerson.getName());
System.out.println("Age: " + deserializedPerson.getAge());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getters
public String getName() { return name; }
public int getAge() { return age; }
// ... rest of the class
}
In this example, we serialize a Person
object to a file named person.ser
and then deserialize it back into a Person
object.
It’s important to note that when deserializing an object, the constructor and any initialization block are not executed. However, the default initialization of instance variables still occurs.
Also, unlike classes, records are automatically serializable. They implicitly implement the Serializable
interface, so you don’t need to explicitly declare it:
public record PersonRecord(String name, int age) /** implements Serializable */ {
// No need to declare serialVersionUID, as it's automatically generated
}
Remember, records provide a compact way to declare classes that are primarily used to store data, and their built-in serializability makes them convenient for use in scenarios where object serialization is required.
Here are some tables to help you review and understand the I/O stream classes and related concepts:
I/O Stream Classes Summary
Stream Type | Byte Streams | Character Streams |
---|---|---|
Input | Abstract Classes: InputStream Concrete Classes: FileInputStream , BufferedInputStream , ObjectInputStream |
Abstract Classes: Reader Concrete Classes: FileReader , BufferedReader |
Output | Abstract Classes: OutputStream Concrete Classes: FileOutputStream , BufferedOutputStream , ObjectOutputStream , PrintStream |
Abstract Classes: Writer Concrete Classes: FileWriter , BufferedWriter , PrintWriter |
File and Path Operations Comparison
Operation | File Class | NIO.2 (Path and Files) |
---|---|---|
Create File | file.createNewFile() |
Files.createFile(path) |
Delete File | file.delete() |
Files.delete(path) |
Check Existence | file.exists() |
Files.exists(path) |
Get Absolute Path | file.getAbsolutePath() |
path.toAbsolutePath() |
Check if Directory | file.isDirectory() |
Files.isDirectory(path) |
Check if File | file.isFile() |
Files.isRegularFile(path) |
List Directory Contents | file.list() , file.listFiles() |
Files.list(path) |
Create Directory | file.mkdir() |
Files.createDirectory(path) |
Create Directories | file.mkdirs() |
Files.createDirectories(path) |
Rename File | file.renameTo(dest) |
Files.move(source, target) |
Files Class Methods Summary
Method | Description |
---|---|
copy() |
Copies a file to a target file |
createDirectories() |
Creates a directory and any necessary parent directories |
delete() |
Deletes a file or empty directory |
exists() |
Checks file existence |
isDirectory() |
Checks if the path is a directory |
isRegularFile() |
Checks if the path is a regular file |
move() |
Moves or renames a file |
size() |
Returns the size of a file |
readAllBytes() |
Reads all bytes from a file |
readAllLines() |
Reads all lines from a file |
walk() |
Returns a Stream of file tree structure |
write() |
Writes bytes or lines to a file |
Common File Attributes
Attribute | BasicFileAttributes |
DosFileAttributes |
PosixFileAttributes |
---|---|---|---|
Creation Time | ✓ | ✓ | ✓ |
Last Modified Time | ✓ | ✓ | ✓ |
Last Access Time | ✓ | ✓ | ✓ |
Size | ✓ | ✓ | ✓ |
Is Directory | ✓ | ✓ | ✓ |
Is Regular File | ✓ | ✓ | ✓ |
Is Symbolic Link | ✓ | ✓ | ✓ |
Is Hidden | ✓ | ||
Is Read-only | ✓ | ||
Owner | ✓ | ||
Group | ✓ | ||
Permissions | ✓ |
StandardOpenOption Values
Option | Description |
---|---|
APPEND |
Append to the end of the file if it exists |
CREATE |
Create a new file if it doesn’t exist |
CREATE_NEW |
Create a new file, failing if it already exists |
DELETE_ON_CLOSE |
Delete the file when the stream is closed |
DSYNC |
Synchronize only the file’s content with the underlying storage device |
READ |
Open for read access |
SPARSE |
Hint that a newly created file will be sparse |
SYNC |
Synchronize every update to the file’s content and metadata with the underlying storage device |
TRUNCATE_EXISTING |
Truncate the file to zero bytes if it exists |
WRITE |
Open for write access |
StandardCopyOption Values
Option | Description |
---|---|
REPLACE_EXISTING |
Replace the target file if it exists |
COPY_ATTRIBUTES |
Copy file attributes to the target file |
ATOMIC_MOVE |
Move the file as an atomic file system operation |
java.nio.file.Path
: Represents file and directory paths in a more flexible way (compared to the java.io.File
class).java.nio.file.Files
: Utility class for file operations.InputStream
and OutputStream
.Reader
and Writer
.FileInputStream
and FileOutputStream
: Read and write bytes from/to files.FileReader
and FileWriter
: Read and write characters from/to files.BufferedReader
and BufferedWriter
: Add buffering capability to character streams.ObjectInputStream
and ObjectOutputStream
: Read and write serialized objects.PrintStream
and PrintWriter
: Write formatted data to byte and character streams respectively.The try-with-resources
statement should be used with I/O classes to ensure proper resource management.
Most I/O operations can throw IOException
or its subclasses, which need to be handled.
System.in
: Standard input stream (typically keyboard input).System.out
: Standard output stream (typically console output).System.err
: Standard error stream (typically error console output).When working with character streams, be aware of character encoding. The default is usually the system’s default encoding.
Buffered streams (BufferedReader
, BufferedWriter
, etc.) can improve performance by reducing the number of I/O operations.
The Files
class provides methods for various file operations like copying, moving, deleting, and comparing files.
Files.copy(source, target, CopyOption...)
to copy files.StandardCopyOption.REPLACE_EXISTING
can be used to overwrite existing files.Files.move(source, target, CopyOption...)
to move or rename files.StandardCopyOption.ATOMIC_MOVE
ensures the move operation is performed as a single indivisible operation.Files.delete(path)
deletes a file or empty directory.Files.deleteIfExists(path)
deletes a file if it exists and returns a boolean
indicating success.Files.isSameFile(path1, path2)
checks if two paths refer to the same file.Files.mismatch(file1, file2)
compares the content of two files and returns the position of the first mismatched byte.Files.readAllLines(path)
reads all lines of a file into a List<String>
.Files.lines(path)
returns a Stream<String>
for lazy processing of file lines.Files.newBufferedReader(path)
creates a BufferedReader
for more control over reading.Files.write(path, bytes, OpenOption...)
writes content to a file in a single operation.Files.newBufferedWriter(path)
creates a BufferedWriter
for writing line by line or in chunks.StandardOpenOption
enum to specify how the file should be opened or written to.BasicFileAttributes
, DosFileAttributes
, and PosixFileAttributes
interfaces provide access to various file attributes.Files.readAttributes(path, Class<A>, LinkOption...)
to retrieve file attributes.Files.setAttribute(path, attribute, value)
modifies file attributes.Files.walk(path, options)
returns a Stream<Path>
for traversing a directory tree.FileVisitOption.FOLLOW_LINKS
to follow symbolic links during traversal.Serialization allows objects to be converted to a byte stream, which can be saved or transmitted. Classes must implement the Serializable
interface to be serializable.
Classes must implement the Serializable
interface to be serializable.
Use serialVersionUID
for version control of serialized classes.
Mark fields as transient
to exclude them from serialization.
Use ObjectOutputStream
for serialization and ObjectInputStream
for deserialization.
Serializable
.1. What is the result of the following code snippet?
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathExample {
public static void main(String[] args) {
Path basePath = Paths.get("/home/user");
Path relativePath = Paths.get("documents/notes.txt");
Path resultPath = basePath.resolve(relativePath);
System.out.println(resultPath);
}
}
A) /home/user
B) /home/user/documents
C) /documents/notes.txt
D) /home/user/documents/notes.txt
2. What is the result of the following code snippet?
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathExample {
public static void main(String[] args) {
Path path = Paths.get("/home/user/../documents/./notes.txt");
Path normalizedPath = path.normalize();
System.out.println(normalizedPath);
}
}
A) /home/user/../documents/./notes.txt
B) /home/user/documents/notes.txt
C) /home/documents/notes.txt
D) /documents/notes.txt
3. Which of the following classes is used for reading character streams in Java?
A) FileOutputStream
B) FileReader
C) BufferedOutputStream
D) ObjectInputStream
4. Which of the following code snippets correctly copies a file using the Files
class, ensuring that an existing target file is overwritten?
A)
Path source = Paths.get("source.txt");
Path target = Paths.get("target.txt");
Files.copy(source, target, StandardCopyOption.ATOMIC_MOVE);
B)
Path source = Paths.get("source.txt");
Path target = Paths.get("target.txt");
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
C)
Path source = Paths.get("source.txt");
Path target = Paths.get("target.txt");
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
D)
Path source = Paths.get("source.txt");
Path target = Paths.get("target.txt");
Files.copy(source, target, StandardCopyOption.APPEND);
5. Which of the following code snippets correctly reads all lines from a file into a List<String>
using the java.nio.file
API?
A)
Path path = Paths.get("file.txt");
List<String> lines = Files.readAllBytes(path);
B)
Path path = Paths.get("file.txt");
List<String> lines = Files.readString(path);
C)
Path path = Paths.get("file.txt");
List<String> lines = Files.lines(path);
D)
Path path = Paths.get("file.txt");
List<String> lines = Files.readAllLines(path);
6. Which of the following code snippets correctly writes a List<String>
to a file using the Files
class in Java?
A)
Path path = Paths.get("output.txt");
List<String> lines = Arrays.asList("line1", "line2", "line3");
Files.write(path, lines);
B)
Path path = Paths.get("output.txt");
List<String> lines = Arrays.asList("line1", "line2", "line3");
Files.writeString(path, lines);
C)
Path path = Paths.get("output.txt");
List<String> lines = Arrays.asList("line1", "line2", "line3");
Files.writeLines(path, lines);
D)
Path path = Paths.get("output.txt");
List<String> lines = Arrays.asList("line1", "line2", "line3");
Files.write(path, lines, StandardOpenOption.READ);
7. Which of the following methods from the BasicFileAttributes
class retrieves the creation time of a file?
A)
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
attrs.lastModifiedTime();
B)
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
attrs.creationTime();
C)
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
attrs.lastAccessTime();
D)
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
attrs.size();
8. Which of the following code snippets correctly traverses a directory tree using the Files.walkFileTree
method in Java?
A)
Path start = Paths.get("start_directory");
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
return FileVisitResult.SKIP_SUBTREE;
}
});
B)
Path start = Paths.get("start_directory");
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
throw new IOException("Error visiting file");
}
});
C)
Path start = Paths.get("start_directory");
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println("Visited file: " + file);
return FileVisitResult.TERMINATE;
}
});
D)
Path start = Paths.get("start_directory");
Files.walkFileTree(start, EnumSet.noneOf(FileVisitOption.class), Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println("Visited file: " + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
});
9. Which of the following code snippets correctly serializes an object to a file?
A)
class Animal implements Serializable {
private static final long serialVersionUID = 1L;
private String species;
private int age;
public Animal(String species, int age) {
this.species = species;
this.age = age;
}
}
Animal animal = new Animal("Lion", 5);
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("animal.ser"))) {
ois.writeObject(animal);
} catch (IOException e) {
e.printStackTrace();
}
B)
class Animal implements Serializable {
private static final long serialVersionUID = 1L;
private String species;
private int age;
public Animal(String species, int age) {
this.species = species;
this.age = age;
}
}
Animal animal = new Animal("Lion", 5);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("animal.ser"))) {
oos.writeObject(animal);
} catch (IOException e) {
e.printStackTrace();
}
C)
class Animal {
private String species;
private int age;
public Animal(String species, int age) {
this.species = species;
this.age = age;
}
}
Animal animal = new Animal("Lion", 5);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("animal.ser"))) {
oos.writeObject(animal);
} catch (IOException e) {
e.printStackTrace();
}
D)
class Animal implements Serializable {
private static final long serialVersionUID = 1L;
private String species;
private int age;
public Animal(String species, int age) {
this.species = species;
this.age = age;
}
}
Animal animal = new Animal("Lion", 5);
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("animal.ser"))) {
bos.write(animal);
} catch (IOException e) {
e.printStackTrace();
}
Do you like what you read? Would you consider?
Do you have a problem or something to say?