Java难点重构-IO(下)

简介: 在java 中有输入,输出两种 IO 流,每种输入,输出又分为字节流和字符流两大类。字节又是什么呢,每个字节(byte)右8bit 组成,每种数据类型又几个字节组成。

流的概念模型


Java 的IO流共设计 40 多个类,这些类看上去复杂,但实际上非常规则,而且彼此之间存在非常紧密的联系。它们都是从如下4 个抽象基类派生的。


  • InputStream/Reader:所有输入流的基类,前者是字节输出流,后者是字符输出流
  • OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流。

InputStream/Reader

对于InPutStream 和Reader 而言,它们把输入设备抽象成一个水管,这个水管里的每个水滴一次排列,如图



字节流和字符流的处理方式其实非常相似,只是它们处理的输入/输出单位不同而言。**输入流使用隐式的记录指针来表示 **当前正准备从哪个“水滴”开始读取,每当程序从 InputStream或 Reader 里取出一个或多个 “水滴” 后,记录指针自动向后移动;除此之外,InputSteam 和 Reader里都提供了一些方法来控制记录指针的移动。

OutputStream/Writer

对于OutputStream 和 Writer而言,它们同样把输出设备抽象成一个水管,只是这个水管里没有任何水滴。



当执行输出时,程序相当于依次把 “水滴” 放入到输出流的水管中,输出流同样采用隐式的记录指正来标识当前属地即将放入的位置,每当程序向 OutPutStream 或 Writer 里输出一个或多个水滴后,记录指针自动向后移动。


以上就是Java IO流的基本概念模型,除此之外,Java 的处理流模型则体现了 Java 输入/输出流设计的灵活性。处理流的功能主要体现在以下方面:


  • 性能的提高:主要以增加缓冲的方式来提高输入/输出的效率。
  • 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入/输出大批量的内容,而不是输入/输出一个或多个水滴


处理流可以 嫁接 在任何已存在的流的基础之上,这就允许 java 应用程序采用相同的代码,透明的方式来访问不同的输入,输出设备的数据流。



通过使用处理流,Java 程序无须理会 输入/输出 节点是磁盘,网络还是其他的输入/输出设备,程序只要讲这些节点包装成处理流,就可以使用相同的输入/输出代码来读写不同的输入/输出设备的数据。

InputStream,Reader

InputStream和Reader 都是将输入数据抽象成一条水管,所以程序既可以通过 read() 方法每次读取一个 水滴,也可以通过 read(char[] cbuf)或rea(byte[] b)方法来读取多个 “水滴”。当使用数组作为 read() 方法的参数时,可以理解为使用一个 “竹筒”到水管去取水,而read(char[] cbuf)方法中的数组就相当于一个 “竹筒” ,程序每次调用输入流的 read(char[] cbuf)或read(byte[] b)方法,就相当于用 “竹筒”从输入流中取出一筒 ”水滴“,程序得到 “竹筒” 里的 “水滴” 后,转换成相应的数据即可;程序多次重复这个取水过程,直到 read(char[] cbuf)或read(byte[] b)方法返回-1,表明到了输入流的结束点。内部每次又使用指针移动取水的下标。


InputStream是所有输入字节流的父类,是一个抽象类,主要包含三个方法

 //读取一个字节并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。 
int read() ; 
//读取一系列字节并存储到一个数组buffer,返回实际读取的字节数,如果读取前已到输入流的末尾返回-1。 
int read(byte[] buffer) ; 
//读取length个字节并存储到一个字节数组buffer,从off位置开始存,最多len, 返回实际读取的字节数,如果读取前以到输入流的末尾返回-1。 
int read(byte[] buffer, int off, int len) ;

Demo

public class Test {
    public static void main(String[] args) throws IOException {
        try{
            //创建字节输入流
            FileInputStream fis=new FileInputStream("E:\\Android\\Mvp_Test\\app\\src\\main\\java\\com\\example\\pettepr\\mvp_test\\aaa.txt");
            //创建一个长度为32的字节数组
            byte[] bbuf=new byte[32];
            //用于保存实际读取的字节数
            int hasRead=0;
            while ((hasRead=fis.read(bbuf))>0){
                System.out.println(new String(bbuf,0,hasRead));
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

解决中文乱码问题

//创建字节输入流
FileInputStream fis = new FileInputStream("E:\\Android\\Mvp_Test\\app\\src\\main\\java\\com\\example\\pettepr\\mvp_test\\aaa.txt");
InputStreamReader input = new InputStreamReader(fis, "GBK");
BufferedReader bbuf = new BufferedReader(input);
String next;
while ((next=bbuf.readLine())!=null) {
    System.out.println(next);
}

InputStream与Reader最大不同,一个是操作字节,一个是操作字符


除了上面的方法之外,InputStream和Reader还支持如下方法来移动流中的指针位置:

//在此输入流中标记当前的位置
//readlimit - 在标记位置失效前可以读取字节的最大限制。
void mark(int readlimit)
// 测试此输入流是否支持 mark 方法
boolean markSupported()
// 跳过和丢弃此输入流中数据的 n 个字节/字符
long skip(long n)
//将此流重新定位到最后一次对此输入流调用 mark 方法时的位置
void reset()
OutputStream 和 Writer

OutputStream 是所有的输出字节流的父类,它是一个抽象类,主要包含如下4个方法:

//向输出流中写入一个字节数据,该字节数据为参数b的低8位。 
void write(int b) ; 
//将一个字节类型的数组中的数据写入输出流。 
void write(byte[] b); 
//将一个字节类型的数组中的从指定位置(off)开始的,len个字节写入到输出流。 
void write(byte[] b, int off, int len); 
//将输出流中缓冲的数据全部写出到目的地。 
void flush();

Writer 是所有的输出字符流的父类,它是一个抽象类,主要包含如下六个方法:

//向输出流中写入一个字符数据,该字节数据为参数b的低16位。 
void write(int c); 
//将一个字符类型的数组中的数据写入输出流, 
void write(char[] cbuf) 
//将一个字符类型的数组中的从指定位置(offset)开始的,length个字符写入到输出流。 
void write(char[] cbuf, int offset, int length); 
//将一个字符串中的字符写入到输出流。 
void write(String string); 
//将一个字符串从offset开始的length个字符写入到输出流。 
void write(String string, int offset, int length); 
//将输出流中缓冲的数据全部写出到目的地。 
void flush()

可以看出,Writer比OutputStream多出两个方法,主要是支持写入字符和字符串类型的数据。


Demo

public class Test {
    public static void main(String[] args) throws IOException {
        try{
            //创建字节输入流
            FileInputStream fis=new FileInputStream("E:\\Android\\Mvp_Test\\app\\src\\main\\java\\com\\example\\pettepr\\mvp_test\\Test.java");
            //创建字节输出流
            FileOutputStream fos=new FileOutputStream("E:\\Android\\Mvp_Test\\app\\src\\main\\java\\com\\example\\pettepr\\mvp_test\\aaa.txt",true);  //true指将内容添加到原内容后面
             byte[] buuf=new byte[32];
            int hashRead=0;
            //循环从输入流中取出数据
            while((hashRead=fis.read(buuf))>0){
                fos.write(buuf,0,hashRead);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

使用Java的IO流执行输出时,不要忘记关闭输出流,关闭输出流除了可以保证流的物理资源被回收之外,还能将输出流缓冲区的数据flush到物理节点里(因为在执行close()方法之前,自动执行输出流的flush()方法)

处理流

处理流可以隐藏底层设备上节点流的差异,并对外提供更加方便的输入/输出方法,让程序员只需关心高级流的操作。


使用处理流时的典型思路是,使用处理流老包装节点流,程序通过处理流来执行输入输出功能,让节点流与底层的 I/O设备,文件交互。

它的优势为以下两点:


  1. 对开发人员来说,使用处理流进行输入、输出操作更简单
  2. 使用处理流效率更高
public class Test {
    public static void main(String[] args){
        try{
            FileOutputStream fos=new FileOutputStream("E:\\Android\\Mvp_Test\\app\\src\\main\\java\\com\\example\\pettepr\\mvp_test\\aaa.txt");
            PrintStream ps=new PrintStream(fos);
             //使用PrintStream执行输出
            ps.println("Petterp");
        }catch (Exception e){
        }
    }
}

转换流

转换流用于实现将字节流转换成字符流。


IntputStreamReader 将字节输入流转换成字符输入流,OutputStreamWriter 将字节输出流转换成字符输出流。

为什么没有把字符流转成字节流的呢?

字节流比字符流的应用范围更广,但字符流比字节流操作方面。如果有一个流已经是字符流了,也就是说,是一个用起来更方便的流,为什么要转换成字节流呢?反之,如果现在有一个字节流,但可以确定这个字节流的内容都是文本内容,那么把他转为字符流来处理就会更方便一些,所以 java只提供了 将字节流转换成字符流的转换。

public class Test {
    public static void main(String[] args) throws IOException {
        //创建字节输入流
        FileInputStream fis = new FileInputStream("E:\\Android\\Mvp_Test\\app\\src\\main\\java\\com\\example\\pettepr\\mvp_test\\aaa.txt");
        InputStreamReader input = new InputStreamReader(fis, "GBK");
        //将普通的 Reader包装成 BufferedReader
        BufferedReader bbuf = new BufferedReader(input);
        String next;
        //readLine() 每次读入一行
        while ((next=bbuf.readLine())!=null) {
            System.out.println(next);
        }
    }
}


目录
相关文章
|
2天前
|
设计模式 Java 开发者
Java中的代码优雅重构实战
Java中的代码优雅重构实战
|
3天前
|
存储 Java
杭州 java IO流详解(借鉴-侵-删)
杭州 java IO流详解(借鉴-侵-删)
7 0
|
4天前
|
Java 数据处理 开发者
Java IO流专家级教程:深入理解InputStream/OutputStream和Reader/Writer的内部机制
【6月更文挑战第26天】Java IO流涉及字节流(InputStream/OutputStream)和字符流(Reader/Writer),用于高效处理数据输入输出。InputStream/OutputStream处理二进制数据,常使用缓冲提升性能;Reader/Writer处理文本,关注字符编码转换。两者都有阻塞IO操作,但Java NIO支持非阻塞。示例代码展示了如何使用FileInputStream/FileOutputStream和FileReader/FileWriter读写文件。理解这些流的内部机制有助于优化代码性能。
|
4天前
|
存储 自然语言处理 Java
Java IO流完全手册:字节流和字符流的常见应用场景分析!
【6月更文挑战第26天】Java IO流涵盖字节流和字符流,字节流用于二进制文件读写及网络通信,如图片和音频处理;字符流适用于文本文件操作,支持多语言编码,确保文本正确性。在处理数据时,根据内容类型选择合适的流至关重要。
|
4天前
|
Java 开发者
Java IO流实战技巧:如何优化InputStream/OutputStream和Reader/Writer的使用?
【6月更文挑战第26天】Java IO流优化涉及缓冲、资源管理、字符编码和流式处理。使用Buffered流提高读写效率,如`BufferedInputStream`和`BufferedReader`。确保资源关闭使用try-with-resources,如`try (InputStream is = ...) {...}`。处理文本时指定编码,如`InputStreamReader(is, StandardCharsets.UTF_8)`防止乱码。流式处理大文件,分块读写避免内存溢出,以减少内存占用。这些技巧能提升程序性能和健壮性。
|
4天前
|
自然语言处理 Java
Java IO流进阶教程:掌握字节流和字符流的高级用法!
【6月更文挑战第26天】Java IO流助你高效交换数据,包括字节流(InputStream/OutputStream)和字符流(Reader/Writer)的高级技巧。缓冲流(Buffered*)提升读写性能,对象流(Object*Stream)支持对象序列化。字符流的BufferedReader/BufferedWriter优化文本处理,注意字符集如UTF-8用于编码转换。掌握这些,优化IO操作,提升代码质量。
|
4天前
|
Java
Java IO流终极指南:从InputStream/OutputStream到Reader/Writer的全面解读
【6月更文挑战第26天】Java IO流涵盖字节流(InputStream/OutputStream)和字符流(Reader/Writer),前者处理二进制数据,后者专司文本。例如,FileInputStream/FileOutputStream用于文件的字节级读写,而FileReader/FileWriter处理字符级文本。Buffered流提供缓冲功能,提升效率。选择合适的流类取决于数据类型和性能需求。
|
2月前
|
存储 Java 数据安全/隐私保护
从零开始学习 Java:简单易懂的入门指南之IO字符流(三十一)
从零开始学习 Java:简单易懂的入门指南之IO字符流(三十一)
|
4天前
|
Java 数据处理 开发者
揭秘Java IO流:字节流与字符流的神秘面纱!
【6月更文挑战第26天】Java IO流涵盖字节流和字符流,字节流处理二进制数据,如图像,由InputStream/OutputStream家族管理;字符流处理文本,基于Reader/Writer,适于文本文件。在文件复制示例中,字节流用FileInputStream/FileOutputStream,字符流用FileReader/FileWriter。选择流类型取决于数据类型和处理需求,文本文件优选字符流,二进制数据则选字节流。
|
4天前
|
Java 测试技术
Java IO流深度剖析:字节流和字符流的性能对比!
【6月更文挑战第26天】Java IO流分字节流和字符流,字节流处理所有类型数据(如图片),字符流处理文本(基于Unicode)。字节流直接处理,性能高,适合非文本文件;字符流处理文本时考虑编码,适合文本文件。性能测试显示,字节流在读写非文本文件时更快,而字符流在处理文本时更方便。选择流类型应依据数据类型和需求。