IO stream
File class
File is an abstract representation of a file or directory path. Creating a File object only creates a path object in memory, and does not mean that files or directories on disk must exist.
constructor
File file1 = new File("d:/lesson/java2601/a.txt");
File file2 = new File("d:/lesson/java2601", "a.txt");
File parent = new File("d:/lesson/java2601");
File file3 = new File(parent, "a.txt");
You can use escaped backslashes or forward slashes in Windows path strings.
create files and directories
create a file
File file = new File("d:/lesson/java2601/test/a.txt");
try {
boolean created = file.createNewFile();
System.out.println(created ? "创建成功" : "文件已存在");
} catch (IOException e) {
e.printStackTrace();
}
createNewFile() will not automatically create parent directories that do not exist.
File extensions are often used to help operating systems and applications identify file types, but the extensions themselves do not change file content.
create a directory
mkdir() can only create a single-level directory when the parent directory already exists.
File directory = new File("d:/lesson/java2601/test/a");
directory.mkdir();
mkdirs() can simultaneously create missing multi-level parent directories.
File directory = new File("d:/lesson/java2601/test/a/b/c");
directory.mkdirs();
create temporary files
File directory = new File("d:/lesson/java2601/test");
try {
File tempFile = File.createTempFile("temp", ".txt", directory);
System.out.println(tempFile);
} catch (IOException e) {
e.printStackTrace();
}
Temporary file name prefixes require at least three characters.
common methods
| Method | Action |
|---|---|
exists() | Determine whether there is |
isFile() | Determine whether it is an ordinary document |
isDirectory() | Determine whether it is a catalog |
delete() | Delete files or empty directories |
getName() | Get file or directory name |
getParentFile() | Gets the File object corresponding to the parent path |
getPath() | Path form used when obtaining construction objects |
getAbsolutePath() | Get the absolute path string |
length() | Gets the length of ordinary files in bytes |
listFiles() | Get the direct sub-item in the catalog |
listFiles() may return null when the path is not a directory, IO errors occur, or no access rights are available. It should be checked before use.
File directory = new File("d:/lesson/java2601");
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
System.out.println(file);
}
}
File.equals() compares whether abstract path names are equal, and does not automatically normalize all relative paths, symbolic links, or paths containing ... When you need to compare actual specification paths, you can use getCanonicalFile() and process IOException.
recursive
Recursion is when a method calls itself directly or indirectly. Recursion must have clear termination conditions and ensure that each call approaches the termination conditions gradually.
factorial
public static long factorial(int number) {
if (number < 0) {
throw new IllegalArgumentException("阶乘参数不能为负数");
}
if (number <= 1) {
return 1;
}
return number * factorial(number - 1);
}Fibonacci sequence
public static long fibonacci(int number) {
if (number < 0) {
throw new IllegalArgumentException("参数不能为负数");
}
if (number <= 1) {
return number;
}
return fibonacci(number - 1) + fibonacci(number - 2);
}This direct recursion will repeatedly compute a large number of subproblems and is only suitable for demonstrating recursion. Use circular loops or cached results when processing large data.
Recursively find files
public static void findPdfFiles(File path, List<File> result) {
if (path == null || !path.exists()) {
return;
}
if (path.isFile()) {
if (path.getName().toLowerCase().endsWith(".pdf")) {
result.add(path);
}
return;
}
File[] children = path.listFiles();
if (children == null) {
return;
}
for (File child : children) {
findPdfFiles(child, result);
}
}Recursively delete directory
public static boolean deleteRecursively(File path) {
if (path == null || !path.exists()) {
return true;
}
if (path.isDirectory()) {
File[] children = path.listFiles();
if (children == null) {
return false;
}
for (File child : children) {
if (!deleteRecursively(child)) {
return false;
}
}
}
return path.delete();
}When recursively processing directories, you should also be aware of access permissions, symbolic links, and possible stack overflows caused by excessively deep directories. Modern Java projects can also use the File Tree API provided by java.nio.file.Files.
IO Flow Overview
IO represents input and output. Using program memory as a reference:
- The input stream reads external data into the program.
- The output stream writes program data to external devices.
Classification by data unit
- The
InputStreamandOutputStreamsystems process by byte and are suitable for pictures, audio, video and other binary data. - The
ReaderandWritersystems process characters and are suitable for text data.
Character stream requires decoding bytes into characters according to character encoding, or encoding characters into bytes.
Classified by connection target
- Node flows directly connect data sources or targets such as files, memory, and networks, such as
FileInputStream. - Processing streams wrap other streams and add functions such as buffering, data type reading and writing, and object serialization, such as
BufferedInputStreamandObjectOutputStream.
Processing flows are not necessarily “more advanced” or “faster” than node flows and should be used in combination based on the required functionality.
using step
-
Determine whether input or output.
-
Determine whether to process bytes or characters.
-
Select the node flow corresponding to the actual data source.
-
Increase buffering, transcoding, or object processing streams as needed.
-
Use
try-with-resourcesto shut down resources in a timely manner.
FileInputStream and FileOutputStream
read bytes
The commonly used read() methods for FileInputStream are as follows:
read(): Read one byte, return0to255, and return-1at the end.read(byte[] buffer): Read the array length at most by bytes, and return the actual read number.read(byte[] buffer, int offset, int length): Read data to the specified range of the array.
When reading in batches, only the actual length read this time can be processed.
try (FileInputStream input = new FileInputStream(
"d:/lesson/java2601/test/a.txt")) {
byte[] buffer = new byte[1024];
int length;
while ((length = input.read(buffer)) != -1) {
for (int i = 0; i < length; i++) {
System.out.println(buffer[i]);
}
}
} catch (IOException e) {
e.printStackTrace();
}When outputting text content, you should not force any bytes to characters one by one. You should use a character stream and specify correct encoding.
write byte
FileOutputStream overwrites the original file by default, and when the second parameter of the constructor is true, it means appending.
try (FileOutputStream output = new FileOutputStream(
"d:/lesson/java2601/test/a.txt", true)) {
byte[] data = "ABCDEFG".getBytes(StandardCharsets.UTF_8);
output.write(data);
} catch (IOException e) {
e.printStackTrace();
}
copy files
public static void copyFile(String source, String target) {
try (FileInputStream input = new FileInputStream(source);
FileOutputStream output = new FileOutputStream(target)) {
byte[] buffer = new byte[8192];
int length;
while ((length = input.read(buffer)) != -1) {
output.write(buffer, 0, length);
}
} catch (IOException e) {
e.printStackTrace();
}
}Copying binary files must use a byte stream.
BufferedInputStream and BufferedOutputStream
Buffered byte streams maintain buffers in memory and reduce the number of underlying read and write calls.
public static void copyFileWithBuffer(String source, String target) {
try (BufferedInputStream input = new BufferedInputStream(
new FileInputStream(source));
BufferedOutputStream output = new BufferedOutputStream(
new FileOutputStream(target))) {
byte[] buffer = new byte[8192];
int length;
while ((length = input.read(buffer)) != -1) {
output.write(buffer, 0, length);
}
} catch (IOException e) {
e.printStackTrace();
}
}The buffer is flushed first when the output stream is turned off. flush() can be called when you need to immediately commit data to the underlying target while the stream remains open.
FileReader and FileWriter
FileReader and FileWriter are used to read and write text, but traditional constructors usually use the platform default character set. When file coding is fixed, it is recommended to use InputStreamReader and OutputStreamWriter to explicitly specify the character set.
try (FileReader reader = new FileReader(
"d:/lesson/java2601/test/a.txt")) {
char[] buffer = new char[1024];
int length;
while ((length = reader.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, length));
}
} catch (IOException e) {
e.printStackTrace();
}FileReader itself is not BufferedReader, and it cannot be described as naturally having line buffering functions.
try (FileWriter writer = new FileWriter(
"d:/lesson/java2601/test/a.txt")) {
writer.write("1234567890");
} catch (IOException e) {
e.printStackTrace();
}
BufferedReader and BufferedWriter
BufferedReader provides readLine(), which can read text per line. The returned string does not contain a line terminator and returns null when it reaches the end of the file.
try (BufferedReader reader = new BufferedReader(
new FileReader("d:/lesson/java2601/test/a.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}BufferedWriter provides line separators corresponding to the newLine() writing platform.
try (BufferedWriter writer = new BufferedWriter(
new FileWriter("d:/lesson/java2601/test/a.txt"))) {
writer.write("第一行");
writer.newLine();
writer.write("第二行");
} catch (IOException e) {
e.printStackTrace();
}InputStreamReader and OutputStreamWriter
These two conversion streams connect the byte stream and the character stream and are responsible for character encoding conversions.
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("d:/lesson/java2601/test/a.txt"),
StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("d:/lesson/java2601/test/a.txt"),
StandardCharsets.UTF_8))) {
writer.write("使用 UTF-8 写入文本");
} catch (IOException e) {
e.printStackTrace();
}DataInputStream and DataOutputStream
The data stream can read and write data in binary format of Java’s basic type.
try (DataOutputStream output = new DataOutputStream(
new FileOutputStream("d:/lesson/java2601/test/data.bin"))) {
output.writeInt(1024);
output.writeDouble(100.0);
output.writeUTF("Java");
} catch (IOException e) {
e.printStackTrace();
}You must use the same order and type when reading, otherwise you will read wrong results or throw exceptions.
try (DataInputStream input = new DataInputStream(
new FileInputStream("d:/lesson/java2601/test/data.bin"))) {
int number = input.readInt();
double price = input.readDouble();
String text = input.readUTF();
System.out.println(number);
System.out.println(price);
System.out.println(text);
} catch (IOException e) {
e.printStackTrace();
}Object serialization and deserialization
Serialization converts an object and its serializable object graph into a byte sequence, and deserialization restores the object based on the byte sequence.
Class needs to implement the Serializable tag interface.
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private transient String password;
}
The transient field will not participate in default serialization. serialVersionUID is used to determine whether serialized data is compatible with the current class version.
Serialize to byte array
public static byte[] serialize(Object object) throws IOException {
try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream output = new ObjectOutputStream(bytes)) {
output.writeObject(object);
return bytes.toByteArray();
}
}
deserialize from byte array
public static Object deserialize(byte[] data)
throws IOException, ClassNotFoundException {
try (ByteArrayInputStream bytes = new ByteArrayInputStream(data);
ObjectInputStream input = new ObjectInputStream(bytes)) {
return input.readObject();
}
}Serialization data does not require that JDK minor versions be completely consistent, but class names, field structures, serialVersionUID and related classes must meet compatibility conditions.
deserializing untrusted data may trigger dangerous object behavior. Bytestreams from untrusted sources must not be directly deserialized in real projects.
PrintWriter
PrintWriter provides convenient text output methods such as print(), println() and printf().
try (PrintWriter writer = new PrintWriter(
"d:/lesson/java2601/test/a.txt",
StandardCharsets.UTF_8.name())) {
writer.println("你好");
writer.println("中午吃什么");
} catch (FileNotFoundException | UnsupportedEncodingException e) {
e.printStackTrace();
}Some write errors in PrintWriter will not be directly thrown as IOException. checkError() can be called to check the status when needed. Closing the stream automatically flushes the buffer.
Properties Class
Properties is used to save string key value configuration and inherits from Hashtable<Object, Object>. getProperty() should be used first when reading the configuration, and setProperty() should be used when writing.
Configuration files are suitable for holding data that can be adjusted by the deployment environment but should not be hard-coded into the program.
Common configuration formats
- XML has a clear structure, but it is relatively complex to write and parse.
- Properties use simple key value forms and have limited hierarchical expression capabilities.
- YAML has strong hierarchical expression capabilities and is often used in frameworks such as Spring Boot.
read configuration
p1.name=Tom
p1.age=20
p2.name=Jerry
p2.age=22
Properties properties = new Properties();
try (Reader reader = new InputStreamReader(
new FileInputStream("src/myinfo.properties"),
StandardCharsets.UTF_8)) {
properties.load(reader);
Person person1 = new Person();
person1.name = properties.getProperty("p1.name");
person1.age = Integer.parseInt(properties.getProperty("p1.age"));
Person person2 = new Person();
person2.name = properties.getProperty("p2.name");
person2.age = Integer.parseInt(properties.getProperty("p2.age"));
} catch (IOException e) {
e.printStackTrace();
}Use Reader to explicitly select a character set. When InputStream is directly used to read Properties in Java 8, traditional formats are handled according to ISO-8859-1 rules, and non-ASCII characters usually require Unicode escape.
writing configuration
properties.setProperty("p3.name", "Tom");
properties.setProperty("p3.age", "22");
try (Writer writer = new OutputStreamWriter(
new FileOutputStream("src/myinfo.properties"),
StandardCharsets.UTF_8)) {
properties.store(writer, "第三个人的信息");
} catch (IOException e) {
e.printStackTrace();
}
Apache POI
Apache POI is a Java project of the Apache Software Foundation that can read and write Microsoft Office formats such as Excel and Word.
A typical exercise is to read data from a student’s achievement workbook, calculate the average, and write the results to another worksheet. When processing Excel, you need to select the corresponding model based on the file format:
.xlsusually uses HSSF..xlsxusually uses XSSF.
When using POI to read and write workbooks, input streams, and output streams, resources should also be correctly turned off through try-with-resources, and attention should be paid to the handling of cell types, empty cells, and formula cells.
If you enjoyed this, leave a comment~