1、IO流介绍

基本介绍:常使用的File类是用来描述文件或者文件夹的。File对象可以帮我们获取文件或者文件夹的一些属性数据,但是无法读取文件里面的数据,如果想读取文件的数据,那么需要IO流技术。
IO流技术:解决设备与设备之间数据的传输。如:硬盘---->内存、内存------->硬盘

1.1、IO流技术分类

IO流技术按照不同的功能或者需要可以分成不同的类别。
一、按照数据的流向分:输入流与输出流;
二、按照数据单位划分:字节流与字符流;
字节流的简介:字节流读取的数据都是文件中存储的二进制数据,并且读取的二进制数据不经过任何的加工处理;
字符流的简介:字符流读取的也是文件中的二进制数据,只不过读取的二进制数据按照使用的编码表解码成我们能识别的字符数据。所以可以说:字符流 = 字节流+解码

1.2、输入输出的判断

输入输出的判断,我们可以使用程序本身来当参照物
输入流:从文件中读取数据到程序中。以程序为参照物,数据流入程序内;
输出流:将程序中的数据写到文件中。以程序为参照物,数据流出程序;
下面上图来总结一下:

2、字节流

2.1、字节流分类

字节流按照数据的流向分为:输入字节流,输出字节流;

2.1.1、输入字节流(InputStream)

InputStream:是一个抽象类(不能实例化),是所有输入输入字节流的超类。它的子类有:ByteArrayInputStream, FileInputStream,ObjectInputStream, SequenceInputStream…这些不同的子类读取的对象不一样的。选着什么样的子类来操作目标对象完全是顾名思义的。举个例子:ByteArrayInputStream(字节数组输入流)是操作字节数组的、ObjectInputStream(对象的输入字节流)用来操作对象的。
接下来,主要以FileInputStream这个类的学习为例,其他的输入字节流的使用都是类似的。

2.1.1.1、文件输入字节流(FileInputStream)

首先看代码,FileInputStream是继承InputStream的。

public class FileInputStream extends InputStream

FileInputStream从文件系统中的某个文件中获取输入字节,至于具体是什么文件,取决于主机环境。用于读取图像之类的数据的原始字节流。
FileInputStream中使用一个read()方法来读取文件中的字节数据,返回一个0~255范围内的int数据,读取的时候是一个字节一个字节的读取。如果读取已经达到文件末尾,那个read方法返回-1
FileInputStream的使用步骤:
1、找到目标文件;
2、建立传输管道;
3、读取目标数据;
4、关闭传输管道;

2.1.1.2、读取文件方式一

public class InStream {public static void main(String[] args) throws IOException {getFileData01();}/*** 缺点:只能读取一个字节的数据,不能读取完文件数据*  read():从此输入流中读取下一个数据字节。返回一个 0 到 255 范围内的 int 字节值。*                    如果因为已经到达流末尾而没有字节可用,则返回 -1* @throws IOException*/public static void getFileData01() throws IOException {//1、找到目标文件;File file = new File("F:\\Web_project\\io_demo.txt");//2、建立传输管道;FileInputStream fileInputStream = new FileInputStream(file);// 3、读取目标数据;int content = fileInputStream.read();System.out.println("得到的内容是----》"+(char)content);// 关闭释放资源fileInputStream.close();}
}

这是是非常基础的,只返回一个字节的数据。
read()会抛出一个IO异常,是因为怕读取数据过程中传输中断;

int content = fileInputStream.read();

这是句代码是将读取到的字节数据返回到content当中,再经过强转content就可以了;
这个方法有一个致命的缺点:只能读取一个字节,不能读全文件,所以我们来看下一个方法;

2.1.1.3、读取文件方式二

public class InStream {public static void main(String[] args) throws IOException {getFileData02();}/*** 改良后的方法一,可以读全文件数据,但是在循环中,如果文件过大,效率较低* @throws IOException*/public static void getFileData02() throws IOException {File file = new File("F:\\Web_project\\io_demo.txt");FileInputStream fileInputStream = new FileInputStream(file);int content = 0;while ((content = fileInputStream.read()) != -1) {System.out.print((char)content);}// 关闭释放资源fileInputStream.close();}
}

方式二跟方式一样的地方:

        int content = 0;while ((content = fileInputStream.read()) != -1) {System.out.print((char)content);}

这一片代码,是将读取语句放入循环中读取,并将读取的值赋给content,进而判断content是否为-1来判断是否文件读取完;
read()方法的读取方式,和迭代器有些相识,也是获取一个相应的指针移到下一个元素,读取到末尾时,返回一个标识符;
接下来我们来看看带有缓冲数组的的读取,方式三。

2.1.1.4、读取文件方式三

public class InStream {public static void main(String[] args) throws IOException {getFileData03();}public static void getFileData03() throws IOException {// 1、找到目标文件;File file = new File("F:\\Web_project\\io_demo.txt");// 2、建立传输管道;FileInputStream fileInputStream = new FileInputStream(file);// 定义一个字节数组byte[] buff = new byte[10];// 3、读取目标数据;int length = fileInputStream.read(buff);System.out.println("读取到的文件内容---->"+new String(buff));System.out.println("读取到的长度----->"+length);// 4、关闭传输管道;fileInputStream.close();}
}

然后我们来聊一下这种方式的读取数据:
首先建立了一个缓冲字节数组byte[] buff = new byte[10];长度为10。再将这个数组作为实参放入read()方法当中。这样read()读取的数据就存放在buff数组里。

int length = fileInputStream.read(buff);

这里的返回值length为读入缓冲区的字节总数,如果因为已经到达文件末尾而没有更多的数据,则返回 -1。 也就是lengthread往数组里存放多少个二进制数据,当然长度不会超过数组本身的长度。
读取到的buf二进制数据经过new String(buf),由String本身的解码功能,解码成我们看得懂的字符串;
这种方式同样有致命的缺陷:不能读全文件,因为这个方法能读取文件的多少,是有缓冲数组的长度来决定的,总不能声明一个非常非常大的数组在那里占用空间吧。那么我们来看看,方式四。

2.1.1.5、读取文件方式四

public class InStream {public static void main(String[] args) throws IOException {getFileData04();}public static void getFileData04() throws IOException {File file = new File("F:\\Web_project\\io_demo.txt");FileInputStream fileInputStream = new FileInputStream(file);byte[] buff = new byte[1024];int length = 0;while ((length = fileInputStream.read(buff))!= -1) {System.out.println("解析出的文件内容--->"+new String(buff,0,length));}fileInputStream.close();}

方式四不用说也是方式三的升级版:

        int length = 0;byte[] buff = new byte[1024];while ((length = fileInputStream.read(buff))!= -1) {System.out.println("解析出的文件内容--->"+new String(buff,0,length));}

也是将读取数据的代码放入到了循环,如果read(buf)到达文件末尾并且没有文件可读,返回-1
这里要注意的点:

new String(buff,0,length)

一般都是这样子写的。至于原因嘛
是因为read方法将读取的二进制数据放入缓冲数组,当缓冲数组存储满的时候,read方法返回读入缓冲区的字节总数length。进行第二次读取的时候要注意了,read()方法并不是清空之前buf缓冲数组里的数据,而是覆盖缓冲数组里的数据,也就意味着,如果下一次读取时,剩余的文件数据小于缓冲数组长度,那么在缓冲数组的倒数的几个位置会存在上一次读取的文件数据。

2.1.1.6、总结

我们来总结一下,只有方式二和方式四能读全文件,
要点一:方式四的效率是要远大于方式二的
举个例子:好像去井里打水,一个是拿到一瓢水就跑回家,将这一瓢水倒在水缸里后再返回往井里打水。而另一个是将一瓢一瓢的水倒在水桶里,等水桶装满了,才拎着水桶倒家里再返回往井里打水。这当然是用水桶的效率高。
要点二:read()方法使用缓冲数组时,也是一个字节一个字节的读取的,只不过读取的数据是先存放在缓冲数组里而已。
要点三:read方法再次向缓冲数组里读入数据时,并不是先清空缓冲数组里的数据,而是覆盖。这也正是为什么read要返回:读入缓冲区的字节总数

2.1.2、输出字符流(OutputStream)

OutputStream:抽象类是表示输出字节流的所有类的超类,它也有很多的子类ByteArrayOutputStream, FileOutputStream, ObjectOutputStream,也同样了解FileOutputStream的使用,其他子类的使用方式也是差不多的。

2.1.2.1、文件输出字符流(FileOutputStream)

看代码,是继承了OutputStream,是OutputStream的亲儿子。

public class FileOutputStream extends OutputStream

FileOutputStream:文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流, 用于写入诸如图像数据之类的原始字节的流。
FileOutputStream的使用步骤与FileInputStream一样:
1、找到目标文件;
2、建立传输管道;
3、读取目标数据;
4、关闭传输管道;

2.1.2.2、写数据方式一

public class OutStream {public static void main(String[] args) throws IOException {writeInfo01();}public static void writeInfo01() throws IOException {File file = new File("F:\\Web_project\\out_demo.txt");FileOutputStream fileOutputStream = new FileOutputStream(file);fileOutputStream.write(97);// 释放资源fileOutputStream.close();}

这方法只写一个int类型的数据97(也就是a)
好了我们看看加了缓冲数组的方式二

2.1.2.3、写数据方式二

public class OutStream {public static void main(String[] args) throws IOException {writeInfo03();}public static void writeInfo03() throws IOException {File file = new File("F:\\Web_project\\out_demo.txt");FileOutputStream fileOutputStream = new FileOutputStream(file,true);String msg = "hello word ";byte[] buf = msg.getBytes();fileOutputStream.write(buf);fileOutputStream.close();}

看看一片代码:

        String msg = "hello word ";byte[] buf = msg.getBytes();fileOutputStream.write(buf);

将String的msg转成字节数组,再将字节数组作为实参传给write方法。当然write也是一个字节一个字节的写。

3、字节流的异常处理

上面的所有例子都是直接抛的异常,但这并不太是正确的姿势,下面我上一份拷贝文件的代码:

public class CopyPictureExection {public static void main(String[] args) {copyPic();}public static void copyPic() {FileInputStream fileInputStream = null;FileOutputStream fileOutputStream = null;try {File inFile = new File("F:\\Web_project\\info.txt");File outFile = new File("F:\\Web_project\\Bagua\\记事本.txt");fileInputStream = new FileInputStream(inFile);fileOutputStream = new FileOutputStream(outFile);byte[] buf = new byte[1024];int length = 0;while ((length = fileInputStream.read(buf)) != -1) {fileOutputStream.write(buf, 0, length);}}catch (IOException e) {throw new RuntimeException(e);}finally {try {// 先声明的后关闭,后声明的先关闭if (fileOutputStream != null) {fileOutputStream.close();}} catch (IOException e) {throw new RuntimeException(e);}finally {try {if (fileInputStream != null) {fileInputStream.close();}} catch (IOException e) {throw new RuntimeException(e);}}}}
}

代码上完了,接下来分别讲一些要点:
要点一:关闭流时遵循先声明的后关闭,后声明的先关闭

         fileInputStream = new FileInputStream(inFile);fileOutputStream = new FileOutputStream(outFile);

分别声明了一个输入流,一个输出流。由于先声明的是fileInputStream输入流,所以关闭流的时候先关闭输,再关闭输出流;
要点二:流使用完是必须得关闭的
如果不关闭的话,将一直占用某个资源,是其他代码无法操作那个资源。

4、编码表的内容

使用字节流来读取中文的时候会出现乱码,这是因为一个中文是两个字节,而字节流读取数据这是一个字节一个字节进行的。也就是说只能读取中文的一般,显现出来的就是乱码了。

4.1、码表

计算机中是只存储二进制的,但是我们看到的却是各种认识的文字,是因为将这些文字一一的使用二进制来表示,这些一一对应所形成的一张表,成为码表。各个国家的语言不太一样,所以码表也不一样。
比如美国的码表为:ASCII,一个英文用一个字节表示。中国的GBK,一个中文用两个字节表示;
一般来说英文是比较通用的,因为在各种不同的码表中,英文都是使用一个字节表示的;
常见的码表
ASCII: 美国标准信息交换码。用一个字节的7位可以表示。
ISO8859-1: 拉丁码表。欧洲码表,用一个字节的8位表示。又称Latin-1(拉丁编码)或“西欧语言”。ASCII码是包含的仅仅是英文字母,并且没有完全占满256个编码位置,所以它以ASCII为基础,在空置的0xA0-0xFF的范围内,加入192个字母及符号,
藉以供使用变音符号的拉丁字母语言使用。从而支持德文,法文等。因而它依然是一个单字节编码,只是比ASCII更全面。
GBK: 中国的中文编码表升级,融合了更多的中文文字符号。
UTF-8: 万国码表,国际性码表,融合了各个国家的码表,英文还是一个字节,最多用三个字节来表示一个中文字符。
UTF-16: 不管英文中文都是占两个字节。

4.2、编码

字符串—>字节数组
String类的getBytes() 方法进行编码,将字符串,转为对映的二进制,并且这个方法可以指定编码表。如果没有指定码表,该方法会使用操作系统默认码表。
注意:中国大陆的Windows系统上默认的编码一般为GBK。在Java程序中可以使用System.getProperty(“file.encoding”)方式得到当前的默认编码。

4.3、解码

字节数组—>字符串
String类的构造函数完成。
String(byte[] bytes) 使用系统默认码表
String(byte[],charset)指定码表
注意:我们使用什么字符集(码表)进行编码,就应该使用什么字符集进行解码,否则很有可能出现乱码(兼容字符集不会)。

5、字符流

下面来看看字符流。我们知道用字节流读取中文会出现乱码,字符流就正好可以用来读取文本的数据。之所以字符流读取不会出现乱码,是因为字符流读取是一个字符一个字符的读取的。
可以完全操作字节流的经验来了解字符流

5.1、字符流分类

字符流可以分为输入字符流和输出字符流。

5.2、输入字符流(Reader)

Reader是输入字符流的超类,其子类有:BufferedReader, CharArrayReader…
这里以FileReader为例:

public class ReaderStream {public static void main(String[] args) {readMsg();}public static void readMsg() {FileReader fileReader = null;try {File file = new File("F:\\newspace\\IoExectionDeal.java");fileReader = new FileReader(file);int length = 0;char[] buf = new char[9];while ((length = fileReader.read(buf)) != -1) {System.out.print(new String(buf,0,length));}}catch (IOException e) {throw new RuntimeException(e);}finally {try {if (fileReader != null) {fileReader.close();}} catch (IOException e) {throw new RuntimeException(e);}}}}

之前讲了字节流无论是读写文件数据都是一个字节一个字节的读取,也就是这样才导致读取的中文有乱码存在。而字符流是一个字符一个字符的读取数据。除此之外,用法也都差不多的,这也是流技术容易学习的地方。

         char[] buf = new char[9];while ((length = fileReader.read(buf)) != -1) {System.out.print(new String(buf,0,length));}

只不过这里的缓冲数组不再是字节数组,而是字符数组。while循环结构和字节流是一样的。

5.3、输出字符流(Writer)

Writer是所有输出字符流的超,这里也以为例:

public class WriterStream {public static void main(String[] args) {writeMsg();}public static void writeMsg() {FileWriter fileWriter = null;try {File file = new File("F:\\Web_project\\out_demo.txt");fileWriter = new FileWriter(file);String msg = "大家好啊!";fileWriter.write(msg);fileWriter.flush();} catch (IOException e) {throw new RuntimeException(e);}finally {try {if (fileWriter != null) {fileWriter.close();System.out.println("输出字符流关闭成功...");}} catch (IOException e) {throw new RuntimeException(e);}}}
}

用法也和输出字节流一样,稍不一样的是,这里是可以直接写字符串的;
来看一下之前的输出字节流的用法:

     String msg = "hello word ";byte[] buf = msg.getBytes();

需要将字符串通过msg.getBytes(),getBytes方法编码成字节数据后才能写入数据;
再看输出字符流:

         String msg = "大家好啊!";fileWriter.write(msg);

嗯,,,直接写的字符串。
嗯,,,由于篇幅的关系,关于流的部分就愉快的先到这里吧。
下一篇继续接受一下流的一点点延伸的部分:缓冲字节流,以及缓冲字符流,还有序列(SequenceInputStream)

对InputStream,OutputStream,Reader,Writer的详解相关推荐

  1. Golang bufio Reader 源码详解

    Golang - bufio Reader详解 bufio 是对 gosdk 种 io.Reader 和 io.writer 的二次打包,以 []byte 作为其 buffer,在 io.Reader ...

  2. Java I/O流-总结(InputStream,OutputStream,Reader,Writer)

    Java流总结 一. 流的分类 • 按数据流动方向 – 输入流:只能从中读取字节数据,而不能向其写出数据 – 输出流:只能向其写入字节数据,而不能从中读取数据 • 按照流所处理的数据类型 – 字节流: ...

  3. Java I/O流InputStream,OutputStream,Reader,Writer

    Java流总结 原文地址:http://blog.csdn.net/oypj2010/article/details/7660150 一. 流的分类 – 输入流:只能从中读取字节数据,而不能向其写出数 ...

  4. java IO流(一)文件, InputStream,OutputStream,Reader,Writer

    JAVA IO流(一) (一) 文件 1.概念 文件就是保存数据的地方 文件流 文件在程序中是以流的形式来操作的. java程序(内存) <--------> 文件(磁盘) 输入,输出是针 ...

  5. InputStream read()方法详解

    在Java7中,InputStream被定义为一个抽象类,相应的,该类下的read()方法也是一个抽象方法,这也就意味着必须有一个类继承InputStream并且实现这个read方法. 查阅Java7 ...

  6. java的requestmapping_SpringMVC RequestMapping 详解

    SpringMVC RequestMapping 详解 RequestMapping这个注解在SpringMVC扮演着非常重要的角色,可以说是随处可见.它的知识点很简单.今天我们就一起学习Spring ...

  7. sstream类的详解

     sstream类的详解             sstream类详细介绍 C++引入了ostringstream.istringstream.stringstream这三个类,要使用他们创建对象 ...

  8. SpringMVC学习:控制层(Controller)基于注解详解

    文章目录 一.URL映射Controller的方法返回值 二.SpringMVC各类注解详解 (一) @Controller (二) @RequestMapping 1.基本用法 2. path属性或 ...

  9. Java 装饰器模式详解

    转载请注明出处:http://blog.csdn.net/zhaoyanjun6/article/details/56488020 前言 在上面的几篇文章中,着重介绍了java 中常见的 IO 相关知 ...

  10. java按照io流向基类_Java IO详解

    1 Java IO流的概念,分类 1.1 Java IO流的概念 java的IO是实现输入和输出的基础,可以方便的实现数据的输入和输出操作.在java中把不同的输入/输出源(键盘,文件,网络连接等)抽 ...

最新文章

  1. 工控随笔_09_西门子_S7-200 Smart与V20 USS通信USS_RPM_R利用轮询的方式通讯异常
  2. 48.结构体位域获取内存模型
  3. 单一职责原则(SRP)
  4. 常见的正则表达式验证(更新中)
  5. 计算机软件工作总结,计算机软件培训总结及小结-工作总结
  6. 215. Kth Largest Element in an Array 数组中的第K个最大元素
  7. 使用 StatsD + InfluxDB + Grafana 搭建 Node.js 监控系统 (二)
  8. jQuery学习笔记(五)
  9. 在线预览(pptx、ppt、pps、docx、doc、xlsx、xls)
  10. 07-netty之Socket
  11. 【题解】生日蛋糕-C++
  12. python是什么类型的编程语言-python和scratch有什么区别
  13. pytorch不加载fc_PyTorch | 保存和加载模型
  14. 单词首字母大写 (5分)
  15. TFTP文件传输NFS挂载ARM开发板移植Linux系统步骤
  16. 树莓派3b no wireless interfaces found 的解决办法 360wifi和树莓派结合
  17. matlab里面求出两条曲线的交点问题
  18. Fibonacci数列Linux程序,使用fork()调用计算Fibonacci数列
  19. 禅道mysql怎么在本地配置_本地安装部署禅道
  20. 服务器类型有哪些如何选择

热门文章

  1. 小米9pro计算机打不开,小米9pro怎么连接电脑
  2. 3轴陀螺仪传感器和3轴加速度传感器的工作原理
  3. dis的前缀单词有哪些_前缀dis-dis和un和im前缀-un和dis前缀的区别
  4. 计算机 网络发现不了电脑,win10网络发现已关闭网络计算机和设备不可见怎么办?...
  5. 《AI·未来》 ---- 读书笔记
  6. discards qualifiers —— 丢弃类型
  7. 简单基础的原生JS实现图片上传添加
  8. 智能运维 | 几十万台服务器,保证一条命令执行到位有多难?
  9. 如何应对硬盘无法识别通电异响等那些七七八八的物理故障
  10. C#如何在EPPlus中冻结首行,冻结Excel首行