背景
IO流是Java中很重要的一部分内容,常用的数据传输,文件的上传和下载都和它分不开。
Java中的IO根据处理数据的方式,可以分为字节流和字符流,同时根据传输方向的不同,又可以分为输入流和输出流。
图中,整理了在Java 8中根据上述分类的IO流,其中字节输入流有28种,字节输出流有18种,字符输入流有9种,字符输出流有8种,看到这么多的流,实际开发中经常使用到的只是其中的一部分。
比如:
-
字节输入流中的FileInputStream、BufferedInputStream,
-
字节输出流中的FileOutputStream、BufferedOutputStream
-
字符输入流中的BufferedReader、InputStreamReader、FileReader
-
字符输出流中的BufferedWriter、OutputStreamWriter、FileWriter等。
在图中已用黑色框图进行了突出标注。
实战案例
1、输入流
1.1 FileInputStream
源码
public class FileInputStream extends InputStream
{
//【1】
private final FileDescriptor fd;
//【2】
private final Object closeLock = new Object();
}
代码分析:
FileInputStream是Java IO中的一个类,用于从文件中读取数据。它继承自InputStream类,可以读取字节流。
-
FileInputStream可以打开一个文件,读取其中的数据,并将数据以字节流的形式返回。
-
如果文件存在但是没有读取权限,FileInputStream会抛出SecurityException异常。
-
FileDescriptor 文件描述符
案例
/**
* 字节流-输入流-FileInputStream
*
*/
public class FileInputStreamTest {
public static void main(String[] args) throws IOException {
File file = new File("/Users/bryantmo/Desktop/code/springcloud_test/webdoor/src/test/java/com/bryant/io/1.txt");
FileInputStream fileInputStream = new FileInputStream(file);
byte[] bytes = new byte[1024];
int read = 0;
// 直接从磁盘读入到内存
while ((read = fileInputStream.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, read));
// 可能再输出到磁盘持久化
}
}
}
1.2 ByteArrayInputStream
源码
public class ByteArrayInputStream extends InputStream {
protected byte buf[];
protected int pos;
}
代码分析:
ByteArrayInputStream 提供了一个从字节数组输入数据的流。主要功能如下:
-
buf:存储字节数据的数组,其中buf[pos]表示下一个要读取的字节。
-
pos:记录当前读取位置的索引,保证非负且不大于数组长度。
案例
/**
* 输入流-字节流-ByteArrayInputStream
*/
@Slf4j
public class ByteArrayInputStreamTest {
public static void main(String[] args) throws IOException {
// 被读取的数据,内存
byte[] bytes = "Hello world".getBytes();
// ByteArrayInputStream 是 Java 中的一个类,它是 InputStream 的子类,用于从字节数组中读取数据。
//这个类允许你将一个字节数组视为一个输入流,从而可以使用标准的 I/O 流操作来处理它。
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes, 0, 3);
while (byteArrayInputStream.read() != -1) {
byteArrayInputStream.read(bytes);
log.info("byteArrayInputStream#read: data = {}", new String(bytes));
}
}
}
1.3 BufferedInputStream
源码
public class BufferedInputStream extends FilterInputStream {
private static int DEFAULT_BUFFER_SIZE = 8192;
protected volatile byte[] buf;
protected int count;
protected int pos;
}
代码分析:
BufferedInputStream 是 FilterInputStream 的子类,用于对输入流进行缓冲处理。
缓冲输入流可以提高读取效率,因为它减少了与底层数据源的交互次数。
案例
/**
* 输入流-字节流-缓冲输入流
*/
@Slf4j
public class BufferedInputStreamTest {
public static void main(String[] args) throws IOException {
File file = new File("/Users/bryantmo/Desktop/code/springcloud_test/webdoor/src/test/java/com/bryant/io/1.txt");
FileInputStream fileInputStream = new FileInputStream(file);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream, 2);
// 设置一个内存
byte[] bytes = new byte[3];
int byteRead;
while ((byteRead = bufferedInputStream.read()) != -1) {
// 直接从磁盘读入数据到内存,非常慢
int read = bufferedInputStream.read(bytes);
log.info("read: data = {}, read = {}", new String(bytes), read);
}
}
}
1.4 BufferedReader
源码
public class BufferedReader extends Reader {
private Reader in;
private char cb[];
}
代码分析:
BufferedReader该类实现了一个带缓冲区的字符输入流。
-
in:表示底层的字符输入流。
-
cb[]:字符缓冲区,用于存储读取的数据,提高读取效率
案例
/**
* 字符流-输入流-BufferedReader
*/
@Slf4j
public class BufferedReaderTest {
public static void main(String[] args) throws IOException {
File file = new File("/Users/bryantmo/Desktop/code/springcloud_test/webdoor/src/test/java/com/bryant/io/1.txt");
FileReader fileReader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
log.info("line : {}", line);
}
}
}
2、输出流
2.1 FileOutputStream
源码
public class FileOutputStream extends OutputStream
{
private final FileDescriptor fd;
}
代码分析:FileOutputStream类继承OutputStream
-
FileDescriptor fd,表示系统依赖的文件描述符,用于文件输出流操作。
案例
/**
* 字节流-输入流-FileOutputStream
*
*/
public class FileOutputStreamTest {
public static void main(String[] args) throws IOException {
String data = "字节流-输入流-FileInputStream";
// 文件标识符
File file = new File("/Users/bryantmo/Desktop/code/springcloud_test/webdoor/src/test/java/com/bryant/io/1.txt");
FileOutputStream fileInputStream = new FileOutputStream(file);
// 直接从内存写入磁盘
fileInputStream.write(data.getBytes());
fileInputStream.flush();
}
}
2.2 ByteArrayOutPutStream
源码
public class ByteArrayOutputStream extends OutputStream {
/**
* The buffer where data is stored.
*/
protected byte buf[];
protected int count;
}
ByteArrayOutputStream的部分属性:
-
buf:一个受保护的字节数组,用于存储数据。
-
count:一个受保护的整型变量,记录已存储的数据量。
案例
/**
* 字节流-输出流-ByteArrayOutputStream
*/
@Slf4j
public class ByteArrayOutputStreamTest {
public static void main(String[] args) throws IOException {
String newString = "Hello, World!";
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 底层是for循环,写入ByteArrayOutputStream的内存数组
baos.write(newString.getBytes());
// 读出内存数组:底层会新建一个数组,通过数组拷贝,并返回
byte[] bytes = baos.toByteArray();
log.info("bytes: {}", bytes);
log.info("string: {}", new String(bytes));
}
}
2.3 BufferedOutputStream
源码
public class BufferedOutputStream extends FilterOutputStream {
/**
* The internal buffer where data is stored.
*/
protected byte buf[];
protected int count;
}
BufferedOutputStream的部分属性:
-
buf:一个受保护的字节数组,用于内部缓冲存储数据。
-
count:一个受保护的整型变量,记录缓冲区中已存储的数据量
案例
/** * 输出流-字节流-BufferedOutputStream
* BufferedOutputStream通过内部维护一个缓冲区来减少对底层文件系统的调用次数,从而提高写入效率。
*/
@Slf4j
public class BufferedOutputStreamTest {
public static void main(String[] args) throws IOException {
// 设置一个内存数据
String data = "Hello world...." + System.currentTimeMillis();
// 输出流写入字符到磁盘
File file = new File("/Users/bryantmo/Desktop/code/springcloud_test/webdoor/src/test/java/com/bryant/io/1.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
// 这里我们测试了,缓冲区大小为2和222的预设值时,分别对应了底层不同的写入磁盘逻辑
// BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream, 2);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream, 222);
bufferedOutputStream.write(data.getBytes());
// flush()方法用于强制将缓冲区中的数据写入到底层的文件输出流中。即使缓冲区未满,调用flush()也会立即执行写入操作。
bufferedOutputStream.flush();
}
}
2.4 BufferedReader
源码
public class BufferedWriter extends Writer {
private Writer out;
private char cb[];
}
它是 Java 中的一个类,用于从字符输入流中读取文本,缓冲字符以提供高效的读取操作。BufferedReader 继承自 Reader 类,并且它提供了一个缓冲区,可以减少对底层数据源的访问次数,从而提高读取效率。
主要特点
-
缓冲:BufferedReader 使用内部缓冲区来存储从输入流中读取的字符,这样可以减少对底层数据源的访问次数。
-
高效读取:由于缓冲机制的存在,BufferedReader 提供了比直接使用 Reader 更高效的读取操作。
-
行读取:BufferedReader 提供了 readLine() 方法,可以方便地按行读取文本数据。
案例
/**
* 字符流-输出流-BufferedWriter
*/
@Slf4j
public class BufferedWriterTest {
public static void main(String[] args) throws IOException {
String data = "hello world";
File file = new File("/Users/bryantmo/Desktop/code/springcloud_test/webdoor/src/test/java/com/bryant/io/2.txt");
FileWriter fileWriter = new FileWriter(file);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
try {
bufferedWriter.write(data);
bufferedWriter.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}