一、深入讲解
流是在java中处理I/O的方式。
流由字节或者字符组成,你可以从流中读取字节或者字符,或者将其写到流中,还可以将它们连接到各种数据源或数据存储器,甚至是字节和字符数组。
1、流、读取器和写入器
引入流的概念使得处理不同的数据或者数据存储时的编程更统一。即无论你使用的是磁盘文件、内存缓冲区,还是网络,都可以用同样的方式处理输入和输出。
在java中,流I/O基于InputStream、OutputStream、Reader和Writer类。InputStream和OutputStream用于面向字节的流(比如在处理二进制文件时),Reader和Write类用于面向字符的流(比如在处理文本文件时)。
注意:不要直接使用这些类,而是应该从它们派生出新类。实际上java已经为你做了很多事,因此你可以使用十几类。
通常使用流类的构造器创建流,流通常分为输入流和输出流;在创建流对象后,可以使用它的方法(通常是read和write)传输数据。在使用过流对象后,通常使用close方法关闭并停止使用数据或者数据存储。
File类并不是流类,这个类并不处理文件的内容,而是提供关于文件的许多信息,比如文件的大小、许可权、日期等等。
2、file类
2.1 抽象路径名
用户界面和操作系统使用与系统相关的路径名字符串来命名文件和目录。
抽象路径名 有两个组件:
1、一个可选的与系统有关的前缀 字符串,比如盘符
2、零个或更多字符串名称 的序列
抽象路径名中的第一个名称是目录名,对于 Microsoft Windows UNC路径名则是主机名。抽象路径名中第一个名称之后的每个名称表示一个目录;最后一个名称既可以表示目录,也可以表示文件。空抽象路径名没有前缀和名称序列。
路径名字符串与抽象路径名之间的转换与系统有关。将抽象路径名转换为路径名字符串时,每个名称与下一个名称之间用一个默认分隔符隔开。
默认名称分隔符由系统属性 file.separator定义,可通过此类的公共静态字段separator和separatorChar使其可用。将路径名字符串转换为抽象路径名时,可以使用默认名称分隔符或者底层系统支持的任何其他名称分隔符来分隔其中的名称。
字段 | 作用 |
static String scparator | 与系统相关的默认的名称分隔字符,为了方便表示成字符串 |
static char scparatorChar | 与系统相关的默认的名称分隔字符 |
无论是抽象路径名还是路径名字符串,都可以是绝对路径名或相对 路径名。
绝对路径名是完整的路径名,不需要任何其他信息就可以定位它所表示的文件。相反,相对路径名必须使用取自其他路径名的信息进行解释。
默认情况下,java.io包中的类总是根据当前用户目录来解析相对路径名。
调用此类的getParent()方法可以获取抽象路径名的父 路径名,它由路径名前缀以及路径名名称序列中的每个名称(最后一个除外)组成。
String getParent() | 得到这个抽象路径名的父目录的抽象路径名;如果这个路径名没有命名父目录,那么返回null |
在处理 UNIX平台的根目录,以及 Microsoft Windows 平台的盘符、根目录和 UNC路径名时,将用到前缀这一概念。如下所示:
1、对于 UNIX平台,绝对路径名的前缀始终是 "/"。相对路径名没有前缀。表示根目录的绝对路径名的前缀为 "/"且名称序列为空。2、对于 Microsoft Windows平台,包含盘符的路径名前缀由驱动器号和一个 ":"组成。如果路径名是绝对路径名,还可能后跟 "\\"。UNC路径名的前缀是 "\\\\";主机名和共享名是名称序列中的前两个名称。没有指定驱动器的相对路径名没有前缀。
文件系统可以实现对实际文件系统对象上的某些操作(比如,读、写、执行)进行限制。这些限制统称为访问权限。
File类的实例是不可变的;也就是说,一旦创建,File对象表示的抽象路径名将永不改变。
2.2 File类的构造器
File(File parent, String child) | 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例 |
File(String pathname) | 通过将给定路径名字符串转换为抽象路径名来创建一个新 File实例 |
File(String parent, String child) | 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例 |
File(URI uri) | 通过将给定的 file: URI转换为一个抽象路径名来创建一个新的 File实例 |
2.3 File类的方法
暂时省略。。。
3、InputStream和OutputStream
InputStream和OutputStream类是java中面向字节的I/O的基类。
3.1 InputStream
InputStream类用于建立输入流
构造器:
InputStream():创建一个InputStreaam
方法:
int available() | 得到可以从这个输入流读取的字节数 |
void close() | 关闭这个输入流 |
void mark(int readlimit) | 在这个输入流的当前位置做标记 |
boolean markSupported() | 如果这个输入流支持mark和reset方法,则返回True |
abstract int read() | 从这个输入流读取数据的下一个字节 |
int read(byte[]b) | 从这个输入流读取一些字节并将其存储在缓冲区数组b中 |
int read(byte[]b,int offset,int len) | 从这个输入流读取最多len个字节的数据并将其存储在一个字节数组中 |
void reset() | j将这个输入流重新定位到上次调用mark方法的位置 |
long skip(void) | 跳过这个输入流中n个字节的数据 |
3.2 OutputStream
OutputStream用于建立输出流。
InputStream类用于建立输入流
构造器:
OutputStream():创建一个OutputStream
方法:
void close() | 关闭这个输出流 |
void flush(int readlimit) | 刷新这个输出流并将任何等待的缓冲输出字节写出 |
void write(byte[] b) | 将来自给定的字节数组的b.length个字节写到这个输出流 |
void write(byte[] b,int offset,int len) | 将给定的字节数组中的从偏移量offset开始的len个字节写到这个输出流 |
abstract void write(int b) | 将给定的字节写到这个输出流 |
现在我们已经了解了OutputStream和InputStream类,那么从下一个主题开始使用它们。
4、FileInputStream和FileOutputStream
4.1 FileInputStream类
我需要打开一个图像文件并操作其中的个别字节,我应该怎么做? 你可以使用FileInputStream类处理文件中的字节。
FileInputStream类的构造器如下表所示:
FileInputStream(File file) | 通过打开一个实际文件的连接构造一个FileInputStream |
FileInputStream(FileDescriptor fdObj) | 通过使用文件描述符fdObj构造一个FileInputStream |
FileInputStream(String name) | 通过打开一个实际文件的连接构造一个FileInputStream。name指定了文件名 |
FileInputStream类的方法如下表所示:
int available() | 得到可以从这个输入流读取的字节数 |
void close() | 关闭这个文件输入流 |
protected void finalize() | 当没有引用指向这个文件输入流时,确保调用它的close方法 |
FileDescriptor getFD() | 得到FileDescriptor对象 |
int read() | 从这个输入流读取一个字节的数据并返回它 |
int read(byte[] b) | 从这个输入流读取最多b.length个字节的数据到一个字节数组中,并且返回读取的字节数 |
int read(byte[] b,int offset,int len) | 从这个输入流读取最多len个字节的数据到一个字节数组中,并返回读取的字节数 |
long skip(long n) | 跳过并丢弃n个字节的数据 |
使用FileInputStream类的构造器创建一个文件输入流。这个类的读取方法如下:
- int read():从这个输入流读取一个字节的数据并返回它
- int read(byte[] b):从这个输入流读取最多b.length个字节的数据到一个字节数组中,并且返回读取的字节数
- int read(byte[] b,int offset,int len):从这个输入流读取最多len个字节的数据到一个字节数组中,并返回读取的字节数
让我们看一个例子,在这个例子中,我们打开这个应用程序自己的源代码文件,并读取并显示50个字节的代码,用skip()方法跳过50个字节,然后读取并显示50个字节。
import java.io.*
class fileInputStream{
public static void main(String args[ ]) throws Exception{
int size;
FileInputStream fileinputStream = new FileInputStream("fileInputStream.java");
System.out.println("Available bytes:" + (size - fileinputstream.available()));
System.out.println(Reading 50 bytes...");
byte bytearray[] = new byte[50];
if(fileinputStream.read(bytearray) != 50){
System.out.println("Could not get 50 bytes!");
}
System.out.println(new String(bytearray,0,50));
System.out.println("SKipping 50 bytes...");
fileinputStream.skip(50);
System.out.println(Reading 50 bytes...");
if(fileinputStream.read(bytearray) != 50){
System.out.println("Could not get 50 bytes!");
}
System.out.println(new String(bytearray,0,50));
fileinputStream.close();
}
}
4.2 FileOutputStream类
OK,我们知道应该如何使用FileInPutStream类从文件读,但是用哪个类写文件呢? 我打赌你应该能够猜到。
你可以使用FileOutputStream类逐字节地将数据写到文件中。这个类是从OutputStream类派生的。
FileOutputStream的构造器如下表所示。
FileOutputStream(File file) | 构造一个写到给定的File对象的文件输出流 |
FileOutputStream(FileDescriptor fdbj) | 构造一个写到给定的文件描述符的文件输出流 |
FileOutputStream(String name) | 构造一个附加到具有给定名称/路径的文件输出流 |
FileOutputStream的方法如下表所示:
void close() | 关闭这个文件输出流 |
protected void finalize() | 当没有引用指向这个文件输入流时,确保调用它的close方法 |
FileDescriptor getFD() | 得到FileDescriptor对象 |
void write(byte[] b) | 将来自给定的字节数组的b.length个字节写到这个文件输出流 |
void write(byte[] b,int off,int len) | 将给定字节数组中从偏移量off开始的len个字节写到这个文件输出流给定 |
void write(int b) | 将给定的字节写到这个文件输出流 |