Java I/O 流学习笔记(7月14号)

文章目录

  • 作者信息
  • 前言
  • 一.Java中的File类
      • 1.1 什么是File?
      • 1.2 两种文件路径
      • 1.3 File类的三种构造方法
      • 1.4 File类常用方法
      • 1.5 List方法获取当前目录下的文件/文件名数组
      • 1.6 文件过滤器FilenameFilter
  • 二.Java中的I/O流
      • 2.1 什么是I/O流?
      • 2.2 处理流模型
      • 2.3 字节流和字符流有什么区别?
      • 2.4 I/O流的抽象基类
          • 输入流的抽象基类
          • 输出流的抽象基类
      • 2.5 转换流
      • 2.6 缓冲流
      • 2.7 其他输入输出流
          • 数据输入/输出流 DataInputStream
          • 内存操作流 ByteArrayInputStream
          • 打印流 PrintStream/PrintWriter
          • 随机访问流 RandomAccessFile
          • 合并流 SequenceInputStream
          • 序列化流 ObjectOutputStream
      • 2.8 关于I/O一些问题的思考
          • system.out,system.in和Scanner的本质是什么?
          • 文件如何输入输出数据?
          • 为什么要使用close释放资源?
          • 为什么要使用flush刷新缓冲区?
  • 三.Java的NIO
      • 3.1 传统BIO的缺陷和NIO的优点
      • 3.2 NIO实现非阻塞的原理
  • 四.总结
  • 五.参考资料

作者信息

作者:黄钰朝
邮箱:[email protected]
日期:2019年7月14日

前言

今天整理了Java的File类的相关方法,和Java中IO流相关的知识,了解JavaIO包中的各种流的体系,学习JavaNIO和JavaIO的区别,并且了解Java中的socket编程,按照导师的要求,使用socket编写聊天程序。

一.Java中的File类

1.1 什么是File?

  • File是Java中用来代表文件/文件夹的一个对象
  • File不等于文件,只是文件的抽象,Flie作为一个Java类的实例而存在,不依赖于具体的文件,只是Java把对文件的操作抽象成为对File对象的操作。
  • File对象可以实现对文件的创建,删除,修改等操作,但是无法进行文件内容的读取,只能依靠IO流.

1.2 两种文件路径

  • 绝对路径:带盘符的完整路径
  • 相对路径:不写盘符,默认以项目位置为当前路径

1.3 File类的三种构造方法

  • 通过一个文件路径创建一个File对象
  • 通过一个文件路径+文件名/文件夹名创建一个File对象
  • 通过一个File对象+文件名/文件夹名创建一个File对象

其中第三种相当于把父目录封装成File对象,再根据文件名/文件夹名创建File对象

1.4 File类常用方法

创建:如果目标文件/文件夹已存在,就不创建了

  • createNewFile:创建一个文件,如果目录不存在则无法创建
  • mkdir:创建一个文件夹,如果目录不存在则无法创建
  • mkdirs:创建文件夹,如果目录不存在则创建多级文件夹

删除:如果目标文件夹内有文件/文件夹,则无法删除

  • delete:删除一个文件/文件夹

修改:

  • renameTo(File newfile):重命名一个文件/文件夹,相当于把文件转移到另一个File对象所指的路径

另有获取文件名,文件是否存在/可读/可写等方法

1.5 List方法获取当前目录下的文件/文件名数组

  • list方法:获取当前目录下的文件名数组
  • listFiles方法:获取当前目录下的文件数组

1.6 文件过滤器FilenameFilter

使用FilenameFilter过滤器,其目的是给list()方法列出文件或文件名时增加筛选条件

主要思想:过滤器模式和模板模式

创建一个过滤器,通过重写接口的accept方法传递策略,list方法在遍历文件名时使用过滤器去除不符合条件的文件

扩展:使用Lambda表达式简化代码

FilenameFilter接口是单函数接口,因此可以使用Lambda表达式简化代码

二.Java中的I/O流

2.1 什么是I/O流?

  • 程序中数据的输入输出,被抽象为流, 按照相对于程序的流向,可分为输出流和输入流, 按照数据流的格式,可分为字节流和字符流(类似于C语言中以二进制和以字符形式读取文件)
  • 另有有节点流和处理流,节点流用来包装源头,处理流的作用是对Java程序提供统一的数据输入输出的支持,并提高性能。

2.2 处理流模型

  • Java将来自任何节点的数据流都包装成为处理流,使得可以使用相同的代码来处理来自不同设备的数据流,并可以以增加缓冲的方式提高性能,并支持一次输出大批量的内容,这便是处理流模型,思想类似于C语言中流式文件的处理方式

2.3 字节流和字符流有什么区别?

  • 类似于C语言中二进制文件和文本文件的区别,字符其实只是一种特殊的二进制字节,是按照一定的编码方式处理之后,按照一定规则来存储信息的数据,字符在计算机中也是由二进制组成的,只不过这种二进制可以按照一种规则解码后,成为人类可以直接阅读的自然语言,而普通的二进制文件只有计算机能直接“阅读”

2.4 I/O流的抽象基类

输入流的抽象基类
  • 字节输入流 : InputStream
  • 字符输入流 : Reader
输出流的抽象基类
  • 字节输入流 : OutputStream
  • 字符输入流 : Writer

2.5 转换流

  • Java提供了从字节流到字符流的转换流,分别是InputStreamReader和OutputStreamWriter
  • 字符流=字节流+编码表
  • 没有从字符流到字节流的转换流

2.6 缓冲流

  • 一次读取一个字节数组的效率明显比一次读取一个字节的效率高,因此Java提供了带缓冲区的字节类,称为缓冲区类。BufferedInputStream和BufferedOutputStream
  • 字符缓冲区类BufferedReader和BufferedWriter与之类似.
  • 缓冲区类的构造方法可以指定缓冲区的大小,如果没有指定,则使用默认大小,一般默认大小已经够用了
  • 缓冲区类只是提供缓冲区,实际的读写操作还是基本IO流对象来做的,因此缓冲区类的构造方法的参数是基本IO对象

2.7 其他输入输出流

数据输入/输出流 DataInputStream
  • 与缓冲区流的使用方式类似,也需要传入一个实际进行读写操作的InputStream实现类,数据输入/输出流的特殊之处在于它是对Java中的各种类型数据进行输入输出
内存操作流 ByteArrayInputStream
  • 与FileInputStream类似,只是输入输出的对象是一段内存空间,即内存操作流内部的字节数组。相当于把内存操作流对象当成一个字节数组,对它进行输入输出。
打印流 PrintStream/PrintWriter
  • 打印流是单向的,只有输出流,没有输入流
  • 可以打印任意类型的数据
  • 可以启用自动刷新
  • 打印流可以直接操作文本文件
  • 打印流是以字节方式输出
随机访问流 RandomAccessFile
  • 仅可用于文件操作
  • 相当于C语言中的文件读写操作,可以随机读写文件,即可以操作文件的读写指针,并且可以选择使用只读/只写等方式读写文件
  • getFilePointer:得到当前文件指针位置
  • seek:设置文件指针位置
合并流 SequenceInputStream
  • 用于将多个输入流合并
序列化流 ObjectOutputStream
  • 用于将对象写入文件,或者在网络传输,操作的对象必须是已经实现序列化接口的(序列化接口只是一种标记接口)
  • 序列化流是将对象存入文件或在网络中传输,反序列化流就是将流数据还原成为对象
  • 一个类的序列化值与类的成员变量有关,如果类的成员变量改变,则序列化值也会变,一个对象被存储到文件时会记录它的类的序列化值,如果将来类的序列化值改变了,这个对象就无法被反序列化,即无法还原为对象了,因此通常生成一个serialVersionUID,用来记录当前版本的类的序列化值,以便将来反序列化以往版本的对象
  • 使用translent关键字可以让一个成员变量不被序列化

2.8 关于I/O一些问题的思考

system.out,system.in和Scanner的本质是什么?
  • in和out时system类中的两个静态成员变量分别是标准输入流InputStream和标准输出流PrintStream,分别代表系统的标准输入输出设备,即键盘和显示器
  • Scanner是JDK5之后出现的方便处理键盘输入的数据的类,本质相当于一个缓冲区,并且封装了将字节信息转成各种数据类型的方法
文件如何输入输出数据?
  • 不管是字节流和字符流,都提供了read方法和write方法,分别用来读取和输出数据。
  • 创建输入/输出流对象时的参数都是指向外部节点的对象(比如File对象)
  • 调用read和write方法时的参数都是指向程序中内存空间的对象
  • 文件输出操作步骤:创建输出流对象(提供输出目标)->调用write方法(提供数据源)->释放资源
  • 文件输出的操作步骤可以看成:提供水管(让水管指向目标)->喷水(提供水龙头)->回收水管
为什么要使用close释放资源?
  • 让流对象成为垃圾,可以被垃圾回收器回收
  • 通知系统去释放该文件相关的资源
为什么要使用flush刷新缓冲区?
  • 因为Java中输出流的内容会被先保存在缓冲区,直到缓冲区满,才会真正把数据发送出去,当数据很小时,可能导致数据接收不到,而使用flush可以强制将缓冲区的数据发送出去,不必等到缓冲区满

三.Java的NIO

3.1 传统BIO的缺陷和NIO的优点

Java IO包下的类是传统的BIO,也就是阻塞式IO(blocking-I/O),在做导师安排的控制台聊天项目的过程中,就被传统I/O的“阻塞”问题困扰了很久,“阻塞”就是线程在内存中等待一个事件的就绪,而不能去处理其他任务的状态 ,具体在项目中体现在,使用传统I/O的socket编程,只有一个线程时,一旦开始执行读取客户端的数据的read方法,就进入阻塞状态,直到客户端真的发了数据过来,这个方法才能返回,线程才能处理别的事情,而我一开始做的时候就是只有一个线程,导致消息发不出去,后来使用多线程才解决这个问题。

NIO就是为了解决“阻塞”问题而出现的。NIO的N的含义有两种说法:

  • new 代表这是“新”的I/O
  • non-blocking 代表这是“非阻塞”的I/O

在Java中,认为NIO是New I/O的说法比较多。

3.2 NIO实现非阻塞的原理

先上图:

Java I/O 流学习笔记(7月14号)_第1张图片

可以看到,NIO的主要原理是执行read,write,accept,connect这些方法时,都会立即返回,不会等待,read方法如果没有数据可读,直接返回0,accept方法如果没有客户端连接,直接返回null。在这种非阻塞模式下,可以配合selector来实现单线程监听各个事件。

PS:其实很多时间在看NIO的知识,但是没时间写了,要开设计模式的内容了。

四.总结

I/O流的知识非常基础实用,是网络编程的基础。但是这两天,第一天早上补完了数据库设计的笔记,下午又开了一下午的全体例会,加上做聊天程序花的时间很多,没有先看一下别人的代码,自己看了socket几个方法的用法就开始搞,因为没有线程方面的知识,被线程阻塞的问题拖了很多时间,后来去使用了多线程,才终于能够和客户端互发消息。后面学了NIO,看了疯狂java讲义上NIO做的socket编程的例子,才发现使用非阻塞I/O来做会很容易。

由于做项目花了挺多的时间,NIO和socket的知识没有时间写多少笔记。

五.参考资料

  • Java NIO浅析
  • Java NIO 系列教程
  • Java socket详解,看这一篇就够了
  • 十分钟了解BIO、NIO、AIO
  • 知乎-通俗地讲,Netty 能做什么?
  • 简单理解socket
  • 《疯狂Java讲义》
  • 《Java网络编程》

你可能感兴趣的:(QG训练营日志,Java,I/O流,NIO,学习笔记)