Java文件I/O操作全解析
立即解锁
发布时间: 2025-08-21 00:56:29 阅读量: 2 订阅数: 12 


Java编程艺术:从初学者到大师的进阶指南
### Java 文件 I/O 操作全解析
在 Java 编程中,文件 I/O 操作是一项基础且重要的技能。本文将详细介绍 Java 中基于字符流的文件 I/O 操作,以及 `RandomAccessFile` 类的使用,同时会结合实际代码示例进行讲解。
#### 基于字符流的文件 I/O
`Reader` 和 `Writer` 类在处理基于字符的文件 I/O 方面比 `InputStream` 和 `OutputStream` 类更强大。这主要是因为它们支持指定字符集编码方案,能够将本地文件编码的字节或字符转换为应用程序所需的编码。
##### Writers 类
- **FileWriter**:用于创建文件并向其中写入字符。它的功能继承自 `OutputStreamWriter`,但在使用时只能以默认的本地编码写入字符。若需要指定其他编码,需使用 `OutputStreamWriter` 结合 `FileOutputStream`。
```java
import java.io.*;
public class FWTesterApp {
public static void main(String[] args){
try{
FileWriter fw = new FileWriter("test.txt");
fw.write("This is a test of the FileWriter class.\n");
fw.write("It inherits its functionality from OutputStreamWriter.\n");
fw.write("You can overwrite the contents of a file...\n");
fw.write("...or you can append text to the end of a file.");
fw.close();
}catch(Exception ignored){ }
}
}
```
- **BufferedWriter**:为其他 `Writer` 对象提供输出缓冲功能,通常与 `FileWriter` 或 `OutputStreamWriter` 结合使用,可提高输出效率。它还提供了 `newLine()` 方法,方便写入换行符。
```java
import java.io.*;
public class BWTesterApp {
public static void main(String[] args){
try{
FileWriter fw = new FileWriter("test.txt");
BufferedWriter bw = new BufferedWriter(fw);
bw.write("This is a test of the BufferedWriter class.");
bw.newLine();
bw.write("It improves output efficiency by buffering individual disk writes.");
bw.newLine();
bw.write("You can specify the internal buffer size at the time of object creation.");
bw.newLine();
bw.write("...and it provides a newLine() method!");
bw.flush();
bw.close();
}catch(Exception ignored){ }
}
}
```
- **OutputStreamWriter**:提供字符编码的灵活性,使用时需先创建一个 `OutputStream` 对象,如 `FileOutputStream`。还可结合 `BufferedWriter` 提高输出效率。
```java
import java.io.*;
public class OSWTesterApp {
public static void main(String[] args){
try{
FileOutputStream fos = new FileOutputStream("test.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos);
BufferedWriter bw = new BufferedWriter(osw);
bw.write("Ohhhh do you love Java like I love Java?");
bw.newLine();
bw.write("The OutputStreamWriter needs a FileOutputStream object...");
bw.newLine();
bw.write("...and can be used with a BufferedWriter to improve output efficiency.");
bw.newLine();
bw.write("...and it's fun to use!");
bw.flush();
bw.close();
}catch(Exception ignored){ }
}
}
```
- **PrintWriter**:功能类似于 `PrintStream`,可将 Java 基本类型、对象、字符串和字符的文本表示写入磁盘或其他输出目的地。使用时需结合 `OutputStream` 或 `Writer` 对象,与 `OutputStreamWriter` 结合时可指定所需的字符编码。
```java
import java.io.*;
public class PWTesterApp {
public static void main(String[] args){
try{
FileOutputStream fos = new FileOutputStream("test.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos);
BufferedWriter bw = new BufferedWriter(osw);
PrintWriter pw = new PrintWriter(bw);
pw.println(123);
pw.println(true);
pw.println(1.234);
pw.println("Ohhhh I love Java!!");
pw.println(new Person("Rick", "W", "Miller", "Male"));
pw.flush();
pw.close();
}catch(Exception ignored){ }
}
}
```
##### Readers 类
- **FileReader**:从文件中读取字节,并根据默认的本地编码进行转换。若需要对编码方案有更多控制,可直接使用 `InputStreamReader`。
```java
import java.io.*;
public class FRTesterApp {
public static void main(String[] args){
try{
File file = new File("test.txt");
FileReader fr = new FileReader(file);
char[] char_buffer = new char[(int)file.length()];
fr.read(char_buffer);
for(int i = 0; i<char_buffer.length; i++){
System.out.print(char_buffer[i]);
}
fr.close();
}catch(Exception ignored){ }
}
}
```
- **InputStreamReader**:当需要对将文件中读取的字节转换为字符的编码方案有更多控制时使用,它需要 `InputStream` 对象的支持。
```java
import java.io.*;
public class ISRTesterApp {
public static void main(String[] args){
try{
File file = new File("test.txt");
FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis);
char[] char_buffer = new char[(int)file.length()];
isr.read(char_buffer);
for(int i = 0; i<char_buffer.length; i++){
System.out.print(char_buffer[i]);
}
isr.close();
}catch(Exception ignored){ }
}
}
```
- **BufferedReader**:可与 `InputStreamReader` 结合使用,提高输入效率,其内部缓冲区大小可在实例化时指定。
```java
import java.io.*;
public class BRTesterApp {
public static void main(String[] args){
try{
File file = new File("test.txt");
FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
char[] char_buffer = new char[(int)file.length()];
br.read(char_buffer);
for(int i = 0; i<char_buffer.length; i++){
System.out.print(char_buffer[i]);
}
br.close();
}catch(Exception ignored){ }
}
}
```
#### java.util.Properties 类的实际应用
`Properties` 类提供了一种方便的方式,可将应用程序属性存储在两种纯文本文件格式中:属性名/值对或 XML 文件。以下是一个示例:
```java
import java.util.*;
import java.io.*;
public class AppProperties extends Properties {
public static final String PROPERTY_A_NAME = "PROPERTY_A_NAME";
public static final String PROPERTY_B_NAME = "PROPERTY_B_NAME";
public static final String PROPERTY_C_NAME = "PROPERTY_C_NAME";
public static final String DEFAULT_PROPERTIES_FILENAME = "DEFAULT_PROPERTIES_FILENAME";
public static final String PROPERTIES_FILE_HEADER = "PROPERTIES_FILE_HEADER";
private static final String PROPERTY_A_VALUE = "Property A's Value";
private static final String PROPERTY_B_VALUE = "Property B's Value";
private static final String PROPERTY_C_VALUE = "Property C's Value";
private static final String DEFAULT_PROPERTIES_FILENAME_VALUE = "app_prop.xml";
private static final String PROPERTIES_FILE_HEADER_VALUE = "Application Properties File";
public AppProperties(){
System.out.println("Attempting to read properties file...");
FileInputStream fis = null;
FileOutputStream fos = null;
try{
fis = new FileInputStream(DEFAULT_PROPERTIES_FILENAME_VALUE);
this.loadFromXML(fis);
}catch(Exception e1){
System.out.println("AppProperties: Problem loading properties file.");
System.out.println(e1.toString());
System.out.println("Creating new default properties file.");
this.setProperty(PROPERTY_A_NAME, PROPERTY_A_VALUE);
this.setProperty(PROPERTY_B_NAME, PROPERTY_B_VALUE);
this.setProperty(PROPERTY_C_NAME, PROPERTY_C_VALUE);
this.setProperty(DEFAULT_PROPERTIES_FILENAME, DEFAULT_PROPERTIES_FILENAME_VALUE);
this.setProperty(PROPERTIES_FILE_HEADER, PROPERTIES_FILE_HEADER_VALUE);
try{
fos = new FileOutputStream(DEFAULT_PROPERTIES_FILENAME_VALUE);
this.storeToXML(fos, PROPERTIES_FILE_HEADER_VALUE);
}catch(Exception e2){
System.out.println("AppProperties: Problem creating default properties file.");
System.out.println(e2.toString());
}finally{
if(fis != null){
try{
fis.close();
}catch(Exception ignored){ }
}
if(fos != null){
try{
fos.close();
}catch(Exception ignored){ }
}
}
}
}
}
```
```java
public class PropertiesTesterApp {
public static void main(String[] args){
AppProperties properties = new AppProperties();
System.out.println(properties.getProperty(properties.PROPERTY_A_NAME));
}
}
```
当创建 `AppProperties` 对象时,会尝试打开并读取默认的属性文件。若文件不存在或无法打开,会初始化一组默认属性并创建属性文件。
#### RandomAccessFile 类的使用
`RandomAccessFile` 可用于读写字节、字符和基本类型,但不能读写对象。它提供了 `seek()` 方法,允许在文件中前后移动到任意位置。
##### 项目需求
假设要创建一个 Java 适配器,用于访问遗留系统的固定长度记录数据文件。项目需求如下:
- **目标**:
- 展示使用 `RandomAccessFile` 类进行文件 I/O 操作的能力。
- 展示实现非平凡接口的能力。
- 展示将低级异常转换为高级、用户定义的应用程序特定异常抽象的能力。
- 展示通过对象同步协调文件 I/O 操作的能力。
- **任务**:作为零售书店 IT 部门的初级程序员,CEO 希望使用 Java 技术将遗留系统迁移到 Web。第一步是为现有的遗留数据存储创建 Java 适配器。根据接口定义、示例遗留数据文件和遗留数据文件模式定义,编写一个 Java 类作为遗留数据文件的适配器对象。
- **已知条件**:
- 指定适配器操作的 Java 接口文件。
- 遗留数据文件模式定义。
- 示例遗留数据文件。
遗留数据文件包含三个部分:
| 部分 | 描述 |
| ---- | ---- |
| 文件标识部分 | 一个两字节的值,用于标识文件为数据文件。 |
| 模式描述部分 | 紧随第一部分,包含数据部分每个字段的文本名称和两字节的字段长度。 |
| 数据部分 | 包含固定字段长度的记录数据元素,按照以下模式排列: |
| 字段名称 | 长度(字节) | 描述 |
| ---- | ---- | ---- |
| deleted | 1 | 数值 - 0 表示有效,1 表示已删除 |
| title | 50 | 文本 - 书籍标题 |
| author | 50 | 文本 - 作者全名 |
| pub_code | 4 | 数值 - 出版商代码 |
| ISBN | 13 | 文本 - 国际标准书号 |
| price | 8 | 文本 - 零售价格,格式为:$nnnn.nn |
| qoh | 4 | 数值 - 库存数量 |
##### 项目实现思路
- **从小处着手**:编写一个小程序,读取文件的前两个字节并将其转换为数字,以确定文件标识符的值。
```mermaid
graph TD;
A[开始] --> B[创建 RandomAccessFile 对象];
B --> C[将文件指针定位到文件开头];
C --> D[读取前两个字节并转换为数字];
D --> E[输出文件标识符的值];
E --> F[结束];
```
- **逐步分析**:尝试读取文件的其余部分,至少先读取完整的标题和一条完整的记录,同时对标题和记录长度进行详细分析。
- **实现适配器类**:从 `LegacyDatafileInterface` 中指定的 `readRecord()` 方法开始,逐步创建适配器类。
##### 代码实现
```java
import java.io.*;
import java.util.*;
public class DataFileAdapter implements LegacyDatafileInterface {
private static final short FILE_IDENTIFIER = 378;
private static final int HEADER_LENGTH = 54;
private static final int RECORDS_START = 54;
private static final int RECORD_LENGTH = 130;
private static final int FIELD_COUNT = 7;
private static final short DELETED_FIELD_LENGTH = 1;
private static final short TITLE_FIELD_LENGTH = 50;
private static final short AUTHOR_FIELD_LENGTH = 50;
private static final short PUB_CODE_FIELD_LENGTH = 4;
private static final short ISBN_FIELD_LENGTH = 13;
private static final short PRICE_FIELD_LENGTH = 8;
private static final short QOH_FIELD_LENGTH = 4;
private static final String DELETED_STRING = "deleted";
private static final String TITLE_STRING = "title";
privat
```
0
0
复制全文
相关推荐










