一、什么是Java的IO流?
(1) Java中的IO(输入/输出)流就是处理数据输入和数据输出的抽象类。
(2) Java I/O流中主要分为俩大类:字节流(Byte Streams)和字符流(Character Streams)。
字节流:主要用于处理二进制数据,包括InputStream 和 OutputStream 俩个主要类和其他子类。
字符流:⽤于处理⽂本数据,包括Reader和Writer两个主要类及其⼦类。
二、Java输入流InputStream
2.1 InputStream
是输⼊字节流的⽗类,它是⼀个抽象类(⼀般⽤他的⼦类)。
在Java中, InputStream 是所有字节输⼊流的超类。
它定义了字节流输⼊的基本操作,如读取字节、跳过字节和标记/重置流等。
通过 InputStream ,我们可以从⽂件、⽹络连接或其他数据源中读取字节数据。
2.2 常⻅⽅法
int read():
从输⼊流中读取单个字节,返回0到255范围内的int字节值, 字节数据可直接转换为int类型
如果已经到达流末尾⽽没有可⽤的字节,则返回-1
int read(byte[] b) :
从输⼊流中读取最多 b.length 个字节的数据到字节数组 b 中,并返回实际读取的字节数。
如果因为已经到达流末尾⽽没有更多的数据,则返回-1。
int read(byte[] b, int off, int len) :
从输⼊流中读取最多 len 个字节的数据到字节数组 b 中,从 off 指定的偏移量开始存储,并返回 实际读取的字节数。
如果因为已经到达流末尾⽽没有更多的数据,则返回-1。
long skip(long n) :
跳过输⼊流中的 n 个字节。如果实际跳过的字节数⼩于 n ,则可能是因为已经到达流的末尾。
此⽅法返回实际跳过的字节数。
int available() :返回可以从此输⼊流中读取的字节数的估计值。
void close() :关闭此输⼊流并释放与该流关联的系统资源。
2.3 常见子类
FileInputStream
抽象类InputStream⽤来具体实现类的创建对象, ⽂件字节输⼊流, 对⽂件数据以字节的形式进⾏读 取操作
//常⽤构造函数,传⼊⽂件所在地址
public FileInputStream(String name) throws FileNotFoundException
//常⽤构造函数,传⼊⽂件对象
public FileInputStream(File file) throws FileNotFoundException
ByteArrayInputStream 字节数组输⼊流
ObjectInputStream 对象输⼊流
案例实战:
public class InputStreamDemo {
public static void main(String[] args) {
String dir = "/Users/xdclass/Desktop/coding/xdclass-account/src/chapter11";
String name = "a.txt";
File file = new File(dir, name);
try (InputStream inputStream = new FileInputStream(file)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
// 处理读取到的数据(例如打印到控制台)
System.out.println(new String(buffer, 0, bytesRead));
//中⽂乱码问题,换成GBK 或者 UTF-8
//System.out.println(new String(buffer,"UTF-8"));
//System.out.println(new String(buffer,0, bytesRead,"UTF-8"));
//System.out.println(new String(buffer, 0, bytesRead));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:
在上⾯的示例中,使⽤了try-with-resources语句来⾃动关闭 InputStream 。
这是JDK 7及更⾼版本引⼊的⼀个新特性,⽤于确保在不再需要资源时⾃动关闭它们。
三、Java输出流OutputStream
3.1 OutputStream
是输出字节流的⽗类,它是⼀个抽象类,在Java中, OutputStream 是所有字节输出流的超类。
它定义了字节流输出的基本操作,如写⼊字节、刷新输出流和关闭输出流等。
通过 OutputStream ,可以将数据写⼊⽂件、⽹络连接或其他数据接收端。
3.2 常见方法
void write(int b) :将指定的字节写⼊此输出流。
void write(byte[] b) :将 b.length 个字节从指定的字节数组写⼊此输出流。
void write(byte[] b, int off, int len) :从指定的字节数组写⼊ len 个字节,从偏移量 off 开始。
void flush():
刷新此输出流并强制写出任何缓冲的输出字节,
进⾏输出时,为了提⾼效率,这些类通常会实现缓存机制。
当调⽤ write() ⽅法写⼊数据时,数据可能并不会⽴即被发送到⽬标位置,⽽是先被存储在内部 缓冲区中。
当缓冲区满或我们显式地调⽤ flush() ⽅法时。
void close() :关闭此输出流并释放与此流相关联的任何系统资源
3.3常见子类
FileOutputStream ,抽象类⽤来具体实现类的创建对象, ⽂件字节输出流, 对⽂件数据以字节的形式 进⾏输出的操作
//传⼊输出的⽂件地址
public FileOutputStream(String name)
//传⼊⽬标输出的⽂件对象
public FileOutputStream(File file)
//传⼊⽬标输出的⽂件对象, 是否可以追加内容
public FileOutputStream(File file, boolean append)
ByteArrayOutputStream :在内存中创建⼀个缓冲区,所有写⼊流的数据都会置⼊这个缓冲区。
案例代码:
public class OutputStreamExample {
public static void main(String[] args) {
String dir = "/Users/xdclass/Desktop/coding/xdclass-account/src/chapter11";
String name = "b.txt";
try (OutputStream outputStream = new FileOutputStream(file)) {
String data = "Hello, World!";
outputStream.write(data.getBytes());
outputStream.flush(); // 确保所有数据都写⼊⽂件
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:
在Java中,通常会在以下情况下调⽤ flush() ⽅法:
在完成所有写⼊操作并希望确保所有数据都被发送到⽬标位置时。
在需要在写⼊过程中⽴即看到数据的效果时(例如,在⽹络编程中)。
在关闭输出流之前,以确保所有缓冲的数据都被发送出去。
四、Java IO包之缓冲Buffer输入输出流
1.什么是Buffer?
它是内存空间的⼀部分,在内存空间中预留了⼀定的存储空间
这些存储空间⽤来缓冲输⼊或输出的数据,这部分空间就叫做缓冲区,缓冲区是具有⼀定⼤⼩的
当使⽤缓冲流时,数据⾸先被读写到缓存区中,然后再从缓存区传输到⽬标位置(如⽂件、⽹络等)。
这种⽅式减少了直接对⽬标位置的读写操作,因此提⾼了性能。
2.为什么要使用缓冲区
缓冲,缓和冲击,例如操作磁盘⽐内存慢的很多,所以不⽤缓冲区效率很低,数据传输速度和数据处理 的速度存在不平衡。
⽐如:
1.每秒要读写100次硬盘,对系统冲击很⼤,浪费了⼤量时间在忙着处理开始写和结束写这两个事件。
2.所以⽤内存的buffer暂存起来,变成每10秒写⼀次硬盘,数据可以直接送往缓冲区。
⾼速设备不⽤再等待低速设备,对系统的冲击就很⼩,写⼊效率⾼了。
3.Java中的缓冲输⼊流与输出流(提供了四种Buffer流)
BufferedInputStream:缓存输⼊流,封装了InputStream,提供了缓存区来暂存输⼊数据。
BufferedOutputStream:缓存输出流,封装了OutputStream,提供了缓存区来暂存输出数据。
BufferedReader:缓存字符输⼊流,封装了Reader,提供了缓存区来暂存字符输⼊数据。
BufferedWriter:缓存字符输出流,封装了Writer,提供了缓存区来暂存字符输出数据。
采⽤的包装设计模式(锦上添花)
主要特点:由于使⽤了缓存区,因此减少了直接对⽬标位置的读写操作,从⽽提⾼了性能。
4.BufferInputStream 缓冲字节输⼊流
通过预先读⼊⼀整段原始输⼊流数据⾄缓冲区中,外界对BufferedInputStream的读取操作实际上是在 缓冲区上进⾏,
如果读取的数据超过了缓冲区的范围,BufferedInputStream负责重新从原始输⼊流中载⼊下⼀截数 据,填充缓冲区
然后外界继续通过缓冲区进⾏数据读取,避免了⼤量的磁盘IO,原始的InputStream类实现的read是即 时读取的
因为每⼀次读取都会是⼀次磁盘IO操作(哪怕只读取了1个字节的数据),如果数据量巨⼤,这样的磁 盘消耗⾮常可怕。
读取可以读取缓冲区中的内容,当读取超过缓冲区的内容后再进⾏⼀次磁盘IO
载⼊⼀段数据填充缓冲,下⼀次读取⼀般情况就直接可以从缓冲区读取,减少了磁盘IO。
默认缓冲区⼤⼩是8k, int DEFAULT_BUFFER_SIZE = 8192;
4.1 构造函数
//对输⼊流进⾏包装,⾥⾯默认的缓冲区是8k
public BufferedInputStream(InputStream in);
//对输⼊流进⾏包装,指定创建具有指定缓冲区⼤⼩的
public BufferedInputStream(InputStream in,int size);
4.2 常用方法
/从输⼊流中读取⼀个字节
public int read();
//从字节输⼊流中,给定偏移量offset处开始, 将len字节读取到指定的byte数组中。
public int read(byte[] buf,int off,int len);
//关闭释放资源,关闭的时候这个流即可,InputStream会在⾥⾯被关闭
void close();
案例实战:
创建了⼀个 FileInputStream 并将其包装在 BufferedInputStream 中。
通过 bis.read(buffer) ⽅法,可以从⽂件中读取数据到缓冲区中,并在读取过程中处理这些数据。
在try代码块结束后, BufferedInputStream 和 FileInputStream 都会被⾃动关闭 。
import java.io.*;
public class BufferedInputStreamExample {
public static void main(String[] args) {
String inputFilePath = "input.txt"; // 假设这个⽂件存在并包含⼀些数据
try (FileInputStream fis = new FileInputStream(inputFilePath);
BufferedInputStream bis = new BufferedInputStream(fis)) {
byte[] buffer = new byte[1024];
int bytesRead;
// 读取数据
while ((bytesRead = bis.read(buffer)) != -1) {
// 处理读取到的数据(这⾥只是简单地打印出来)
System.out.print(new String(buffer, 0, bytesRead));
}
// 注意:由于使⽤了try-with-resources,close()⽅法在这⾥是⾃动调⽤的
} catch (IOException e) {
e.printStackTrace();
}
}
}
5.BufferOutputStream 缓冲字节输出流
内部使⽤⼀个缓冲区来暂存待写⼊的数据。
当缓冲区满时,或者调⽤ flush() ⽅法时,缓冲区中的数据会被⼀次性写⼊到底层输出流中。
这种机制提⾼了数据写⼊的效率,减少了系统I/O操作的次数。
5.1构造函数
//对输出流进⾏包装,⾥⾯默认的缓冲区是8k
public BufferedOutputStream(OutputStream out);
//对输出流进⾏包装,指定创建具有指定缓冲区⼤⼩的
public BufferedOutputStream(OutputStream out,int size);
5.2常用方法
//向输出流中输出⼀个字节
public void write(int b);
//将指定 byte 数组中从偏移量 off 开始的 len 个字节写⼊缓冲的输出流。
public void write(byte[] buf,int off,int len);
//刷新此缓冲的输出流,强制使所有缓冲的输出字节被写出到底层输出流中。
public void flush();
//关闭释放资源,关闭的时候这个流即可,OutputStream会在⾥⾯被关闭, JDK7新特性try(在这⾥声明的会⾃
动关闭){}
void close();
案例代码:
在这个例⼦中,创建了⼀个 FileOutputStream 并将其包装在 BufferedOutputStream 中。
在try代码块结束后, BufferedOutputStream 和 FileOutputStream 都会被⾃动关闭,因为它们都实 现了 AutoCloseable 接⼝。
import java.io.*;
public class BufferedOutputStreamExample {
public static void main(String[] args) {
String outputFilePath = "output.txt";
try (FileOutputStream fos = new FileOutputStream(outputFilePath);
BufferedOutputStream bos = new BufferedOutputStream(fos)) {
// 写⼊数据到缓冲区
String data = "Hello, World!";
bos.write(data.getBytes());
// 注意:由于使⽤了try-with-resources,flush()和close()⽅法在这⾥是⾃动调⽤的
System.out.println("数据已成功写⼊⽂件。");
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:
在使⽤完 BufferedOutputStream 后,⼀定要记得关闭它,释放系统资源,通过调⽤ close() ⽅法来 实现关闭操作。
BufferedOutputStream在close()时会⾃动flush
在不调⽤close()的情况下,缓冲区不满,⼜需要把缓冲区的内容写⼊到⽂件或通过⽹络发送到别的机器 时,才需要调⽤flush.
流的关闭顺序: 后开先关, 如果A依赖B,先关闭B