java IO流详解(上)

编程语言的I/O类库中常使用这个抽象概念,他代表了任何有能力产出数据源对象或有能力接收数据的接收端对象。流屏蔽了实际的I/O设备中处理数据的细节。

(一)I/O流层次结构


IO流层次图
这张是整个IO类库的结构,我们常用的仅是其中的几种。整个IO类库分为两大部分:InputStream与OutputStream。
InputStream中的类可以从外部读取内容,称为输入;OutputStream中的类可以从本地传出内容,成为输出。将输入与输出相结合,这是我们组织代码的基本思路。我们能读取的内容非常广泛,有时一谈及IO就会和文件相结合,这是不正确的思路。IO类库可以在下列这些内容上运用:

  1. 字节数组
  2. String对象
  3. 文件
  4. 管道
  5. Internet连接
  6. 其他各种种类的流组成序列

不仅仅是文件,在各种流上我们都可以用上面的InputStream、OutputStream以字节的形式进行操作。随着时间的推移,大家发现上面的类库有一定得局限性,仅仅用字节的形式读取不便于我们的操作,所以又衍生出了下面Reader/Writer这些类,这些类是在字符的级别上进行操作。
Reader与Writer层次结构

(二)InputStream与OutputStream类方法


InputStream类和OutputStream类是输入和输出类的抽象类祖先,这两个类中包含了下面这些流操作类的一些基本的方法,比如:read方法和write方法。继承了这两个类的子类中的read和write方法也是大同小异。InputStream拥有read方法,OutputStream拥有write方法。

1.read方法、write方法

read方法有三种重载形式。

read()
read(byte[] b)
read(byte[] b, int off ,int len)

第一种无参数的方法会向下继续读一个字节,返回一个int字节值,如果结束读不到返回-1。
第二种方法从流中读取一定数量的字节,并将其放在缓冲字节数组b中,以int类型返回实际读取的字节数。在读取的时候会按照b数组的顺序存放,比如,第一个字节会存放在b[0]中,下一个放在b[1]中,以此类推。读取的字节最多等于b数组的长度。默认相当于read( b, 0 , b.length )方法。
第三种方法,可以通过off参数指定数组偏移量,总共一次读取的字节数小于等于len(读到结束可能使总字节数小于len),推荐用这种方式来精确的指定读取,而不采用第二种较为模糊的方法。
read方法在输入数据可用之前会一直被阻塞,直到确实进行了读入操作。这一点也导致了在多线程中其他线程可以有机会去执行其他工作。

write方法的参数和read相同。只不是是从b数组向输出流写出内容。

2.available方法

available方法是用来检查剩余可读入的字节数量的。

public int available() throws IOException

int bytesAvailable = in.available();
if(bytesAvailable>0){
    byte[] data = new byte[bytesAvailable];
    in.read(data);
}

如果我们在代码中运用这种写法,那么我们每次在读入之前进行检查,就不会有阻塞发生。

3.close方法

public void close() throws IOException

有人会问,java中有这么完备的垃圾回收机制,我们为什么还需要用close去关闭资源呢?java自己去解决不就可以了么?这句话只说对了一半,java中有关流的一些残余的内容确实会被自动回收,但是打开流时会在操作系统上分配资源,而我们通过close是为了去释放十分有限的操作系统资源。

(三)InputStream、OutputStream的子类


我们上面列出了六种可以通过IO类库处理的流信息,每一种流都可以用相应的InputStream子类来处理。另外FilterInputStream也属于InputStream的一个子类,对于这个特殊的包装类,我们在之后会单独讨论。(你在上面的图中会发现,FilterInputStream类很特殊,他有子类,而其他的InputStream的子类都不再有子类了)

1.ByteArrayInputStream类

这个类可以允许将内存的缓冲区当作InputStream使用。这个类的作用是作为一种数据源,将其与FilterInputStream对象相连以提供有用接口。

2.StringBufferInputStream类

这个类将String转换成InputStream类型。这个类的作用是作为一种数据源,将其与FilterInputStream对象相连以提供有用接口。

3.PipedInputStream类

一般在多线程中我们会使用这样的“管道化”数据来操作。这个类的作用是作为一种数据源,将其与FilterInputStream对象相连以提供有用接口。

4.SequenceInputStream类

这个类可以将两个或多个InputSream对象转换成单一的InputStream对象。这个类的作用是作为一种数据源,将其与FilterInputStream对象相连以提供有用接口。

上面的四种输入流,是我们平时使用较少的。还有FileInputStream,ObjectInputStream等我们要做详细介绍。而OutputStream类的子类也是上面相应类的输出,用途功能相近。

(四)组合流过滤器


上面已经介绍了一些InputStream的子类,这些子类获取的是各不相同的流,但是获取之后他们都是InputStream类型的对象。当得到这些流对象时,我们却发现我们处理流对象的手段极度缺乏。根据往常的经验我们会把各种操作流对象的方法打包封装,java也是这么做的,但是只不过,这个类就是InputStream的子类——FilterInputStream。(FilterOutputStream同理)FilterInputStream,FilterOutputStream是一种包装器,向原生字节流中添加额外的功能。

FileInputStream可以通过提供File类型对象或String文件路径字符串来创建文件输入流。

FileInputStream fis = new FileInputStream("example.txt");

java.io类中将相对路径解释为以这个工作目录开始,如果需要工作目录使用System.getProperty(“user.dir”)方法获取。

我们通过使用FileInputStream类获得了fis对象,代表着我们可以从fis中获取文件的内容。现在我们想要将内容中的基本类型读取出来,但是FileInputStream类中没有这种方法,我们需要使用FilterInputStream类的子类DataInputStream进行细节的操作

正如FileInputStream类中没有任何读入基本数值类型的方法一样,在DataInputStream类中也没有任何从文件中读取数据的方法,只有将读取流的类和装饰类相结合才能达成我们的目的。

所有的FilterInputStream的子类都是接受一个InputStream类对象作为构造器的参数。

FileInputStream fis = new FileInputStream("example.txt");
DataInputStream dis = new DataInputStream(fis);
double d = dis.readDouble();

通常,我们上面的这种做法会带来一个问题:每当我们要调用readDouble(或者调用dis的其它读取方法)的时候,fis中都要调用read方法来获取字节,这样每次去操作系统申请内存的方法费时费力。为了让我们的速度变快,我们可以先在缓冲区申请一个数据块存放数据。BufferedInputStream类就是做这个工作的。

FileInputStream fis = new FileInputStream("example.txt");
BufferedInputStream bis = new BufferedInputStream(fis)
DataInputStream dis = new DataInputStream(bis);
double d = dis.readDouble();

(五)FilterInputStream子类


这些FilterInputStream的子类都是通过在构造器传入一个InputStream类型来获取需要处理的流信息。

1.DataInputStream类

这个类与DataOutputStream类搭配使用,所以我们可以按照可移植的方式从流读取基本数据类型(int,char,long等)。

2.BufferedInputStream类

使用这个类可以防止每次读取时都要进行实际的写操作,代表使用缓冲区的意思。

3.LineNumberInputStream类

可以通过这个类来跟踪输入流中的行号,这个类没有对应的OutputStream方法。可以调用getLineNumber方法和setLineNumber方法。

4.PushbackInputStream类

具有能弹出一个字节的缓冲区,因此可以将读到的最后一个字符回退。很少使用。

FilterOutputStream的子类最经常使用的有这几种:BufferedOutputStream、DataOutputStream方法。还有PrintStream方法渐渐被我们之后要说的PrintWrite方法取代。

(六)Reader与Writer


因为面向字节的InputStream和OutputStream类不便于处理多字节表示字符的Unicode信息,所以java抽象出了Reader和Writer类作为专门仅处理字符的类,这些类拥有的读入和写出操作都是基于两字节的Unicode码元的,而不是基于单字节的字符。

有的时候我们必须把来自于“字节”层次结构中的类和”字符“层次结构中的类结合起来使用,为了实现这个转换的目的,要用到InputStreamReader类和OutputStreamWirter类,前者是将InputStream转换为Reader类,而后者是将OutputStream类转换为Wirter类。

新类库(Reader与Writer)的设计使得它比老类库(InputStream与OutputStream)速度更快。

从理论上来讲,老类库的每个类和新类库相对应的类的功能是相似的,我们在这里不可能把每个类的具体方法都进行介绍。其中我们需要注意的是,DataInputStream在这两者中没有变化,它仍旧是我们读取基本类型数据的首选,但是这个类不支持readLine方法,如果我们一定要使用这种读取方法,最好使用BufferedReader类。

(七)PrintWriter文本输入与输出


有的时候我们想要在代码中将我们的文本输出到文件或者其他的流中。这是我们可以使用PrintWriter类。这个类拥有以文本格式打印字符串和数字的方法,它甚至还有一个将PrintWriter链接到FileWirter的便捷方法,如果我们

PrintWriter pw = new PrintWriter("example.txt");

等同于:

PrintWriter pw = new PrintWriter(new FileWeiter("example.txt"));

我们为了能将内容输出到打印写出器pw中,需要使用与使用System.out相同的print、println、printf方法。

println方法在行中添加了对目标系统来说恰当的行结束符,就是通过调用System.getProperty(“line.separator”)换行。

那么现在我们知道:
1. DataOutputStream,用二进制格式写出数据
2. PrintWriter,用文本格式写出数据

那么我们可能会认为,存在着那么一种类似DataInputStream的类允许我们以文本格式读入数据。事实上,我们可以用BufferedReader类readLine方法读入文本输入。但是BufferedReader类却没有任何读入数字的方法,所以我们还是建议使用Scanner类来读入文本输入。

  1. DataOutputStream,用二进制格式写出数据
  2. PrintWriter,用文本格式写出数据
  3. DataInputStream ,读入基本类型数值
  4. Scanner,读入文本输入
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值