编程语言
首页 > 编程语言> > JavaIo流

JavaIo流

作者:互联网

1.Java Io流的概念,分类,类图。

1.1 Java Io流的概念

    java的io是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。在java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为“流”(stream)。通过流的形式允许java程序使用相同的方式来访问不同的输入/输出源。stram是从起源(source)到接收的(sink)的有序数据。

注:java把所有的传统的流类型都放到在java io包下,用于实现输入和输出功能。

1.2 Io流的分类:

按照不同的分类方式,可以把流分为不同的类型。常用的分类有三种:

1.2.1 按照流的流向分,可以分为输入流和输出流。

    此处的输入,输出涉及一个方向的问题,对于如图15.1所示的数据流向,数据从内存到硬盘,通常称为输出流——也就是说,这里的输入,输出都是从程序运行所在的内存的角度来划分的。

注:如果从硬盘的角度来考虑,图15.1所示的数据流应该是输入流才对;但划分输入/输出流时是从程序运行所在的内存的角度来考虑的,因此如图15.1所在的流时输出流。而不是输入流。

    对于如图15.2所示的数据流向,数据从服务器通过网络流向客户端,在这种情况下,Server端的内存负责将数据输出到网络里,因此Server端的程序使用输出流;Client端的内存负责从网络中读取数据,因此Client端的程序应该使用输入流。

这是图片描述

注:java的输入流主要是InputStream和Reader作为基类,而输出流则是主要由outputStream和Writer作为基类。它们都是一些抽象基类,无法直接创建实例。


1.2.2 按照操作单元划分,可以划分为字节流和字符流。

    字节流和字符流的用法几乎完成全一样,区别在于字节流和字符流所操作的数据单元不同,字节流操作的单元是数据单元是8位的字节,字符流操作的是数据单元为16位的字符。

字节流主要是由InputStream和outPutStream作为基类,而字符流则主要有Reader和Writer作为基类。


1.2.3 按照流的角色划分为节点流和处理流。

    可以从/向一个特定的IO设备(如磁盘,网络)读/写数据的流,称为节点流。节点流也被称为低级流。图15.3显示了节点流的示意图。 
    从图15.3中可以看出,当使用节点流进行输入和输出时,程序直接连接到实际的数据源,和实际的输入/输出节点连接。 
处理流则用于对一个已存在的流进行连接和封装,通过封装后的流来实现数据的读/写功能。处理流也被称为高级流。图15.4显示了处理流的示意图。

这里写图片描述

    从图15.4可以看出,当使用处理流进行输入/输出时,程序并不会直接连接到实际的数据源,没有和实际的输入和输出节点连接。使用处理流的一个明显的好处是,只要使用相同的处理流,程序就可以采用完全相同的输入/输出代码来访问不同的数据源,随着处理流所包装的节点流的变化,程序实际所访问的数据源也相应的发生变化。

1.3 流的原理浅析和常用的流的分类表:

1.3.1 流的原理浅析:

    java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java Io流的40多个类都是从如下4个抽象类基类中派生出来的。

   对于InputStream和Reader而言,它们把输入设备抽象成为一个”水管“,这个水管的每个“水滴”依次排列,如图15.5所示: 
    从图15.5可以看出,字节流和字符流的处理方式其实很相似,只是它们处理的输入/输出单位不同而已。输入流使用隐式的记录指针来表示当前正准备从哪个“水滴”开始读取,每当程序从InputStream或者Reader里面取出一个或者多个“水滴”后,记录指针自定向后移动;除此之外,InputStream和Reader里面都提供了一些方法来控制记录指针的移动。

这里写图片描述

   对于OutputStream和Writer而言,它们同样把输出设备抽象成一个”水管“,只是这个水管里面没有任何水滴,如图15.6所示:

   正如图15.6所示,当执行输出时,程序相当于依次把“水滴”放入到输出流的水管中,输出流同样采用隐示指针来标识当前水滴即将放入的位置,每当程序向OutputStream或者Writer里面输出一个或者多个水滴后,记录指针自动向后移动。 
   图15.5和图15.6显示了java Io的基本概念模型,除此之外,Java的处理流模型则体现了Java输入和输出流设计的灵活性。处理流的功能主要体现在以下两个方面。

    处理流可以“嫁接”在任何已存在的流的基础之上,这就允许Java应用程序采用相同的代码,透明的方式来访问不同的输入和输出设备的数据流。图15.7显示了处理流的模型。

这里写图片描述


1.3.2 java输入/输出流体系中常用的流的分类表

分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter
访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
访问管道PipedInputStreamPipedOutputStreamPipedReaderPipedWriter
访问字符串  StringReaderStringWriter
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
转换流  InputStreamReaderOutputStreamWriter
对象流ObjectInputStreamObjectOutputStream  
抽象基类FilterInputStreamFilterOutputStreamFilterReaderFilterWriter
打印流 PrintStream PrintWriter
推回输入流PushbackInputStream PushbackReader 
特殊流DataInputStreamDataOutputStream  

注:表中粗体字所标出的类代表节点流,必须直接与指定的物理节点关联:斜体字标出的类代表抽象基类,无法直接创建实例。

2.常用的io流的用法

下面是整理的常用的Io流的特性及使用方法,只有清楚每个Io流的特性和方法。才能在不同的需求面前正确的选择对应的IO流进行开发。

2.1 Io体系的基类(InputStream/Reader,OutputStream/Writer)。

字节流和字符流的操作方式基本一致,只是操作的数据单元不同——字节流的操作单元是字节,字符流的操作单元是字符。所以字节流和字符流就整理在一起了。

InputStream和Reader是所有输入流的抽象基类,本身并不能创建实例来执行输入,但它们将成为所有输入流的模板,所以它们的方法是所有输入流都可使用的方法。 
在InputStream里面包含如下3个方法。

在Reader中包含如下3个方法。

InputStream和Reader提供的一些移动指针的方法:

OutputStream和Writer: 
OutputStream和Writer的用法也非常相似,它们采用如图15.6所示的模型来执行输入,两个流都提供了如下三个方法:

因为字符流直接以字符作为操作单位,所以Writer可以用字符串来代替字符数组,即以String对象作为参数。Writer里面还包含如下两个方法。

2.2 Io体系的基类文件流的使用(FileInputStream/FileReader ,FileOutputStream/FileWriter)

   前面说过InputStream和Reader都是抽象类,本身不能创建实例,但它们分别有一个用于读取文件的输入流:FileInputStream和FileReader,它们都是节点流——会直接和指定文件关联。下面程序示范使用FileInputStream和FileReader。 
使用FileInputStream读取文件:


   
  1. public class MyClass {
  2. public static void main(String[] args)throws IOException{
  3. FileInputStream fis= null;
  4. try {
  5. //创建字节输入流
  6. fis= new FileInputStream( "E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\Test.txt");
  7. //创建一个长度为1024的竹筒
  8. byte[] b= new byte[ 1024];
  9. //用于保存的实际字节数
  10. int hasRead= 0;
  11. //使用循环来重复取水的过程
  12. while((hasRead=fis.read(b))> 0){
  13. //取出竹筒中的水滴(字节),将字节数组转换成字符串进行输出
  14. System. out.print( new String(b, 0,hasRead));
  15. }
  16. } catch (IOException e){
  17. e.printStackTrace();
  18. } finally {
  19. fis.close();
  20. }
  21. }
  22. }

注:上面程序最后使用了fis.close()来关闭该文件的输入流,与JDBC编程一样,程序里面打开的文件IO资源不属于内存的资源,垃圾回收机制无法回收该资源,所以应该显示的关闭打开的IO资源。Java 7改写了所有的IO资源类,它们都实现了AntoCloseable接口,因此都可以通过自动关闭资源的try语句来关闭这些Io流。

使用FileReader读取文件:


   
  1. public class FileReaderTest {
  2. public static void main(String[] args)throws IOException{
  3. FileReader fis= null;
  4. try {
  5. //创建字节输入流
  6. fis= new FileReader( "E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\Test.txt");
  7. //创建一个长度为1024的竹筒
  8. char[] b= new char[ 1024];
  9. //用于保存的实际字节数
  10. int hasRead= 0;
  11. //使用循环来重复取水的过程
  12. while((hasRead=fis.read(b))> 0){
  13. //取出竹筒中的水滴(字节),将字节数组转换成字符串进行输出
  14. System. out.print( new String(b, 0,hasRead));
  15. }
  16. } catch (IOException e){
  17. e.printStackTrace();
  18. } finally {
  19. fis.close();
  20. }
  21. }
  22. }

可以看出使用FileInputStream和FileReader进行文件的读写并没有什么区别,只是操作单元不同而且。

FileOutputStream/FileWriter是Io中的文件输出流,下面介绍这两个类的用法。

FileOutputStream的用法:


   
  1. public class FileOutputStreamTest {
  2. public static void main(String[] args)throws IOException {
  3. FileInputStream fis= null;
  4. FileOutputStream fos= null;
  5. try {
  6. //创建字节输入流
  7. fis= new FileInputStream( "E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\Test.txt");
  8. //创建字节输出流
  9. fos= new FileOutputStream( "E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\newTest.txt");
  10. byte[] b= new byte[ 1024];
  11. int hasRead= 0;
  12. //循环从输入流中取出数据
  13. while((hasRead=fis.read(b))> 0){
  14. //每读取一次,即写入文件输入流,读了多少,就写多少。
  15. fos.write(b, 0,hasRead);
  16. }
  17. } catch (IOException e){
  18. e.printStackTrace();
  19. } finally {
  20. fis.close();
  21. fos.close();
  22. }
  23. }
  24. } 运行程序可以看到输出流指定的目录下多了一个文件:newTest.txt, 该文件的内容和Test.txt文件的内容完全相同。FileWriter的使用方式和FileOutputStream基本类似,这里就带过。

注: 使用java的io流执行输出时,不要忘记关闭输出流,关闭输出流除了可以保证流的物理资源被回收之外,可能还可以将输出流缓冲区中的数据flush到物理节点中里(因为在执行close()方法之前,自动执行输出流的flush()方法)。java很多输出流默认都提供了缓存功能,其实我们没有必要刻意去记忆哪些流有缓存功能,哪些流没有,只有正常关闭所有的输出流即可保证程序正常。

缓冲流的使用(BufferedInputStream/BufferedReader, BufferedOutputStream/BufferedWriter): 
下面介绍字节缓存流的用法(字符缓存流的用法和字节缓存流一致就不介绍了):


   
  1. public class BufferedStreamTest {
  2. public static void main(String[] args)throws IOException {
  3. FileInputStream fis= null;
  4. FileOutputStream fos= null;
  5. BufferedInputStream bis= null;
  6. BufferedOutputStream bos= null;
  7. try {
  8. //创建字节输入流
  9. fis= new FileInputStream( "E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\Test.txt");
  10. //创建字节输出流
  11. fos= new FileOutputStream( "E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\newTest.txt");
  12. //创建字节缓存输入流
  13. bis= new BufferedInputStream(fis);
  14. //创建字节缓存输出流
  15. bos= new BufferedOutputStream(fos);
  16. byte[] b= new byte[ 1024];
  17. int hasRead= 0;
  18. //循环从缓存流中读取数据
  19. while((hasRead=bis.read(b))> 0){
  20. //向缓存流中写入数据,读取多少写入多少
  21. bos.write(b, 0,hasRead);
  22. }
  23. } catch (IOException e){
  24. e.printStackTrace();
  25. } finally {
  26. bis.close();
  27. bos.close();
  28. }
  29. }
  30. }

可以看到使用字节缓存流读取和写入数据的方式和文件流(FileInputStream,FileOutputStream)并没有什么不同,只是把处理流套接到文件流上进行读写。缓存流的原理下节介绍。

上面代码中我们使用了缓存流和文件流,但是我们只关闭了缓存流。这个需要注意一下,当我们使用处理流套接到节点流上的使用的时候,只需要关闭最上层的处理就可以了。java会自动帮我们关闭下层的节点流。

2.3 转换流的使用(InputStreamReader/OutputStreamWriter):

   下面以获取键盘输入为例来介绍转换流的用法。java使用System.in代表输入。即键盘输入,但这个标准输入流是InputStream类的实例,使用不太方便,而且键盘输入内容都是文本内容,所以可以使用InputStreamReader将其包装成BufferedReader,利用BufferedReader的readLine()方法可以一次读取一行内容,如下代码所示:


   
  1. public class InputStreamReaderTest {
  2. public static void main( String[] args)throws IOException {
  3. try {
  4. // 将System.in对象转化为Reader对象
  5. InputStreamReader reader= new InputStreamReader(System.in);
  6. //将普通的Reader包装成BufferedReader
  7. BufferedReader bufferedReader= new BufferedReader(reader);
  8. String buffer= null;
  9. while ((buffer=bufferedReader.readLine())!= null){
  10. // 如果读取到的字符串为“exit”,则程序退出
  11. if(buffer.equals( "exit")){
  12. System. exit( 1);
  13. }
  14. //打印读取的内容
  15. System.out. print( "输入内容:"+buffer);
  16. }
  17. } catch (IOException e){
  18. e.printStackTrace();
  19. } finally {
  20. }
  21. }
  22. }

 

上面程序将System.in包装成BufferedReader,BufferedReader流具有缓存功能,它可以一次读取一行文本——以换行符为标志,如果它没有读到换行符,则程序堵塞。等到读到换行符为止。运行上面程序可以发现这个特征,当我们在控制台执行输入时,只有按下回车键,程序才会打印出刚刚输入的内容。

2.4 对象流的使用(ObjectInputStream/ObjectOutputStream)的使用:

写入对象:


   
  1. public static void writeObject(){
  2. OutputStream outputStream= null;
  3. BufferedOutputStream buf= null;
  4. ObjectOutputStream obj= null;
  5. try {
  6. //序列化文件輸出流
  7. outputStream= new FileOutputStream( "E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\myfile.tmp");
  8. //构建缓冲流
  9. buf= new BufferedOutputStream(outputStream);
  10. //构建字符输出的对象流
  11. obj= new ObjectOutputStream(buf);
  12. //序列化数据写入
  13. obj.writeObject( new Person( "A", 21)); //Person对象
  14. //关闭流
  15. obj.close();
  16. } catch (FileNotFoundException e) {
  17. e.printStackTrace();
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. }
  21. }

 

读取对象:


   
  1. /**
  2. * 读取对象
  3. */
  4. public static void readObject() throws IOException {
  5. try {
  6. InputStream inputStream= new FileInputStream( "E:\\learnproject\\Iotest\\lib\\src\\main\\java\\com\\myfile.tmp");
  7. //构建缓冲流
  8. BufferedInputStream buf= new BufferedInputStream(inputStream);
  9. //构建字符输入的对象流
  10. ObjectInputStream obj= new ObjectInputStream(buf);
  11. Person tempPerson=(Person)obj.readObject();
  12. System. out.println( "Person对象为:"+tempPerson);
  13. //关闭流
  14. obj.close();
  15. buf.close();
  16. inputStream.close();
  17. } catch (FileNotFoundException e) {
  18. e.printStackTrace();
  19. } catch (IOException e) {
  20. e.printStackTrace();
  21. } catch (ClassNotFoundException e) {
  22. e.printStackTrace();
  23. }
  24. }

一些注意事项 
1.读取顺序和写入顺序一定要一致,不然会读取出错。 
2.在对象属性前面加transient关键字,则该对象的属性不会被序列化。

4.何为NIO,和传统Io有何区别?

    我们使用InputStream从输入流中读取数据时,如果没有读取到有效的数据,程序将在此处阻塞该线程的执行。其实传统的输入里和输出流都是阻塞式的进行输入和输出。 不仅如此,传统的输入流、输出流都是通过字节的移动来处理的(即使我们不直接处理字节流,但底层实现还是依赖于字节处理),也就是说,面向流的输入和输出一次只能处理一个字节,因此面向流的输入和输出系统效率通常不高。 
    从JDk1.4开始,java提供了一系列改进的输入和输出处理的新功能,这些功能被统称为新IO(NIO)。新增了许多用于处理输入和输出的类,这些类都被放在java.nio包及其子包下,并且对原io的很多类都以NIO为基础进行了改写。新增了满足NIO的功能。 
    NIO采用了内存映射对象的方式来处理输入和输出,NIO将文件或者文件的一块区域映射到内存中,这样就可以像访问内存一样来访问文件了。通过这种方式来进行输入/输出比传统的输入和输出要快的多。

JDk1.4使用NIO改写了传统Io后,传统Io的读写速度和NIO差不了太多。

5.在开发中正确使用Io流

    了解了java io的整体类结构和每个类的一下特性后,我们可以在开发的过程中根据需要灵活的使用不同的Io流进行开发。下面是我整理2点原则:

`在这里插入代码片`](这里写自定义目录标题) ``` 在这里插入代码片 ```

欢迎使用Markdown编辑器

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

居中的图片: Alt

居中并且带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block
var foo = 'bar';

生成一个适合你的列表

  1. 项目1
  2. 项目2
  3. 项目3

创建一个表格

一个简单的表格是这么创建的:

项目Value
电脑$1600
手机$12
导管$1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列第二列第三列
第一列文本居中第二列文本居右第三列文本居左

SmartyPants

SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

TYPEASCIIHTML
Single backticks'Isn't this fun?'‘Isn’t this fun?’
Quotes"Isn't this fun?"“Isn’t this fun?”
Dashes-- is en-dash, --- is em-dash– is en-dash, — is em-dash

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n−1)!∀n∈N 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞​tz−1e−tdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

Mon 06 Mon 13 Mon 20 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接 长方形 圆角长方形 菱形

FLowchart流程图

我们依旧会支持flowchart的流程图:

Created with Raphaël 2.2.0 开始 我的操作 确认? 结束 yes no

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

标签:字符,java,字节,输出,JavaIo,输入,读取
来源: https://blog.csdn.net/qq_45935054/article/details/116904681