Java 文件——流(Stream)的基本介绍

1. 流 Stream

Java中(绝大部分编程语言类似)文件一般不是单独处理,而是视为输入输出(I/O)设备的一种。
Java使用统一的概念来处理所有的IO。包括键盘,终端,网络等。

  • JavaIO的基本类位于java.io包下。
Stream 含义
InputStream 表示输入流
OutputStream 表示输出流
FileInputStream 表示文件输入流
FileOutputStream 表示文件输出流

有了流的概念,就有了很多面向流的函数,输入输出都是抽象的流,提供流的加密、压缩等功能。
一些实际上不是IO的数据源和目的地也转换成了流,以便参与流的整体概念体系的协作。
例如,字节数组ByteArrayInputStream/ByteArrayOutputStream

2. 装饰器设计模式

基本的流按照字节读写,没有缓冲区,性能底下。Java使用装饰器模式引入很多装饰类对基本的流增加功能。
一个装饰类一般只关注一个方面的功能。所以实际使用中经常需要使用多个装饰类。

Java中有很多装饰类。对流提供过滤功能有两个基类:FilterInputStream/FilterOutputSteam.
过滤功能的装饰器对流提供一些功能包装,输入输出都是流。
流的装饰器类成对出现,分别对应输入和输出流。
FilterInputStream/FilterOutputSteam有很多子类,这里只列出出入的子类,输出的子类类似:

FilterInputStream的子类 功能
BufferedInputStream 对流起缓冲作用
DataInputStream 按照8种基本类型对流读写
GZipInputStream 对流提供压缩功能
ZipInputStream 对流提供压缩功能
PrintStream 将基本类型,对象输出为字符串表示

3. Reader/Writer

以InputStream/OutputStream 为基类的流基本都是以二进制形式处理数据。
不能方便的处理文本文件,没有编码概念。能方便的按字符处理文本的基类是Reader/Writer。

这两个类也有很多子类。

Reader/Writer 的子类 功能
FileReader/FileWriter 读/写文件
BufferedReader/BufferedWriter 缓冲读/写
CharArrayReader/CharArrayWriter 将字符数组包装成流
StringReader/StringWriter 将字符串包装成流
InputStreamReader/InputStreamWriter 转换InputStream/OutputStream和Reader/Writer
PrintWrite 输出Write中的基本类型,对象输出为其字符串表示

大部分情况下,使用流或者Reader/Writer读写文件内容。但Java还提供乐一个独立的可以随机读写文件的类RandomAccessFile。用于大小已知的文件。开发中用的较少。
一些系统程序中用的较多。

以上介绍的类都是操作数据本身,而数据保存的载体,Java使用File类来表示,提供文件路径,文件元数据,临时文件,权限管理等功能。

4. Nio

上面的类都是位于java.io包下,Java中还有一个关于IO的操作包java.nio,nio的意思是New IO。
它有缓冲区和通道的概念。

缓冲和通道概念更像是操作系统的概念。利用缓冲区和通道往往可以达成和流类似的目的,某些操作性能也更高,

例如:通道可以利用操作系统和硬件提供的DMA(direct memory access 直接内存存取)机制直接将数据从硬盘复制到网卡(不需要程序和CPU的参与)。

NIO还支持一些比较底层的功能。如 内存映射文件(常用)、文件加锁、自定义文件系统、非阻塞式IO,异步IO等。

5. Serialization/Deserialization 序列化 反序列化

Serialization 序列化:是指将内存中的java对象持久的保存到一个流中。
Deserialization 反序列:是指将流中的对象恢复到java内存。

一般有两个用途:一个是对象持久化;另一个是网络远程调用。

Java主要通过interface Serializable 和class ObjectInputStream/ObjectOutputStream提供对序列化的支持。

Java的默认序列化有一些缺点:序列化后的形式较大浪费空间,序列化和反序列化的性能较低,java独有的格式,不能与其他语言交互。

5.1 Xml/Json

Java对象可以序列化为xml格式,xml格式比较笨重,现在更多的使用Json格式。

XML/JSON都是文本格式,便于人阅读,但占用空间相对较大。在只用于网络远程调用的情况下,有很多其他的更高效的格式,比如ProtoBuf、Thrift,MessagePack.
其中,MessagePack是二进制形式的JSON,更小更快。

6. 二进制文件和字节流

二进制读写流的类的有:

类名 说明
InputStream/OutputStream 抽象基类
FileInputStream/FileOutputStream 输入源和输出目标是文件的流
ByteArrayInputStream/ByteArrayOutputStream 输入源和输出目标是字节数组的流
DataInputStream/DateOutputStream 装饰类,按照基本类型和字符串而非是字节读写流
BufferedInputStream/BufferedOutputStream 装饰类,对输入输出流提供缓冲功能

6.1 InputStream/OutputStream 抽象基类

先介绍InputStream

6.1.1 InputStream的主要方法是 public abstract int read() throw IOException

该抽象方法的一般实现是从流中读取下一个字节,返回取值范围是-1,0~255的int类型。
当读取到流的结尾时返回-1,如果流中没有数据,read()方法会阻塞知道数据到来、流关闭或异常出现。

6.1.2 InputStream还有 public int read(byte b[]) throws IOException 方法,可以一次性读取多个字节。

  • 读入的字节放入参数数组b[]中。返回读入的实际字节个数。实际字节个数小于(流读完了而没有读满)等于数组b的长度。
    如果刚开始读取就读到末尾则返回-1。否则该方法会尽力读取至少一个字节,如果流中没有字节则阻塞。

  • 非抽象方法,有默认实现:循环读取一个字节的read方法,但是子类的实现一边更为高效。

  • 流读取结束后都应该调用close()方法关闭释放资源。所以一般放入finally语句中。
    close()方法自身也可能抛出异常,但通常可以捕获并忽略。

6.1.3 InputStream中还定义了如下方法:

1. public long skip(long n) throws IOException
2. public int available() throws IOException
3. public synchronized void mark(int readlimit)
4. public boolean markSupported()
5. public synchronized void reset() throws IOException
  • skip(long n)方法跳过输入流中的n个字节,返回实际跳过的字节个数(流中剩余的字节个数可能小于n).
  • avaiable() 返回下一次不需要阻塞就能读取到的大概字节个数,一般用在从网络读取数据时等到有足够数据才去读,防止阻塞。

一般的流都是一次性读取的,且只能按照输出的这一个方向读取,但是有时候希望能够先看下后面的内容,根据情况再重新读取。

比如处理一个未知的二进制文件,不确定其类型,我们可能可以通过流的前几十个字节判断其类型。然后重置到流开头。交给相应的代码处理。

InputStream定义了三个方法,用于支持从读过的流中重复读取:mark reset markSupported
具体步骤是

  1. 先使用 mark(int readlimit)将当前位置标记下来,readlimit表示往后可以读取的最多字节数。读取一些字节后(需要小于readlimit,否则标记失效)。
  2. 调用reset()方法重置到mark标记的位置,

不是所有的流都支持mark reset方法。是否支持可以通过调用markSupported方法查看返回值确认。
InputStream 默认实现是不支持的,FileInputStream不支持,但BufferedInputStreamByteArrayInputStream支持。

接下来介绍 OutputStream

6.1.4 OutputStream的基本(主要)方法是 public abstract void write(int b) throws IOException
向六中写入一个Byte字节,参数虽然是int,但只会用到最低的8bit(1Byte=8bit).

6.1.5 OutputStream还有两个批量写入的方法

public void write(byte b[]) throws IOException
public void write(byte b[], int off, int len) throws IOException 

第二个方法,第一个写入的字节是b[off],写入个数len,最后一个是b[off+len-1].

6.1.6 OutputStream还有两个方法 flush close

public void flush() throws IOException
public void close() throws IOException

flush()方法只对有缓冲的流起作用,其将流中缓冲而未实际写入的数据进行实际写入。
比如在BufferedOutputStream中,调用flush方法会将其缓冲区的内容写到其装饰的流中,并调用该流的flush方法。
基类OutputStream没有缓冲,flush方法为空。
FileOutputStream没有缓冲,没有flush方法。

close()方法一般会先调用flush()方法,然后再释放占用的资源。
InputOutStream一样,close()方法应该放在finally块中。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354