Java - IO

对于程序语言的设计者来说,创建一个好的I/O系统是一项很艰难的任务。挑战不仅存在于IO多样的输入/输出端,还在于多种不同的通信方式,如顺序存取、缓冲、二进制、按字符/按行/按字存取等。Java类库的设计者创建了大量的类来解决这个困难。

Java I/O设计初衷是避免使用过多的类,但讽刺的是,Java I/O系统提供的类库依旧很多。

Java 的 I/O 操作类在包 java.io 下,大概有将近 80 个类,但是这些类大概可以分成四组,分别是:

  • 基于字节操作的 I/O 接口:InputStream 和 OutputStream
  • 基于字符操作的 I/O 接口:Writer 和 Reader
  • 基于磁盘操作的 I/O 接口:File @Deprecated
  • 基于网络操作的 I/O 接口:Socket @Deprecated

典型的数据源及媒介有:

  • 文件
  • 控制台
  • 网络
  • 设备

前三个位于package java.io下,最后一个位于package java.net下。

File

File类不负责具体文件内容的读写,但是负责文件及文件目录的操作,是基于磁盘操作的I/O。
File类名字有一定的误导性。我们可能会认为它指代的是文件,实际上并非如此。它既能指代一个具体的文件,也能代表一个目录。

常用File类操作:

//创建文件
public boolean createNewFile() throws IOException;

//列举目录列表
public String[] list();
//列举目录列表,FilenameFilter用来辅助过滤。如列举以`.java`结尾的文件,则可通过implements FilenameFilter来实现
public String[] list(FilenameFilter filter);

//获取文件/目录名
public String getName();

//获取文件父目录路径
public String getParent();

//删除文件/目录
public boolean delete()

更多其他操作可参考JDK文档。

基于字节和字符的I/O操作

是Java IO的核心。
分为输入流(Input)和输出流(Output)两部分。输入流从数据源读取数据,输出流将数据写入到特定的目标。

常见数据源

  • 字节数组
  • String对象
  • 文件
  • 管道
  • 其他种类的输出流
  • 网络

常见数据输出端

  • 字节数组
  • 文件
  • 管道
  • 其他种类的输入流
  • 网络

所有的输入流都有read()方法,所有的输出流都有write()方法。

不管是磁盘还是网络传输,最小的存储单元都是字节,因此支持基于字节的I/O就显然而已了。但是我们的程序中通常操作的数据都是以字符形式,为了操作方便当然要提供一个直接写字符的 I/O 接口,因此又产生了基于字符的I/O。

基于字节的IO

计算机识别数据的基本单位就是字节。
基于字节的 I/O 操作接口输入和输出分别是:InputStreamOutputStream

InputStream

InputStream是所有输入类的接口,部分方法如下:

public abstract class InputStream implements Closeable {
    public int available() throws IOException;
    public abstract int read() throws IOException;
    public int read(byte b[]) throws IOException;
    public int read(byte b[], int off, int len) throws IOException;
}

常见InputStream实现类

image.png

ByteArrayInputStream
将内存当做数据源,构造函数需要传入byte数组。

public class ByteArrayInputStream extends InputStream {
    public ByteArrayInputStream(byte buf[]);
}

FileInputStream
数据源是文件,构造函数如下:

public class FileInputStream extends InputStream{
    public FileInputStream(File file) throws FileNotFoundException;
    public FileInputStream(FileDescriptor fdObj)
    public FileInputStream(String name) throws FileNotFoundException;
}

PipedInputStream
数据源是PipedOutputStream,常用在多线程中

public class PipedInputStream extends InputStream {
    public PipedInputStream(PipedOutputStream src) throws IOException;
}

FilterInputStream
这里使用了装饰者模式,做为装饰器的接口。可以从构造函数看出,只支持传入InputStream类型的对象。

public class FilterInputStream extends InputStream {
    protected FilterInputStream(InputStream in);
}

不太了解装饰者模式的可以看下Design Pattern - 装饰者模式

为什么需要装饰者模式呢?JDK的设计者主要是为了给不同的I/O提供更多的灵活性。
举个例子,传统的I/O是无缓冲的,也就是说每次读或者写哪怕一个字节,都需要调用底层操作系统接口,这是个效率很低的操作。为传统I/O设计一个缓冲区,是非常必要的。但是读取文件的FileInputStream需要缓冲区,读取内存的ByteArrayInputStream需要缓冲区,其他的InputStream的实现类也需要缓冲区。怎么设计才能支持所有的InputStream都能支持缓冲区呢?JDK的设计者就想到了使用装饰者模式。使用BufferedInputStream来装饰任意InputStream对象,该输入流就能支持缓冲区了!
如:

byte[] buff = new byte[1024];
File file = new File(filePath);
BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
in.read(buff);  //支持了缓冲的read操作

OutputStream

OutputStream是所有输出类的接口,部分方法如下:

public abstract class OutputStream implements Closeable, Flushable{
    public abstract void write(int b) throws IOException;
    public void write(byte b[]) throws IOException;
    public void write(byte b[], int off, int len) throws IOException;
    public void flush() throws IOException;
}

常见OutputStream实现类

image.png

OutputStream的实现类大部分是跟InputStream一一对应的,这个从名字上就能看出来。二者的原理类似,因此这里就不再做过多介绍了。

基于字符的IO

基于字符的 I/O 操作接口输入和输出分别是:ReaderWriter

Reader

Reader的继承层级:

image.png

StringReader
String做为输入源

public class StringReader extends Reader{
    public StringReader(String s);
}

BufferedReader
从其他Reader读取数据到缓冲区。类似BufferedInputStream的功能

public class BufferedReader extends Reader{
    public BufferedReader(Reader in, int sz);
}

FileReader
从文件中读取数据

public class FileReader extends InputStreamReader {
    public FileReader(File file) throws FileNotFoundException;
}

PipedReader
从管道中读取数据。跟PipedWriter联合使用。

例子:

public void read(){
    String hello= new String( "hello word!");
    File file= new File( "d:/test.txt");
    //因为是用字符流来读媒介,所以对应的是Writer,又因为媒介对象是文件,所以用到子类是FileWriter
    Writer os= new FileWriter( file);
    os.write( hello);
    os.close();
}

Writer
Writer的实现类同Reader对应,从名字上就能很容易找到联系。

image.png

例子:

public static void write() throws IOException{
    File file= new File( "d:/test.txt");
    //因为是用字符流来读媒介,所以对应的是Reader
    //又因为媒介对象是文件,所以用到子类是FileReader
    Reader reader= new FileReader( file);
    char [] byteArray= new char[( int) file.length()];
    int size= reader.read( byteArray);
    System. out.println( "大小:"+size +";内容:" +new String(byteArray));
    reader.close();
  }

字节和字符的转换

数据持久化或网络传输都是以字节进行的,所以必须要有字符到字节或字节到字符的转化。字符到字节需要转化,其中读的转化过程如下图所示:


image.png

InputStreamReader类是字节到字符的转化桥梁,InputStreamReader 的过程要指定编码字符集,否则将采用操作系统默认字符集,很可能会出现乱码问题。StreamDecoder 正是完成字节到字符的解码的实现类。也就是当你用如下方式读取一个文件时:

try { 
     StringBuffer str = new StringBuffer(); 
     char[] buf = new char[1024]; 
     FileReader f = new FileReader("file"); 
     while(f.read(buf)>0){ 
         str.append(buf); 
     } 
     str.toString(); 
} catch (IOException e) {}

写入也是类似的过程如下图所示:


image.png

通过 OutputStreamWriter 类完成,字符到字节的编码过程,由 StreamEncoder 完成编码过程。

基于网络的IO

网络编程依赖于ServerSocketSocket。同File类似,只是表示网络的描述符。真正的数据传输依旧依赖于InputStreamOutputStream!!!
在互联网时代,网络编程在任何一个编程语言中都是一个极其重要的内容。因此,这部分的内容会在一个专门的文中来讲。

Java1.4引入了NIO
Java NIO

参考

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

推荐阅读更多精彩内容

  • 虽然写了一些javaIo流的总结,感觉依旧没有系统的了解javaIO,幸好从网上看到这一篇文章,觉得不错。整理记录...
    Marlon666阅读 344评论 0 2
  • Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有的I/O机制都是基于数据流进行输入输出,...
    cmlong_阅读 485评论 0 4
  • tags:io categories:总结 date: 2017-03-28 22:49:50 不仅仅在JAVA领...
    行径行阅读 2,133评论 0 3
  • 1.流的分类 (1)输入输出流输入输出是针对程序运行的内存而言的输入流的基类:InputStream,Reader...
    ql2012jz阅读 548评论 0 3
  • 一颗心最终变硬变冷 得被多少寒风围攻 跌多少跟头 打几次趔趄 才会距离缩短、音讯解封 暂且不得而知 我只会把自己磨...
    明子的世界阅读 273评论 1 8