java疯狂讲义输入输出视频_疯狂Java讲义读书笔记14 输入输出

IO是比较乏味的事情,因为看不到明显的运行效果,但输入输出是所有程序都必须的部分。

使用输入机制,允许程序读取外部数据(来自磁盘、光盘等存储设备的数据)、用户输入数据,使用输出机制,允许程序记录运行状态,将程序数据输出到磁盘、光盘等存储设备中。

Java的IO通过java.io包下的类和接口来支持。其中字节流以字节为单位来处理输入输出操作,而字符流以字符为单位处理输入输出操作。除此之外,Java的IO流使用了一种装饰器设计模式,它将IO流分成底层节点流和上层处理流,其中节点流用于和底层的物理存储节点直接关联。不同的物理节点获取节点流的方式可能存在一定的差异,但程序可以把不同的物理节点流包装成统一的处理流,从而允许程序使用统一的输入输出代码来读取不同的物理存储节定的资源。

Java7在java.nio以及其子包下提供了一系列全新的API,这些API是对原有新IO的升级。

File类

File类是java.io包下代表与平台无关的文件和目录,也就是说,如果希望程序中操作文件和目录,都可以通过File类完成。

File可以新建删除重命名文件和目录

File是不能访问文件内容本身。如果需要访问文件内容本身,如果需要访问文件内容本身,则需要使用输入输出流

访问文件和目录

File类可以使用文件路径字符串来创建File实例,该文件路径字符串即可以是绝对路径也可以是相对路径。在默认情况下,系统总是依据用户的工作路径来解释相对路径,这给路径由系统属性user.dir指定,通常也就是运行Java虚拟机时所在的路径。

一旦创建了File对象后,就可以调用File对象来访问,file类提供了很多方法来操作文件和目录,下面列出了一些比较常用的方法。

1、访问文件名相关的方法

5e7736b2182d73dcc037be8f16ea7a53.png

Windows的路径分隔符使用\,而Java程序中的反斜线表示转义字符,所以如果需要在windows路径下包括反斜线,则应该使用两条反斜线\\,或者直接使用斜线也是可以的/。

文件过滤器,在File类的list()方法中可接收一个FilenameFilter参数,通过该参数可以只列出符合条件的文件。

这里的FilenameFilter接口包含了一个accept(File dir,String name)方法,该方法将依次对指定File的所有子目录或者文件进行迭代,如果该方法返回true,则list方法会列出该子目录或者文件。

理解Java的IO流

Java的IO流是实现输入输出的基础,他可以方便地实现数据的输入输出操作,在Java中把不同的输入输出源。stream是从起源到接收的有序数据。

Java把所有传统的流类型都放在java.io包中,用以实现输入输出功能。

因为Java提供了这种IO流的抽象,所以开发者可以使用一致的IO代码去读写不同的IO流节点。

流的分类

按照不同的分类方式,可以将流分为不同的类型,下面从不同的角度来对流进行分类,它们在概念上可能 存在重叠的地方。

1、输出流和输入流

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

输入流:只能向其读取数据,而不能向其写入数据

输出流:只能向其写入数据,而不能从中读取数据。

此处的输入、输出涉及一个方向问题,数据从内存到硬盘就是输出流,也就是说,这里的输入输出,都是从程序运行所在的内存的角度来划分的。

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

字节流和字符流

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

字节流操作的数据单元是8位的字节,而字符流操作的数据单元是16位的字符

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

3、节点流和处理流

按照流的角色来分,可以分为节点流和处理流。

可以从/向一个特定的IO设备读写数据的流称为节点流,节点流也被称为低级流。

当使用节点流进行输入输出时,程序直接连接到实际的数据源,和实际的输入输出节点连接。

处理流则用于对一个已经存在的流进行连接或封装,通过封装后的流实现数据读写功能。处理流也被称为高级流。

b10f293b7211b1975819c19d87eddfae.png

当使用处理流进行输入输出时,程序并不会直接连接到实际的数据源,没有和实际的输入输出节点连接。

使用处理流的一个明显好处是,只要使用相同的处理流,程序就可以采用完全相同的输入输出代码来访问完全不同的数据源,随着处理流所包装的节点流变化,程序实际所访问的数据源也在相应的变化。

实际上,Java使用处理流来包装节点流是一种典型的装饰器模式,通过使用处理流来包装不同的节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出功能。因此处理流也被称为包装流。

流的概念模型

Java把所有设备里的有序数据结构抽象成流模型,简化了输入输出的处理,理解了流模型的概念也就理解了JavaIO

JavaIO流涉及40多个类,这些类看上去负责凌乱,但实际上非常规则,而且彼此之间存在非常紧密的联系。

Java的IO流的40多个类都是从如下4个抽象基类派生的。

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

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

对于InputStream和Reader而言,他们把输入设备抽象成一个水管

dee25527e3f48eb7951a3bbfad570177.png

输入流使用隐式的记录指针来表示当前正准备从哪个水滴开始读取,每当程序从InputStream或Reader里取出一个或多个水滴后,记录指针自动向后移动。除此之外,InputStream和Reader里都提供了一些方法来控制记录指针的移动。

对于OutputStream和Writer而言,他们同样是把输出设备抽象成一个水管,只是这个水管里没有任何水滴。当执行输出,程序相当于依次把水滴放入到输出流的水管中,输出流同样采用隐式指针来标识当前水滴即将放入的位置。每当程序向OutputStream或Writer里输出一个或多个水滴后,记录指针自动向后移动。

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

字节流和字符流

本书会将字节流和字符流放在一起讲解,因为它们的操作方式几乎完全一样,区别只是操作的数据单元不同而已,字节流操作的数据单元是字节,字符流操作的数据单元是字符。

InputStream和Reader是所有输入流的抽象基类,本身并不能创建实例来执行输入,但它们将称为所有输入流的模板,所以它们的方法是所有输入流都可以使用。

在InputStream里包含三个方法。

e342c36b3940994cebac98a12d8b7eb5.png

在Reader里包含三个方法如下

463b8d75113d34b8ff95305d551f04d7.png

b849c3323cf2a65252b02437a712f5ce.png

OutputStream和Writer也非常相似

3d4cc2a79332048c12a35823601c3bfc.png

因为字符流直接以字符作为操作单位,所以writer可以用字符串来代替字符数组

038efb9decca87720d8c9b7d4a8e9e4b.png

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

使用处理流的典型思路是,使用处理流来包装节点流,程序提供处理流来执行输入输出功能,让节点流与底层的IO设备、文件交互

识别处理流特别简单,只要流的构造器参数不是一个物理节点,而是已经存在的流,那么这种流就一定是处理流,所有的节点流都是直接以物理IO节点作为构造器参数的。

cf78b82100d1ed811e13099b0db399d6.png

上面程序中的两行粗体字代码先定义了一个节点输出流FileOutputStream,然后程序使用PrintStream包装了该节点输出流,最后使用PrintStream输出字符串,输出对象。

PrintStream的输出功能很强大,前面程序中一直使用的标准输出System.out的类型就是PrintStream。

30eab2252e0558ed337cd9fc380e7283.png

通常来说,字节流的功能比字符流的功能强大,因为计算机里所有的数据都是二进制的,而字节流可以处理所有的二进制文件,但问题是,如果使用字节流来处理文本文件,则需要使用合适的方式将这些字节转换成字符。这就增加了编程的难度。所以通常有一个规则,如果进行输入输出的内容是文本内容就应该考虑使用字符流,如果输入输出的内容是二进制内容,则应该考虑使用字节流。

6330226c0b4ef9decfad282131326f4a.png

转换流

输入输出流体系还提供了两个转换流,这两个转换流用于实现将字符流转换成字符流,其中InputStreamReader将字节输入流转换成字符输入流,OutputStreamWriter将字节输出流转换成字符输出流。

db8c9c10a3163e1f6832ee171c43860c.png

由于BufferedReader具有一个readLine()方法,可以非常方便地一次读入一行内容,所以经常把读取文本内容输入流包装成一个BufferedReader,用来方便地读取输入流的文本内容

推回输入流

在输入输出流体系中,有两个特殊的流与众不同,就是PushbackInputStream和PushbackReader

它们都提供了如下三个方法。

231b413dde474ef88efdd718d638cf83.png

细心的读者可能已经发现了这三个方法与InputStream和Reader中的三个read方法一一对应。

这两个推回流都有一个推回缓冲区,而推回输入流每次调用read方法时总是先从推回缓冲区读取,只有完全读取了推回缓冲区的内容后,但还没有装满read所需要的数组时,才会从原输入流读取。

重定向标准输入/输出

第七章介绍过,Java的标准输入输出分别通过,System.in和System.out来代表,默认情况下它们分别代表键盘和显示器。

system类里面提供了三个重定向标准输入输出的方法。

ce2066e5bfa22e77e74fca95b44333fa.png

下面程序通过重定向标准输出流,将System.out的输出重定向到文件输出,而不是在屏幕上输出。

b356c33f0d3456508ab49894c2b93847.png

dac1af9f161485ba17ee8a64a42438d9.png

15.6 Java虚拟机读写其他进程的数据

在第七章介绍过,使用Runtime对象的exec方法可以运行平台上的其他程序,该方法产生一个Process对象,Process对象代表由该Java程序启动的子进程。

Process类提供了如下三个方法,用于让程序和其子进程进程通信

a2ee7844cfe6e8ef3087dad8d4be8d59.png

e9779369084eec30cab30c5402f8bdb2.png

9cacc89adb08e642cdba5208bc0f0ade.png

RandomAccessFile

支持随机访问,程序可以直接跳到文件的任意地方开始读写数据

4d350c96812bc020d123df437993c5b3.png

对象序列化

对象序列化是将对象保存到磁盘中,或允许网络中直接传输对象。

对象序列化机制允许将内存中的java对象转换成与平台无关的二进制流,从而允许将这种二进制流永久保存在磁盘上,其他程序一旦获取了这种二进制流,都可以将这种二进制流恢复成原来的Java对象。

序列化的含义和意义

序列化机制允许将实现序列化的Java对象转换成字节序列,这些字节序列可以保存至磁盘上,或通过网络传输以备后来重新恢复成对象。

序列化机制使得对象可以脱离程序运行而独立存在。

如果需要让某个对象支持序列化机制,则必须让它的类是可序列化的,该类必须实现如下两个接口之一

8a9e8609e20a680cdbd7a99325de1ee2.png

通常建议,每个Javabean都实现serialzable接口

952be4fb4a544977482a974c72b97f12.png

62524a5cf80df43d96e0921dcb70b493.png

1cabf1f2ccdf9ad1dc066cc22ff87925.png

NIO

前面介绍BufferedReader时提到了它的一个特征。当Bufferedreader读取输入流中的数据时,如果没有读到有效数据,程序将在此处阻塞该线程的执行。也就是说之前介绍的输入流输出流都是阻塞式输入输出。

不仅如此,传统的输入流输出流都是通过字节的移动来处理的(即使不直接去处理字节流,但底层的实现还是依赖于字节处理),也就是说,面向流的输入输出系统一次只能处理一个字节,因此面向流的输入输出效率不高。

Channel和Buffer是新IO中的两个核心对象,Channel是对传统输入输出系统的模拟,在新IO系统中所有的数据都需要通过通道传输传输。Channel与传统的InputStream和OutputStream最大的区别就是提供了一个map方法,该方法可以直接将一块数据映射到内存中。

如果说传统的输入输出系统是面向流的处理,则新IO是面向块的处理。

Buffer可以理解成一个容器,它的本质是一个数组,发送到Channel中所有对象都必须首先放到Buffer中,而从Channel中读取的数据也必须放到Buffer中

f8fc95abfc990d57aacd68a6bf18c646.png

Buffer的主要作用就是装入数据,然后输出数据(其作用类似于取水的竹筒)开始的时候Buffer的postion是0,limit为capacity,程序可以通过put方法向Buffer中放入一些数据,或者从Channel中获取一些数据,每放入一些数据,Buffer的position相应的向后移动一些位置。

当Buffer装入数据结束后,调用Buffer的flip方法,该方法将limit设置为position所在的位置,并将position设为0,这就使得Buffer的读写指针又移到了数据结束,也就是说,Buffer调用flip方法之后,Buffer为输出数据做好准备,当Buffer输出数据结束后,Buffer调用clear方法,clear方法不是清空Buffer数据,仅仅将position置为0,将limit置为capacity,这样为再次向Buffer中装入数据做好准备。

fcc86990ac45e5a2cd9625f4ea16fd14.png

fe5da259ff33954c5f18362be81a7abc.png

上面代码虽然使用FileChannel和Buffer来读取文件,但处理方式和使用InputStream读取文件几乎是一样的,都是采用用竹筒多次重复取水的方式。但是因为Buffer提供了flip和clear两个方法,所以程序处理比较方便,每次读取数据后调用flip方法将没有数据的区域封印起来。

53caf73eea0e40df2020982465d412c1.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值