万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)(中)

简介: 万字长文+思维导图帮你梳理 Java IO 流,还学不会你来打我(值得收藏)(中)

内存节点流


ByteArrayInputStream是从内存的字节数组中读取数据


public ByteArrayInputStream(byte buf[]) {}


注意:不需要close数据源和抛出IOException,因为不涉及底层的系统调用


ByteArrayOutputStream是向内存字节数组中写数据,内部维护了一个数组


public ByteArrayOutputStream() {
   // 内部维护了一个可变的字节数组
   // protected byte buf[];
    this(32);
}


内存节点流代码示例


ByteArrayInputStream bis = new ByteArrayInputStream("data".getBytes());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int len = 0;
while ((len = bis.read()) != -1){
    bos.write(len);
}
// 输出data
System.out.println(new String(bos.toByteArray()));


应用场景


  1. 内存操作流一般在一些生成临时信息时会被使用,如果临时信息保存着文件中,代码执行完还要删除文件比较麻烦


  1. 结合对象流,可以实现对象和字节数组的互转

字符流

字符流封装了更加适合操作文本字符的方法


Reader用于读取文本字符


public abstract class Reader implements Readable, Closeable {}


核心方法


方法名 方法说明
int read() throws IOException 从输入流中读取一个字符,读到文件末尾时返回-1
int read(char cbuf[]) throws IOException 从输入流中读取字符到char数组中


Writer用于写出文本字符


public abstract class Writer implements Appendable, Closeable, Flushable {}


核心方法


方法名 方法说明
void write(int c) throws IOException 写入单个字符到输出流中
void write(char[] cbuf) throws IOException 写入字符数组到输出流中
void write(char[] cbuf, int off, int len)  throws IOException 写入字符数组的一部分,偏移量off开始,长度为len到输出流中
void write(String str) throws IOException 直接写入字符串到输出流中(常用)
void write(String str, int off, int len) throws IOException 写入字符串的一部分,偏移量off开始,长度为len
Writer append(char c) throws IOException 追加字符到输出流中


文件节点流


字符流操作纯文本字符的文件是最合适的,主要有FileReader和FileWriter


FileReader主要是向磁盘文件中写出数据,常用构造方法如下


public FileReader(String fileName) throws FileNotFoundException{}
public FileReader(File file) throws FileNotFoundException {}


注意:当读取的文件不存在时,会抛出FileNotFoundException,这点和


FileInputStream一致


  1. read()循环读取文件


FileReader fileReader = new FileReader("D:/三国/赵云.txt");
int b;
while ((b = fileReader.read()) != -1) {
    System.out.println((char) b);
}


  1. read(char[]) 读取文件


FileReader fileReader = new FileReader("D:/三国/赵云.txt");
int len;
char[] data = new char[2];
while ((len = fileReader.read(data)) != -1) {
    System.out.println(new String(data, 0, len));
}
// 两个字符两个字符依次读取


FileWriter构造方法如下,和FileOutStream构造方法类似,和FileOutputStream类似。


public FileWriter(String fileName) throws IOException {}
public FileWriter(String fileName, boolean append) throws IOException {}
public FileWriter(File file) throws IOException{}
public FileWriter(File file, boolean append) throws IOException {}


常用的写数据进文件的方法


FileWriter fileWriter = new FileWriter("D:/三国/孙权.txt");
fileWriter.write(97); 
fileWriter.write('b'); 
fileWriter.write('C'); 
fileWriter.write("权"); 
fileWriter.append("力");


注意:


  • 如果不执行close()或者flush()方法,数据只是保存到缓冲区,不会保存到文件。这点和与FileOutputStream不同,原因见 字节流和字符流的共同点章节


应用场景


纯文本文件的io操作,配合处理流一起实现。


内存节点流


字符流也有对应的内存节点流,常用的有StringWriter和CharArrayWriter

StringWriter是向内部的StringBuffer对象写数据。


// 定义
public class StringWriter extends Writer {
    private StringBuffer buf;
    public StringWriter() {
        buf = new StringBuffer();
        lock = buf;
    }
}
// 应用
StringWriter sw = new StringWriter();
sw.write("hello");
StringBuffer buffer = sw.getBuffer();
// 输出hello
System.out.println(buffer.toString());


CharArrayWriter是向内部的char数组写数据


// 定义
public class CharArrayWriter extends Writer {
    protected char buf[];
}
// 应用 
CharArrayWriter caw = new CharArrayWriter();
caw.write("hello");
char[] chars = caw.toCharArray();
for (char c : chars) {
 // 输出了h e l l o
 System.out.println(c);
}


四种常用节点流的使用总结


image.png


字节流和字符流的共同点

注意到,OutputStream、Reader、Writer都实现了Flushable接口,Flushable接口有flush()方法


flush():强制刷新缓冲区的数据到目的地,刷新后流对象还可以继续使用


close(): 强制刷新缓冲区后关闭资源,关闭后流对象不可以继续使用


缓冲区:可以理解为内存区域,程序频繁操作资源(如文件)时,性能较低,因为读写内存较快,利用内存缓冲一部分数据,不要频繁的访问系统资源,是提高效率的一种方式

具体的流只要内部有维护了缓冲区,必须要close()或者flush(),不然不会真正的输出到文件中

处理流

上面的章节介绍了字节流和字符流的常用节点流,但是真正开发中都是使用更为强大的处理流


处理流是对节点流在功能上、性能上的增强


字节流的处理流的基类是FilterInputStreamFilterOutputStream

缓冲流(重点)

前面说了节点流,都是直接使用操作系统底层方法读取硬盘中的数据,缓冲流是处理流的一种实现,增强了节点流的性能,为了提高效率,缓冲流类在初始化对象的时候,内部有一个缓冲数组,一次性从底层流中读取数据到数组中,程序中执行read()或者read(byte[])的时候,就直接从内存数组中读取数据。


分类


字节缓冲流:BufferedInputStream , BufferedOutputStream


字符缓冲流:BufferedReader , BufferedWriter


字节缓冲流


可见构造方法传入的是节点流,是对节点流的装饰


// 内部默认8192 =8*1024 即8M的缓冲区
public BufferedInputStream(InputStream in) {
   // 8192    
   // 内部维护了下面这样的字节数组
    // protected volatile byte buf[];
    this(in, DEFAULT_BUFFER_SIZE);
}
public BufferedOutputStream(OutputStream out) {
        this(out, 8192);
}


这里使用复制一部1G的电影来感受缓冲流的强大


  1. 使用基本的流读取数据(一次传输一个字节)


long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("D:/三国/视频.mp4");
FileOutputStream fos = new FileOutputStream("D:/三国/拷贝.mp4");
int data;
while ((data = fis.read()) != -1) {
    fos.write(data);
}
log.info("拷贝电影耗时:{}ms", System.currentTimeMillis() - start);
// 五分钟还没拷好,关闭程序了...


  1. 使用基本的流读取数据(一次传输一个8M的字节数组)


long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("D:/三国/视频.mp4");
FileOutputStream fos = new FileOutputStream("D:/三国/拷贝.mp4");
int len;
byte[] data = new byte[1024 * 1024 * 1024];
while ((len = fis.read(data)) != -1) {
    fos.write(data, 0, len);
}
log.info("拷贝电影耗时:{}ms", System.currentTimeMillis() - start);
// 拷贝电影耗时:4651ms


  1. 使用缓冲流读取数据(一次传输一个字节)


long start = System.currentTimeMillis();
BufferedInputStream fis = new BufferedInputStream(new FileInputStream("D:/三国/视频.mp4"));
BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream("D:/三国/拷贝.mp4"));
int data;
while ((data = fis.read()) != -1) {
    fos.write(data);
}
log.info("拷贝电影耗时:{}ms", System.currentTimeMillis() - start);
// 拷贝电影耗时:39033ms


  1. 使用缓冲流读取数据(一次传输一个8M的字节数组)(最常使用)


long start = System.currentTimeMillis();
BufferedInputStream fis = new BufferedInputStream(new FileInputStream("D:/三国/视频.mp4"));
BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream("D:/三国/拷贝.mp4"));
int len;
byte[] data = new byte[8 * 1024];
while ((len = fis.read(data)) != -1) {
    fos.write(data, 0, len);
}
log.info("拷贝电影耗时:{}ms", System.currentTimeMillis() - start);
// 拷贝电影耗时:1946ms


由上述四个例子可以得出结论,缓冲流读取数据比普通流读取数据快很多!


注意:采用边读边写的方式,一次传输几兆的数据效率比较高,如果采用先把文件的数据都读入内存,在进行写出,这样读写的次数是较小,但是占用太大的内存空间,一次读太大的数据也严重影响效率!

相关文章
|
5月前
|
Java
【思维导图】JAVA网络编程思维升级:URL与URLConnection的逻辑梳理,助你一臂之力!
【思维导图】JAVA网络编程思维升级:URL与URLConnection的逻辑梳理,助你一臂之力!
69 1
|
2月前
|
Java
java 中 IO 流
Java中的IO流是用于处理输入输出操作的机制,主要包括字节流和字符流两大类。字节流以8位字节为单位处理数据,如FileInputStream和FileOutputStream;字符流以16位Unicode字符为单位,如FileReader和FileWriter。这些流提供了读写文件、网络传输等基本功能。
59 9
|
3月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
102 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
4月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
|
3月前
|
Java 数据处理 开发者
揭秘Java IO流:字节流与字符流的神秘面纱!
揭秘Java IO流:字节流与字符流的神秘面纱!
52 1
|
3月前
|
自然语言处理 Java 数据处理
Java IO流全解析:字节流和字符流的区别与联系!
Java IO流全解析:字节流和字符流的区别与联系!
128 1
|
4月前
|
Java 大数据 API
Java 流(Stream)、文件(File)和IO的区别
Java中的流(Stream)、文件(File)和输入/输出(I/O)是处理数据的关键概念。`File`类用于基本文件操作,如创建、删除和检查文件;流则提供了数据读写的抽象机制,适用于文件、内存和网络等多种数据源;I/O涵盖更广泛的输入输出操作,包括文件I/O、网络通信等,并支持异常处理和缓冲等功能。实际开发中,这三者常结合使用,以实现高效的数据处理。例如,`File`用于管理文件路径,`Stream`用于读写数据,I/O则处理复杂的输入输出需求。
271 12
|
3月前
|
Java
Java 中 IO 流的分类详解
【10月更文挑战第10天】不同类型的 IO 流具有不同的特点和适用场景,我们可以根据具体的需求选择合适的流来进行数据的输入和输出操作。在实际应用中,还可以通过组合使用多种流来实现更复杂的功能。
77 0
|
3月前
|
存储 Java 程序员
【Java】文件IO
【Java】文件IO
47 0
|
4月前
|
数据采集 Java 数据挖掘
Java IO异常处理:在Web爬虫开发中的实践
Java IO异常处理:在Web爬虫开发中的实践