java教程笔记(十五)-io流

发布于:2025-06-11 ⋅ 阅读:(26) ⋅ 点赞:(0)

1.IO流概述

  1. 什么是IO流

    • IO流是处理设备上数据的读取和写入的一种方式。数据输入到计算机内存的过程即输入,反之输出到外部存储(比如数据库,文件,远程主机)的过程即输出。
    • Java IO流主要包含输入流(InputStream)和输出流(OutputStream),用于对文件或网络等进行数据读写操作。
  2. 分类

    • 按流向分
      • 输入流:InputStream 和 Reader
      • 输出流:OutputStream 和 Writer
    • 按处理单元分
      • 字节流:以字节(8位)为单位进行读写,适合读取二进制文件(如图片、音频、视频等)
        • InputStreamOutputStream
      • 字符流:以字符(字符(16位 Unicode))为单位进行读写,适合文本文件(如txt文件)
        • ReaderWriter

2.Java IO常用类图谱

1.字节流顶层抽象类

  • InputStream
  • OutputStream

1.子类:

子类名 用途说明
FileInputStream 从文件系统中的某个文件读取字节流
ByteArrayInputStream 从字节数组中读取数据
BufferedInputStream 给其他输入流添加缓冲功能,提高读取效率
DataInputStream 支持读取 Java 基本数据类型(如 intdouble 等)
ObjectInputStream 支持反序列化对象(需配合 Serializable 接口使用)
PipedInputStream 与 PipedOutputStream 配合,实现线程间通信
InputStreamReader 将字节流转换为字符流(配合 Reader 使用)
GZIPInputStream 用于解压缩 GZIP 格式的数据
InflaterInputStream 用于解压缩 ZIP 或 Deflate 格式的数据
SequenceInputStream 合并多个输入流,按顺序读取
子类名 用途说明
FileOutputStream 向文件系统中的某个文件写入字节流
ByteArrayOutputStream 向字节数组中写入数据(内存操作)
BufferedOutputStream 给其他输出流添加缓冲功能,提高写入效率
DataOutputStream 支持写入 Java 基本数据类型(如 intdouble 等)
ObjectOutputStream 支持序列化对象(需配合 Serializable 接口使用)
PipedOutputStream 与 PipedInputStream 配合,实现线程间通信
OutputStreamWriter 将字符流转换为字节流(配合 Writer 使用)
GZIPOutputStream 用于压缩数据为 GZIP 格式
DeflaterOutputStream 用于压缩 ZIP 或 Deflate 格式的数据

2.字符流顶层抽象类

  • Reader
  • Writer

3.InputStream和OutputStream  

1.InputStream 的基本作用

  • 以字节为单位读取数据
  • 支持顺序读取
  • 提供基础 API,由子类实现具体功能

2. OutputStream的基本作用

  • 以字节为单位写入数据
  • 支持顺序写入
  • 提供基础 API,由子类实现具体功能

3.InputStream和OutputStream 的核心方法

1.InputStream

方法 描述
int read() 从输入流中读取一个字节的数据,返回 0~255 的 int 值,若到达流末尾则返回 -1
int read(byte[] b) 从输入流中读取最多 b.length 字节的数据到字节数组 b 中,返回实际读取的字节数
int read(byte[] b, int off, int len) 从输入流中读取最多 len 字节的数据到字节数组 b 中,从偏移量 off 开始存放
void close() 关闭输入流并释放相关资源(必须调用)
long skip(long n) 跳过 n 个字节,并返回实际跳过的字节数
int available() 返回当前可读取的字节数(不阻塞)
boolean markSupported() 判断该流是否支持 mark() 和 reset() 操作
void mark(int readlimit) 标记当前流的位置,后续可通过 reset() 回退
void reset() 将流重置到最近一次调用 mark() 的位置
1. int read()
import java.io.FileInputStream; 
import java.io.InputStream; 
import java.io.IOException; 
public class ReadSingleByte { 
public static void main(String[] args) { 
String filePath = "example.txt";
 try (InputStream is = new FileInputStream(filePath)) {
 int data;
 while ((data = is.read()) != -1) { 
System.out.print((char) data); // 转换为字符输出
 } 
} 
catch (IOException e) { e.printStackTrace(); } } }

 输出说明:

假设 example.txt 内容为:

Hello InputStream

输出结果:

Hello InputStream

2. int read(byte[] b)
import java.io.FileInputStream; 
import java.io.InputStream; 
import java.io.IOException; 
public class ReadByteArray { 
public static void main(String[] args) { 
String filePath = "example.txt"; 
try (InputStream is = new FileInputStream(filePath)) {
 byte[] buffer = new byte[1024]; // 缓冲区大小
 int bytesRead; 
while ((bytesRead = is.read(buffer)) != -1) { 
System.out.write(buffer, 0, bytesRead); 
} 
}
 catch (IOException e) { e.printStackTrace(); } } }

 输出

与上一个例子相同,但效率更高,适合大文件读取。

3. int read(byte[] b, int off, int len)

import java.io.FileInputStream; 
import java.io.InputStream; 
import java.io.IOException; 
public class ReadPartialArray { 
public static void main(String[] args) {
 String filePath = "example.txt"; 
try (InputStream is = new FileInputStream(filePath)) { 
byte[] buffer = new byte[1024];
 int bytesRead; 
while ((bytesRead = is.read(buffer, 0, buffer.length)) != -1) {
 System.out.println("Read " + bytesRead + " bytes: " + new String(buffer, 0, bytesRead)); 
}
 } 
catch (IOException e) { e.printStackTrace(); } } }
4. void close()
import java.io.FileInputStream;
 import java.io.InputStream; 
import java.io.IOException;
 public class CloseStream { 
public static void main(String[] args) { 
String filePath = "example.txt"; 
try (InputStream is = new FileInputStream(filePath)) {
 // 自动调用close() 
int data; 
while ((data = is.read()) != -1) {
 System.out.print((char) data); 
} 
} 
catch (IOException e) { e.printStackTrace(); } } }
5. long skip(long n)
import java.io.FileInputStream; 
import java.io.InputStream;
 import java.io.IOException;
 public class SkipBytes {
 public static void main(String[] args) { 
String filePath = "example.txt"; 
try (InputStream is = new FileInputStream(filePath)) { 
is.skip(5); // 跳过前5个字节 
int data; 
while ((data = is.read()) != -1) { 
System.out.print((char) data);
 } 
} 
catch (IOException e) { e.printStackTrace(); } } }

 输出

若原文件是 "Hello InputStream",跳过后输出:

InputStream

6. int available()
import java.io.FileInputStream; 
import java.io.InputStream; 
import java.io.IOException;
 public class AvailableBytes { 
public static void main(String[] args) {
 String filePath = "example.txt"; 
try (InputStream is = new FileInputStream(filePath)) {
 System.out.println("Available bytes: " + is.available()); 
int data; 
while ((data = is.read()) != -1) {
 System.out.print((char) data);
 } 
} 
catch (IOException e) { e.printStackTrace(); } } }
7. boolean markSupported()
import java.io.FileInputStream; 
import java.io.InputStream; 
import java.io.IOException; 
public class MarkSupportedCheck { 
public static void main(String[] args) {
 String filePath = "example.txt"; 
try (InputStream is = new FileInputStream(filePath)) { 
if (is.markSupported()) { 
System.out.println("Mark/reset supported."); 
} 
else { 
System.out.println("Mark/reset NOT supported."); 
} 
} 
catch (IOException e) { e.printStackTrace(); } } }
8. void mark(int readlimit)

✅ 功能

在当前位置设置标记,之后可以调用 reset() 回退到该位置。

import java.io.BufferedInputStream; 
import java.io.FileInputStream;
 import java.io.InputStream; 
import java.io.IOException; 
public class UseMarkAndReset { 
public static void main(String[] args) { 
String filePath = "example.txt"; 
try (InputStream is = new BufferedInputStream(new FileInputStream(filePath))) {
 int firstChar = is.read(); 
System.out.println("First char: " + (char) firstChar); 
is.mark(100); // 设置标记,允许最多读100字节后仍能回退 
int secondChar = is.read(); 
System.out.println("Second char: " + (char) secondChar);
 is.reset(); // 回退到mark的位置 
int thirdChar = is.read(); 
System.out.println("Third char (after reset): " + (char) thirdChar); } 
catch (IOException e) { e.printStackTrace(); } } }

 输出

First char: H Second char: e Third char (after reset): H

 2.OutputStream 

方法 描述
void write(int b)

方法用于向输出流写入一个字节的数据。参数b是一个0 - 255的整数,实际写入的是该整数对应的低8位字节。虽然方法接收的是int类型,但仅使用其低8位参与写入操作。

outputStream.write(65); // 写入字符'A'的ASCII码

outputStream.write(66); // 写入字符'B'的ASCII码

void write(byte[] b) 写入整个字节数组中的所有字节
void write(byte[] b, int off, int len) 写入字节数组中从偏移量 off 开始的 len 个字节,
  • b包含要写入的字节的数组。
  • off:起始位置(偏移量)。
  • len:要写入的字节数
void flush()

flush()方法用于强制刷新输出流缓冲区。当数据写入输出流时,通常会先存储在缓冲区中,以减少实际的物理写入次数,提高效率。但在某些情况下,需要确保缓冲区中的数据立即被写入目标,此时就需要调用flush()方法。例如,在网络通信中向客户端发送数据后,为保证数据及时发送,可调用flush():

void close() 关闭输出流并释放相关资源(必须调用)
1. void write(int b) 示例
import java.io.FileOutputStream;
 import java.io.IOException; 
import java.io.OutputStream; 
public class WriteSingleByteExample { 
public static void main(String[] args) { 
String filePath = "single_byte_output.txt"; 
try (OutputStream os = new FileOutputStream(filePath)) { 
int data = 'A'; // ASCII value of 'A' is 65 
os.write(data); // 写入一个字节 
} 
catch (IOException e) { e.printStackTrace(); } } }

说明:

  • 将字符 'A'(ASCII 码为 65)作为一个字节写入文件 single_byte_output.txt
  • 文件内容将只包含一个字符:A
2. void write(byte[] b) 示例
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.OutputStream;
 public class WriteByteArrayExample { 
public static void main(String[] args) {
 String filePath = "byte_array_output.txt"; 
try (OutputStream os = new FileOutputStream(filePath)) { 
byte[] bytes = "Hello, OutputStream!".getBytes();
 os.write(bytes); // 写入整个字节数组
 } 
catch (IOException e) { e.printStackTrace(); } } }

说明:

  • 将字符串 "Hello, OutputStream!" 转换为字节数组后一次性写入文件。
  • 文件内容为完整的字符串。
3. void write(byte[] b, int off, int len) 示例
import java.io.FileOutputStream;
 import java.io.IOException; 
import java.io.OutputStream; 
public class WritePartialArrayExample {
 public static void main(String[] args) { 
String filePath = "partial_array_output.txt"; 
try (OutputStream os = new FileOutputStream(filePath)) {
 byte[] bytes = "This is a test string.".getBytes();
 os.write(bytes, 5, 10); // 从索引5开始写入10个字节
 } 
catch (IOException e) { e.printStackTrace(); } } }

说明:

  • 原始字节数组对应字符串 "This is a test string."
  • 从第 5 个字符(即 'i')开始写入 10 个字节,因此写入的内容是 "is a test"
4. void flush() 示例
import java.io.FileOutputStream;
 import java.io.BufferedOutputStream; 
import java.io.IOException; 
import java.io.OutputStream;
 public class FlushExample { 
public static void main(String[] args) { 
String filePath = "flush_output.txt"; 
try (OutputStream os = new BufferedOutputStream(new FileOutputStream(filePath))) {
 byte[] bytes = "Data to be flushed".getBytes(); 
os.write(bytes); os.flush(); // 显式刷新缓冲区
 } catch (IOException e) { e.printStackTrace(); } } }

说明:

  • 使用了 BufferedOutputStream,它内部有缓冲区。
  • 调用 flush() 强制将缓冲区的数据写入目标文件。
5. void close() 示例
import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 public class CloseExample { 
public static void main(String[] args) { 
String filePath = "close_output.txt"; 
try (OutputStream os = new FileOutputStream(filePath)) {
 byte[] bytes = "Closing the stream properly".getBytes(); 
os.write(bytes);
 } 
catch (IOException e) { e.printStackTrace(); } } }

说明:

  • 使用 try-with-resources 自动调用 close() 方法关闭流。
  • 避免资源泄漏,确保流在使用完毕后被正确关闭。

4.常用使用示例

1.FileInputStream 和FileOutputStream

1.FileInputStream 

创建 FileInputStream 对象,常用的2种方法:

new FileInputStream(File file):通过 File 对象创建一个输入流

new FileInputStream(String name):通过文件路径创建一个输入流。

其中 name 为文件的路径,如 d:\\0\\fist.txt

try (InputStream is = new FileInputStream("example.txt")) {
 int data;
 while ((data = is.read()) != -1) { 
// // 读取的data是字节对应的编码值(int值),转换为字符后从编码获取对应的字符
System.out.print((char) data);
 }
 } 
catch (IOException e) { e.printStackTrace(); }
/*
文件内容:
读取的 data=72,对应的字符 char=H
读取的 data=101,对应的字符 char=e
*/
2.FileOutputStream

常用构造方法

FileOutputStream(String name):根据指定的文件名创建文件输出流,如果文件已存在,则覆盖原有内容;若文件不存在,将创建新文件。

FileOutputStream(File file):根据给定的File对象创建文件输出流。如果文件已存在,则覆盖原有内容;若文件不存在,将创建新文件。若文件是目录或无法打开,抛出 FileNotFoundException

FileOutputStream(String name, boolean append):当appendtrue时,数据将追加到文件末尾;若为false,则覆盖原有内容。

getBytes方法通常指将输入流(如InputStream)转换为字节数组(byte[]

try (FileOutputStream fos = new FileOutputStream("output.txt")) {
    String text = "Hello, World!";
    fos.write(text.getBytes());
} catch (IOException e) {
    e.printStackTrace();
}

2.BufferedInputStream和BufferedOutStream

一般我们是不会直接单独使用 FileInputStream ,通常会配合 BufferedInputStream来使用。这两个类是 Java I/O 包中用于提供缓冲功能的装饰流类。它们可以提高读取和写入数据的效率,减少底层 I/O 操作的次数。

1. BufferedInputStream

作用:为 InputStream添加缓冲功能,从数据源读取数据时,先将数据批量读入缓冲区,后续的读取操作直接从缓冲区获取数据,减少了与数据源的交互次数。

常用构造函数

  • BufferedInputStream(InputStream in):使用默认缓冲区大小(8192字节)创建缓冲流
  • BufferedInputStream(InputStream in, int size):使用指定大小的缓冲区创建缓冲流

示例代码

try (InputStream is = new BufferedInputStream(new FileInputStream("input.txt"))) {
 int data;
 while ((data = is.read()) != -1) { 
System.out.print((char) data); 
} 
} 
catch (IOException e) { e.printStackTrace(); }
import java.io.*;

public class BufferedInputStreamExample {
    public static void main(String[] args) {
        try (BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream("large_file.dat"), 16384)) {
            
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = bis.read(buffer)) != -1) {
                // 处理读取的数据
                processData(buffer, bytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private static void processData(byte[] buffer, int bytesRead) {
        // 数据处理逻辑
    }
}
2. BufferedOutputStream

作用:为 OutputStream 添加缓冲功能,向目标写入数据时,先将数据写入缓冲区,当缓冲区满或调用flush()方法时,再将缓冲区中的数据批量写入目标,减少了与目标的交互次数。

常用构造函数

  • BufferedOutputStream(OutputStream out):使用默认缓冲区大小(8192字节)创建缓冲流
  • BufferedOutputStream(OutputStream out, int size):使用指定大小的缓冲区创建缓冲流

示例代码

try (OutputStream os = new BufferedOutputStream(new FileOutputStream("output.txt"))) { String text = "Hello, World!"; 
os.write(
text.getBytes()); 
} 
catch (IOException e) { e.printStackTrace(); }
import java.io.*;

public class BufferedOutputStreamExample {
    public static void main(String[] args) {
        try (BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("output.dat"), 16384)) {
            
            byte[] data = generateData(1024 * 1024); // 生成1MB数据
            
            // 写入数据到缓冲区
            bos.write(data);
            
            // 确保所有数据都写入底层流
            bos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private static byte[] generateData(int size) {
        byte[] data = new byte[size];
        // 填充数据
        for (int i = 0; i < size; i++) {
            data[i] = (byte) (i % 256);
        }
        return data;
    }
}

注意事项

  • 缓冲机制:这两个类内部维护了一个内区区域的缓冲区(默认大小通常是 8KB),减少了直接与底层数据源(如磁盘、网络)的交互次数,从而提高了IO操作的效率。
  • 关闭流:使用完后务必关闭流以释放资源,并确保所有数据被正确写入目标输出流。
  • 线程安全:它们不是线程安全的,多线程环境下需要额外同步控制。