Summary of Java IO principles
Java IO Overview
File related classes
Files are a common source or destination of data in Java applications.
In Java applications, files are a common data source or medium for storing data.
Read files through Java IO
-
FileInputStream
Binary file input stream
You can read one byte at a time from the beginning of the file to the end of the file
-
FileReader
Text file input stream
You can read one character at a time from the beginning of the file to the end of the file
You also don’t have to read the entire file at once; instead, you can read the bytes and characters in the file sequentially.
-
RandomAccessFile
Read parts of a file by skipping
Write files through Java IO
FileOutputStream
FileWriter
Random access to files via Java IO
-
RandomAccess
Random access doesn’t mean that you can read and write in truly random places, it just means that you can skip parts of a file and write simultaneously without requiring a specific order of access.
This allows RandomAccessFile to overwrite parts of a file, append content to the end of it, or delete content from it, or of course read from anywhere in the file.
The Stream (flow)
A Java IO stream is a data stream that can either be read from or written to. Flows are often associated with data sources, data flow destinations, such as files, networks, and so on.
The characteristics of the flow
-
Streams, unlike arrays, cannot read or write data through indexes (subscripts)
In a stream, you can’t move data back and forth like an array, either, unless you use RandomAccessFile to process the file. A stream is simply a continuous stream of data.
-
Java IO flows are usually byte – or character-based.
-
Byte streams are usually named “stream”, such as InputStream and OutputStream
With the exception of DataInputStream and DataOutputStream, which can also read and write int, long, float, and double values, streams can read or write only one raw byte ata time.
-
Character streams are often named “Reader” or “Writer”
-
Byte stream
InputStream
If you are writing a component that needs to read input from a stream, try to make our component depend on an
InputStream
, rather than any of it’s subclasses (e.g.FileInputStream
).Doing so makes your code able to work with all types of input streams, instead of only the concrete subclass.
The java.io.InputStream class is the base class for all Java IO input streams. If you’re developing a component that reads data from a stream, try InputStream instead of any of its subclasses (such as FileInputStream).
Doing so allows your code to be compatible with any type of input stream rather than a certain type. (Better compatibility)
read()
Java Doc: Reads the next byte of data from the input stream. The value byte is returned as an
int
in the range0
to255
.If no byte is available because the end of the stream has been reached, the value
-1
is returned.This method blocks until input data is available, the end of the stream is detected,or an exception is thrown.
Read data: Data is usually read using the read() method in InputStream.
The read() method returns an integer representing the contents of the bytes read(0 to 255). The read() method returns -1 when there is no more data to read at the end of the stream
try (InputStream in = new FileInputStream(
"D:\\gitRepository\\organized-learning\\deep-in-java\\stage-8\\helloworld.txt");
OutputStream out = new FileOutputStream(
"D:\\gitRepository\\organized-learning\\deep-in-java\\stage-8\\output.txt")) {
int data;
while((data = in.read()) ! = -1) { out.write(data); }}Copy the code
FileInputStream
Focus on constructors
public FileInputStream(String name) throws FileNotFoundException {
this(name ! =null ? new File(name) : null);
}
public FileInputStream(File file) throws FileNotFoundException {... }Copy the code
If the file itself does not exist, FileInputStream will be created with an error
File file = new File("c:\\data\\input-text.txt");
InputStream input = new FileInputStream(file);
// Or the incoming path
InputStream inputStream=new FileInputStream("c:\\data\\input-text.txt");
Copy the code
BufferedInputStream
BufferedInputStream provides a buffer for the input stream, which can speed up a lot of IO.
Instead of reading one byte at a time from the network or disk, you can read a chunk of data at a time. Especially when accessing a lot of disk data, caching often makes I/O much faster.
InputStream input = new BufferedInputStream(new FileInputStream("c:\\data\\input-file.txt"));
public synchronized int read(a) throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
Copy the code
The byte[] array is filled up at one time
OutputStream
Java.io.OutputStream is the base class for all output streams in Java IO.
write(int)
Used to write a single byte to the output stream.
The write(int) method of OutputStream writes as an argument an int variable containing data to be written.
Only the first byte of type int is written; the remaining bits are ignored. (Write the lower 8 bits, ignore the higher 24 bits).
write(byte[])
-
Write (byte[]) writes all the data in the byte array to the output stream.
-
Write (byte[], int offset, int length) Writes the length of byte data starting at offset to the output stream.
flush()
Flush all data written to OutputStream into the appropriate target medium.
For example, if the output stream is a FileOutputStream, the data written to it may not actually be written to disk. Even if all data is written to a FileOutputStream, it may remain in an in-memory buffer.
By calling flush(), you flush the data in the buffer to disk (or network, or any other form of target medium).
It does not guarantee that they are actually written to a physical device such as a disk drive.
FileOutputStream
File content overwrite && append
OutputStream output = new FileOutputStream("c:\\data\\output-text.txt".true);//appends to file
// Overwrite by default
OutputStream output = new FileOutputStream("c:\\data\\output-text.txt".false); //overwrites file
Copy the code
BufferedOutputStream
Similar to BufferedInputStream, BufferedOutputStream can provide buffers for output streams.
OutputStream output = new BufferedOutputStream(new FileOutputStream("c:\\data\\output-file.txt"));
Characters of the flow
Reader
Reader is the base class for character input streams and has the following subclasses.
BufferedReader
InputStreamReader
FileReader
FilterReader
PushbackReader
PipedReader
StringReader
CharArrayReader
A Reader is similar to an InputStream except that it is character based rather than byte based. In other words, Reader is used to read text, and InputStream is used to read raw bytes.
Reader#read
The InputStream#read() method returns one byte, meaning that the return value is in the range of 0 to 255 (-1 when the end of the stream is reached);
The Reader#read() method returns a character in the range 0 to 65535 (-1 is also returned when the end of the stream is reached).
This does not mean that readers only read two bytes at a time from the data source, but one or more bytes at a time, depending on the encoding of the text.
InputStreamReader
The original: A Java Reader can be combined with an InputStream. If you have an InputStream and want to read characters from it, you can wrap it in an InputStreamReader.
A Reader can be combined with an InputStream. If you have an InputStream InputStream and you want to read characters from it, you can wrap that InputStream in an InputStreamReader.
Reader reader = new InputStreamReader(inputStream);
Copy the code
Writer
Writer is the parent of all character output streams.
The subclass:
BufferedWriter
CharArrayWriter
FilterWriter
OutputStreamWriter
FileWriter
PipedWriter
PrintWriter
StringWriter
write(int c)
The write(int C) method of Writer writes the lower 16 bits of the passed parameter to Writer and ignores the higher 16 bits.
public void write(int c) throws IOException {
synchronized (lock) {
if (writeBuffer == null){
writeBuffer = new char[WRITE_BUFFER_SIZE];
}
writeBuffer[0] = (char) c;
write(writeBuffer, 0.1); }}Copy the code
OutputStreamWriter
tips: A Java Writer
can be combined with an OutputStream
just like Readers
and InputStream
‘s. Wrap the OutputStream
in an OutputStreamWriter
and all characters written to the Writer
are passed on to the OutputStream
.
such as :
Writer writer = new OutputStreamWriter(outputStream);
Similar to Reader and InputStream, a Writer can be combined with an OutputStream. Wrap the OutputStream in the OutputStreamWriter. All characters written to the OutputStreamWriter will be passed to the OutputStream. (Character stream -> byte stream).
Combining Streams
The original: You can combine streams into chains to achieve more advanced input and output operations. For instance, reading every byte one at a time from a file is slow. It is faster to read a larger block of data from the disk and then iterate through that block byte for byte afterwards.
We can integrate streams to enable more advanced input and output operations.
For example, reading a byte at a time is slow (each time triggering a disk access, network activity, or some other relatively expensive operation), so you can read a chunk of data at a time from disk, and then get bytes from the read chunk.
InputStream input = new BufferedInputStream(
new FileInputStream("d:\\data\\input-file.txt"));
Copy the code
Buffering can also be applied to OutputStream. We can also write large chunks of data to disk (or stream) in batches, which is implemented by BufferedOutputStream.
Serialization and deserialization
Classes corresponding to serialized and deserialized objects must implement the Serializable interface.
Core class: ObjectInputStream && ObjectOutputStream
-
ObjectInputStream allows you to read Java objects from an input stream without having to read one byte at a time. (deserialization)
You can wrap InputStream in ObjectInputStream and then read objects from it.
-
ObjectOutputStream allows you to write objects to the output stream without having to write one byte at a time. (serialization)
You can wrap an OutputStream in an ObjectOutputStream, and then write objects to that OutputStream.
Traditional Java file systems
File object API –java.io.File
-
Check whether the file exists
-
Get file length
-
Delete the file
-
Check whether the path corresponds to a file or directory
- True indicates that the path exists and is a directory
- If false is returned, it is possible that the path does not exist instead of pointing to the file.
-
Read a list of files in a directory
String printInfoPrefix = "[" + TEST_SOURCE_FILE_PATH + "] Path file";
File file = new File(TEST_SOURCE_FILE_PATH);
boolean exists = file.exists();
String existInfo = exists ? "There" : "Doesn't exist.";
// [D:\gitRepository\organized-learning\deep-in-java\stage-8\ helloWorld.txt
System.out.println(printInfoPrefix + existInfo);
System.out.println(printInfoPrefix + "Length :[" + file.length() + "] bytes");
// true indicates that the path exists and is a directory,
// If false is returned it is possible that the path does not exist instead of pointing to the file.
String pathInfo = file.isDirectory() ? "Directory" : "File";
System.out.println("[" + TEST_SOURCE_FILE_PATH + "] The path corresponds to one" + pathInfo);
// Get the path of the previous layer
File parentFile = file.getParentFile();
// Read the file or directory directly corresponding to the path
String[] childFileArr = parentFile.list();
for (String childFile : childFileArr) {
// Iterate over the print
System.out.println(childFile);
}
Copy the code
[D:\gitRepository\organized-learning\deep-in-java\stage-8\ helloWorld.txt] The file corresponding to the path exists [D:\gitRepository\organized-learning\deep-in-java\stage-8\helloworld.txt] The file corresponding to the path is [12612] bytes [D:\gitRepository\organized-learning\deep-in-java\stage-8\helloworld.txt] The path corresponds to a file
file.test file.txt helloworld.txt output.txt outputchar.txt pom.xml stage-8.iml stage8-lesson1
File
The shortcomings of class
Prior to the Java SE 7 release, the
java.io.File
class was the mechanism used for file I/O, but it had several drawbacks.
- Many methods didn’t throw exceptions when they failed, so it was impossible to obtain a useful error message. For example, if a file deletion failed, the program would receive a “delete fail” but wouldn’t know if it was because the file didn’t exist, the user didn’t have permissions, or there was some other problem.
- The
rename
method didn’t work consistently across platforms.- There was no real support for symbolic links.
- More support for metadata was desired, such as file permissions, file owner, and other security attributes.
- Accessing file metadata was inefficient.
- Many of the
File
methods didn’t scale. Requesting a large directory listing over a server could result in a hang. Large directories could also cause memory resource problems, resulting in a denial of service.- It was not possible to write reliable code that could recursively walk a file tree and respond appropriately if there were circular symbolic links.
File system API –java.io.FileSystem
java.io.WinNTFileSystem
File descriptor –java.io.FileDescriptor
public static void main(String[] args) throws Exception {
displayFileDescriptor(FileDescriptor.in);
displayFileDescriptor(FileDescriptor.out);
displayFileDescriptor(FileDescriptor.err);
}
private static void displayFileDescriptor(FileDescriptor fileDescriptor)
throws NoSuchFieldException, IllegalAccessException {
Integer fd = getFieldValue(fileDescriptor, "fd");
Long handle = getFieldValue(fileDescriptor, "handle");
Boolean closed = getFieldValue(fileDescriptor, "closed");
System.out.printf("FileDescriptor=[ fd: %d , handle : %d , closed: %s]\n",fd,handle,closed);
}
private static <T> T getFieldValue(FileDescriptor fileDescriptor, String fieldName)
throws NoSuchFieldException, IllegalAccessException {
Field field = FileDescriptor.class.getDeclaredField(fieldName);
field.setAccessible(true);
return (T)field.get(fileDescriptor);
}
Copy the code
File input/output streams
FileInputStream
FileOutputStream
File filter
java.io.FileFilter
** * Calculates the space occupied by a directory **@author ajin
*/
public class DirectorySpaceDemo {
/** * root directory ** /
private final File rootDirectory;
/ * * * {@link Predicate}
* */
private final Predicate<File> filter;
public DirectorySpaceDemo(File rootDirectory, FileFilter... fileFilters) {
this.rootDirectory = rootDirectory;
this.filter = new FilePredicate(fileFilters);
}
private class FilePredicate implements Predicate<File> {
private final FileFilter[] fileFilters;
public FilePredicate(FileFilter[] fileFilters) {
this.fileFilters = fileFilters;
}
@Override
public boolean test(File file) {
for (FileFilter fileFilter : fileFilters) {
if(! fileFilter.accept(file)) {return false; }}return true; }}private interface FilePredicateAdapter extends Predicate<File>, FileFilter {
@Override
default boolean accept(File pathname) {
returntest(pathname); }}/** * Get path size for example: d:\\test occupies memory 128KB */
public long getSpace(a) {
if (rootDirectory.isFile()) {
return rootDirectory.length();
} else if (rootDirectory.isDirectory()) {
File[] subFiles = rootDirectory.listFiles();
long space = 0L;
if (null == subFiles) {
return space;
}
// Add files in the current directory
space += Stream.of(subFiles).filter(File::isFile).filter(filter).map(File::length).reduce(Long::sum).orElse(
0L);
// Recurse the current subdirectory
space += Stream.of(subFiles).filter(File::isDirectory).filter(filter).map(DirectorySpaceDemo::new).map(
DirectorySpaceDemo::getSpace).reduce(Long::sum).orElse(0L);
return space;
}
return -1L;
}
public static long calculateSpace(File file) {
Objects.requireNonNull(file);
if (file.isFile()) {
return file.length();
} else if (file.isDirectory()) {
long filesSpace = 0L;
File[] subFiles = file.listFiles();
if (null == subFiles) {
return filesSpace;
}
for (File subFile : subFiles) {
filesSpace += calculateSpace(subFile);
}
return filesSpace;
}
return -1L;
}
public static void main(String[] args) {
System.out.println(
new DirectorySpaceDemo(new File(System.getProperty("user.home")), file -> file.getName().endsWith(".log"))
.getSpace() / 1024); }}Copy the code
java.io.FileNameFilter
@FunctionalInterface
public interface FilenameFilter {
boolean accept(File dir, String name);
}
Copy the code
Functional interfaces, nothing special.
The resources
-
Lesson: Basic I/O
-
Java IO Tutorial
-
Java IO tutorial
-
Section 1 Java I/O flows
-
Java File system
-
Legacy File I/O Code