黑马程序员——Java I/O系统

数据处理在程序内部执行,待处理的数据需要传入程序,处理后的数据可能会需要传出程序,数据在程序内部和外部之间的这种传输动作叫做IO操作。
与程序进行IO的端有控制台、文件(硬盘)、网络端口甚至是内存,对应包装的IO流对象有System.in、System.out,用文件名创建的输入输出流对象,由Socket获得的输入输出流对象、字节数组或者字符串等。

Java中字节流的基本框架如图一:

图一

字符流的基本框架如图二:


图二

如图中描述的,字节流的基本来源有由System.in或System.out获得键盘或控制台的流,由String或File对象获得文件的流(输入 或输出),由Socket(getInputStream()方法或getOutputStream()方法)获得网络流(输入或输出),由字节数组或字符串获得内存内部数据的流;字符流有由String或File对象直接获得的文件的流(输入或输出),有由字符数组或字符串获得内存内部数据的流。剩下的流基本都是对这些基本流的包装(构造时将这些基本流的对象传入,并将基本流对象作为该类的成员,建立各种针对该基本流对象成员的方法)。

一、字节流

字节输入流就是用来读取的,其读取方法有:

int read()读取一个字节,并将该字节放于返回的int值的最低字节中,若读到流末尾返回-1

int read(byte[] b)将流中的字节读入b字节数组,在流中数据足够的情况下读慢b数组,若读至流末尾返回-1

int read(byte[] b,int offset,int len)从流中读取最多len个字节,读入b字节数组中偏移为offset处。返回读取的字节数,若读到流末尾返回-1

字节输出流就是用来写入的,其写入方法有:

void write(int b)   将int值b的最低一个字节写入流

void write(byte[] b)将b中的数组全部写入流中

void write(byte[] b,int offset,int len)b字节数组中从0索引偏移offset处开始的len个字节,写入流中

包装流基本都包含基本流的这三种方法,但在此基础上又有其它方法。

1.Buffered流:

Buffered流都有一个缓冲区,用于在实际读取流和写入流之前进行数据缓冲。BufferedInputStream每次在调用读取方法时,都预先将其内部的缓冲区读满(如果流中数据足够的话),然后再从该缓冲区中读取,无论是读一个字节还是读若干字节到某个字节数组中,都是从该缓冲区数组中读取,并有指针和计数器用于标记缓冲区读取位置和剩余的未读取字节数。BufferedOutputStream每次在调用写入方法时,无论是调用写入一个字节还是写入一个字节数组,都将这些数据先写入内部的缓冲区,待调用flush()方法或关闭流时再将缓冲区中的数据写入流。缓冲流提高了读取和写入的效率。

2.Data流:

Data流(DataInputStream或DataOutputStream)提供了直接读取和写入基本数据类型的方法。该流很重要。

3.转换流:

通过以String的形式提供编码表,加入此编码表或使用默认的编码表将字节流包装成字符流。这是非常重要的流,用于将字节流转换成字符流。字符输入流在读取时能根据其编码表按照字符读取,字符输出流在写入时能按照其编码表将字符或字符数组写入流。

4.合并流:

该流用于将多个流合并,其本质类似于将多个流读取到一个流,故该流只限于输入流。

SequenceInputStream(Enumeration<? extends InputStream> e)

SequenceInputStream(InputStream in1,InputStream in2)

构造方法中用到的Enumeration<? extends InputStream>可以由Vector<InputStream>的elements()方法获得。被合并的流中除最后一个流之外,其它流的结束标记不再被当作结束标记,只保留最后一个流的结束标记。

5.打印流:

打印流顾名思义只限于输出流,打印流有特殊的方法集(print(...)或println(...)),能够直接将基本数据类型打印入流,这些打印方法和Data流中的将基本数据类型写入流的方法不同,Data流中写入方法是将基本数据类型直接按原字节内容写入(byte写入1个自己,short写入2个字节,int写4个字节,long写入8个字节......),而打印流的打印方法是将基本数据类型转换成字符串再写入这些字符串。另外打印流还有特殊的构造方法,打印流既能用字节输出流构造又能用字符输出流构造。

6.管道流:

管道流也可以理解成基本流对象,管道输入流是管道输出流的源,管道输出流是管道输入流的目的地,管道输入流.面向的方法是int read()或int read(byte[] b)等方法从管道的另一端读取数据,管道输出流面向的方法是void write(int b)或void write(byte[] b)等方法将字节或字节数组写入管道流并流向管道输入流。一般将管道流的两端放在不同的线程中。
构造管道流的方法有:
1)分别创建管道输入流和管道输出流,再通过connect(...)方法连接;
2)  创建管道流的一端,再在构造另一端时将此端传入。

二、字符流

System.in是字节流,System.out是打印流,这两个端口没有为字符流提供直接创建的类;此外,网络端口也只能直接获得字节流。但是可以通过String(提供文件路径或文件名)或File对象直接创建文件的字符流对象,还可以直接通过字符数组或字符串创建字符流对象。

其它非基本字符流类基本都是对基本字符流类的包装,即传入一个Writer对象或Reader对象。打印流仍然只有输出流,也仍然是既可以用字节流又可以用字符流创建。

字符流同字节流相比,读和写的数组由字节数组变为字符数组;字符缓冲流同字节缓冲流相比,BufferedWriter增加了void newLine()方法,BufferedReader增加了
String readLine()方法。

三、RandomAccessFile
该类不是继承自InputStream、OutputStream、Reader、Writer等的类,但它内部有IO流对象的成员,它只能用于为文件创建流。其构造方法有两种,都传入两个参数一个用于提供文件,一个用于提供开启文件的模式。可以用String提供文件名(包括路径)或用File对象提供文件,提供文件开启模式是用String变量,可选模式只有四种:
“r”——只读
“rw”——读写
“rws”——打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
“rsd”——打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。

该类十分类似C语言打开文件的方式。有打开模式,既能读又能写。
同时,RandomAccessFile还能调整流中指针的位置,用到的方法是void seek(long pos)(让文件指针位置移到从文件开头向后偏移pos个字节处)和int skipBytes(int n)(让文件指针从当前位置向后移动n个字节)。
同时,该类对象还提供了和DataStream一样的对基本数据类型的读和写的方法。

四、其它特性

输入流中,System.in、网络端口输入等的读取方法都是阻塞式方法,当未读到信息时,该方法会等,其它输入流的读取方法不是阻塞式的,当读到流末尾时,返回-1。

五、File

File类是文件或目录路径名的抽象表现形式,该类对象用来表示一个文件或文件夹。该类对象创建时,不一定对应的在存储设备上存在相应的文件或文件夹,File对象建立好后也不一定对应文件还是文件夹,对应文件还是文件夹的一个因素是在于存储设备上现有的同名File是文件还是文件夹,另一个因素是,若存储设备上没有该名的File,则使用createNewFile()方法后建立的是文件,使用mkdir()或mkdirs()方法后建立的就是文件夹,其中mkdir()只能创建一层目录,mkdirs()可以创建多层目录。

File类的构造方法:
File类构造时需要传入参数以提供文件或目录的绝对路径或相对路径。该路径可以单独以String形式提供也可以分成两部分提供,第一部分可以是File对象也可以是String,第二部分只能是String;又或者可以传入一个URL类对象以构造File。

File类的其它方法:
疑问: canExecute()对于存在的文件或目录都返回true;
对于文件调用setExecutable(false)都返回false,即不能设置成不可执行;
对于文件调用setReadable(false)都返回false,即不能设置成不可读。
setWritable(boolean b)和canWrite()方法都能正常执行。用于设置和判断可写性。
isFile()和isDirectory()用于判断是文件还是文件夹。
exists()方法用于判断是否存在。
另外还有一组很重要的方法:list  该组方法只用与目录也就是文件夹
String[]  list()
String[]  list(FilenameFilter f)
File[]  listFiles()
File[]  listFiles(FilenameFilter f)
File[]  listFiles(FileFilter f)
这些方法区别在于返回字符串还是File类对象,返回全部文件还是加以过滤。
由此引出FilenameFilter接口和FileFilter接口,两个接口都要求实现accept方法但是传入参数不同,FilenameFilter要实现boolean accept(File dir,String name),FileFilter要实现boolean accept(File pathname),list方法要的就是该accept方法返回的布尔值。
另外还有一个静态方法:static File[] listRoots()  用于返回当前系统的根目录的File数组。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章