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);
        }
    }
}


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