java.io包下,提供了一系列基于读写的快捷操作,以满足各种不同的需求。在此,io系统引入了流这个概念,它代表了任何有能力产出数据的数据源对象或者是有能力接收数据的接收端对象。流屏蔽实际的I/O设备中处理数据的细节。java的IO机制都是基于数据流进行输入输出。

流IO的好处是简单易用,缺点是效率较低。块IO的效率较高,但编程复杂。

下图是IO的层次结构:

io流存储用户信息 java java中io流重要么_结构

在整个IO包中,Reader,Writer,InputStream,OutputStrean是流式部分的核心类,也是整个IO中相当重要的概念。而Serializable,Flushable,Readable,Closeable是核心的接口,所有的流式操作都是他们的实现。

IO系统主要分为3个层次:

1.流式部分:IO的主体部分,大部分IO操作都可以在这里面进行。

2.非流式部分:主要是包含一些辅助流式部分的类,如File类等。

3.其他部分:为流式部分或者非流式部分提供操作接口和异常检测,如序列化接口等。


其他部分:

Serializable:

public interface Serializable {
}

类通过实现该接口以启动其序列化功能。未实现该接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或者字段,仅用于标识可序列化的语义。

对象序列化能让一个实现了该接口的对象转换为byte流,在要用到该对象的时候,把byte数据恢复出来,并且重构那个对象,从而实现该对象适应不同环境的传递。

需要序列化的情况:

1.想把内存中的对象保存到一个文件中或者数据库。

2.想用套接字在网络上传递对象。

3.想通过RMI传递对象。

Readable:

public interface Readable {
	public int read(java.nio.CharBuffer cb) throws IOException;
}

Readable是一个字符源,作为读取字符转换的缓冲区。缓冲区作为一个库的特点是操作的结果是唯一可以变化的,没有翻转或者缓冲复卷(及没有记忆保存)。


Closeable:

public interface Closeable extends AutoCloseable {
   public void close() throws IOException;
}

Closeable是可以关闭的数据源或目标。调用close()方法可以释放对象保存的资源(如打开文件)。关闭流并释放任何与它相关的系统资源。如果流已关闭然后调用该方法没有效果。


Flushable:

public interface Flushable {
	void flush() throws IOException;
}

Flushable是可刷新数据的目的地。调用flush方法将所有已缓冲输出写入底层流。

非流式部分:

File:

public class File
	implements Serializable, Comparable<File>

File分为文件和路径,作为最常见的数据源,很多时候我们需要将数据存储在文件中,也经常根据需要从指定的文件中进行数据的读取和修改。


canExecute() 
          测试应用程序是否可以执行此抽象路径名表示的文件。 
boolean canRead() 
          测试应用程序是否可以读取此抽象路径名表示的文件。 
boolean canWrite() 
          测试应用程序是否可以修改此抽象路径名表示的文件。 
boolean createNewFile() 
          当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。 
static File createTempFile(String prefix, String suffix) 
          在默认临时文件目录中创建一个空文件,使用给定前缀和后缀生成其名称。 
static File createTempFile(String prefix, String suffix, File directory) 
           在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。   
exists() 
          测试此抽象路径名表示的文件或目录是否存在。 
File getAbsoluteFile() 
          返回此抽象路径名的绝对路径名形式。 
String getAbsolutePath() 
          返回此抽象路径名的绝对路径名字符串。 
isAbsolute() 
          测试此抽象路径名是否为绝对路径名。 
boolean isDirectory() 
          测试此抽象路径名表示的文件是否是一个目录。 
boolean isFile()  
long length() 
          返回由此抽象路径名表示的文件的长度。 
String[] list() 
          返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。 
String[] list(FilenameFilter filter) 
          返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。 
File[] listFiles() 
          返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。 
File[] listFiles(FileFilter filter) 
          返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 
File[] listFiles(FilenameFilter filter) 
          返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 
static File[] listRoots() 
          列出可用的文件系统根。 
boolean mkdir() 
          创建此抽象路径名指定的目录。 
boolean mkdirs() 
          创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。 
boolean setReadable(boolean readable) 
          设置此抽象路径名所有者读权限的一个便捷方法。 
boolean setReadable(boolean readable, boolean ownerOnly) 
          设置此抽象路径名的所有者或所有用户的读权限。 
boolean setReadOnly() 
          标记此抽象路径名指定的文件或目录,从而只能对其进行读操作。 
boolean setWritable(boolean writable) 
          设置此抽象路径名所有者写权限的一个便捷方法。 
boolean setWritable(boolean writable, boolean ownerOnly) 
          设置此抽象路径名的所有者或所有用户的写权限。

创建文件例子:


public void operatorFile() {
		File file = null;
		try {
			// "d:\\file.txt" 和"d:/file.txt" 路径都可产生文件实例
			String path = "d:/file.txt";

			// 创建文件实例
			file = new File(path);
			// file = new File(new URI("file:/" + path));
			// file = new File("d:\\", "cat.txt");
			// file = new File(new File("d:\\"), "cat.txt");

			// 当实例对应的文件不存在时,创建新的文件
			if (!file.exists()) {
				// 新的文件
				file.createNewFile();
			}
			// 使用静态方法创建临时文件
			// File.createTempFile("hello", ".txt");
			File.createTempFile("hello", ".txt", new File("d:/"));

			path = "d:/f";
			// 创建文件夹
			file = new File(path);
			if (!file.exists()) {
				file.mkdir();
				// file.mkdirs();
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

RandomAccessFile:

public class RandomAccessFile implements DataOutput, DataInput, Closeable {
}

此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过 getFilePointer 方法读取,并通过 seek 方法设置。


void close() 
          关闭此随机访问文件流并释放与该流关联的所有系统资源。 
 FileChannel getChannel() 
          返回与此文件关联的唯一 FileChannel 对象。 
 FileDescriptor getFD() 
          返回与此流关联的不透明文件描述符对象。 
 long getFilePointer() 
          返回此文件中的当前偏移量。 
 long length() 
          返回此文件的长度。 
 int read() 
          从此文件中读取一个数据字节。 
 int read(byte[] b) 
          将最多 b.length 个数据字节从此文件读入 byte 数组。 
 int read(byte[] b, int off, int len) 
          将最多 len 个数据字节从此文件读入 byte 数组。 
 boolean readBoolean() 
          从此文件读取一个 boolean。 
 byte readByte() 
          从此文件读取一个有符号的八位值。 
 char readChar() 
          从此文件读取一个字符。 
 double readDouble() 
          从此文件读取一个 double。 
 float readFloat() 
          从此文件读取一个 float。 
 void readFully(byte[] b) 
          将 b.length 个字节从此文件读入 byte 数组,并从当前文件指针开始。 
 void readFully(byte[] b, int off, int len) 
          将正好 len 个字节从此文件读入 byte 数组,并从当前文件指针开始。 
 int readInt() 
          从此文件读取一个有符号的 32 位整数。 
 String readLine() 
          从此文件读取文本的下一行。 
 long readLong() 
          从此文件读取一个有符号的 64 位整数。 
 short readShort() 
          从此文件读取一个有符号的 16 位数。 
 int readUnsignedByte() 
          从此文件读取一个无符号的八位数。 
 int readUnsignedShort() 
          从此文件读取一个无符号的 16 位数。 
 String readUTF() 
          从此文件读取一个字符串。 
 void seek(long pos) 
          设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。 
 void setLength(long newLength) 
          设置此文件的长度。 
 int skipBytes(int n) 
          尝试跳过输入的 n 个字节以丢弃跳过的字节。 
 void write(byte[] b) 
          将 b.length 个字节从指定 byte 数组写入到此文件,并从当前文件指针开始。 
 void write(byte[] b, int off, int len) 
          将 len 个字节从指定 byte 数组写入到此文件,并从偏移量 off 处开始。 
 void write(int b) 
          向此文件写入指定的字节。 
 void writeBoolean(boolean v) 
          按单字节值将 boolean 写入该文件。 
 void writeByte(int v) 
          按单字节值将 byte 写入该文件。 
 void writeBytes(String s) 
          按字节序列将该字符串写入该文件。 
 void writeChar(int v) 
          按双字节值将 char 写入该文件,先写高字节。 
 void writeChars(String s) 
          按字符序列将一个字符串写入该文件。 
 void writeDouble(double v) 
          使用 Double 类中的 doubleToLongBits 方法将双精度参数转换为一个 long,然后按八字节数量将该 long 值写入该文件,先定高字节。 
 void writeFloat(float v) 
          使用 Float 类中的 floatToIntBits 方法将浮点参数转换为一个 int,然后按四字节数量将该 int 值写入该文件,先写高字节。 
 void writeInt(int v) 
          按四个字节将 int 写入该文件,先写高字节。 
 void writeLong(long v) 
          按八个字节将 long 写入该文件,先写高字节。 
 void writeShort(int v) 
          按两个字节将 short 写入该文件,先写高字节。 
 void writeUTF(String str) 
          使用 modified UTF-8 编码以与机器无关的方式将一个字符串写入该文件。

示例:

public void randomFile() {
		RandomAccessFile file;
		try {
			file = new RandomAccessFile("d:/file", "rw");
			// 以下向file文件中写数据
			file.writeInt(20);// 占4个字节
			file.writeDouble(8.236598);// 占8个字节
			file.writeUTF("这是一个UTF字符串");// 这个长度写在当前文件指针的前两个字节处,可用readShort()读取
			file.writeBoolean(true);// 占1个字节
			file.writeShort(395);// 占2个字节
			file.writeLong(2325451l);// 占8个字节
			file.writeUTF("又是一个UTF字符串");
			file.writeFloat(35.5f);// 占4个字节
			file.writeChar('a');// 占2个字节

			file.seek(0);// 把文件指针位置设置到文件起始处

			// 以下从file文件中读数据,要注意文件指针的位置
			System.out.println("——————从file文件指定位置读数据——————");
			System.out.println(file.readInt());
			System.out.println(file.readDouble());
			System.out.println(file.readUTF());

			file.skipBytes(3);// 将文件指针跳过3个字节,本例中即跳过了一个boolean值和short值。
			System.out.println(file.readLong());

			file.skipBytes(file.readShort()); // 跳过文件中“又是一个UTF字符串”所占字节,注意readShort()方法会移动文件指针,所以不用加2。
			System.out.println(file.readFloat());

			// 以下演示文件复制操作
			System.out.println("——————文件复制(从file到fileCopy)——————");
			file.seek(0);
			RandomAccessFile fileCopy = new RandomAccessFile("d:/fileCopy", "rw");
			int len = (int) file.length();// 取得文件长度(字节数)
			byte[] b = new byte[len];
			file.readFully(b);
			fileCopy.write(b);
			System.out.println("复制完成!");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}


流式部分:

流是一组有顺序的,有起始和终点的字节集合,是对数据传输的总称或抽象。

流的分类:

io流存储用户信息 java java中io流重要么_结构_02

字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取的。

字节流和字符流的区别:
1.读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
2.处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。

使用字符流,可以让效率提高些,所以优先考虑使用。

对输入流只能进行读操作,对输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。 

流的上层结构:

io流存储用户信息 java java中io流重要么_io_03

流类图结构:

io流存储用户信息 java java中io流重要么_结构_04

在流操作中,有读取的流,就有相对应的写入流一一对映。

对应关系:

io流存储用户信息 java java中io流重要么_字符流_05

下面介绍文件流相关的,也是使用最常见的操作。

FileInputStream:

FileInputStream 从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。 

FileInputStream 用于读取诸如图像数据之类的原始字节流。

FileInputStream(File file) 
          通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。 
FileInputStream(FileDescriptor fdObj) 
          通过使用文件描述符 fdObj 创建一个 FileInputStream,该文件描述符表示到文件系统中某个实际文件的现有连接。 
FileInputStream(String name) 
          通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。

操作方法:

int available() 
          返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。 
 void close() 
          关闭此文件输入流并释放与此流有关的所有系统资源。 
protected  void finalize() 
          确保在不再引用文件输入流时调用其 close 方法。 
 FileChannel getChannel() 
          返回与此文件输入流有关的唯一 FileChannel 对象。 
 FileDescriptor getFD() 
          返回表示到文件系统中实际文件的连接的 FileDescriptor 对象,该文件系统正被此 FileInputStream 使用。 
 int read() 
          从此输入流中读取一个数据字节。 
 int read(byte[] b) 
          从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。 
 int read(byte[] b, int off, int len) 
          从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。 
 long skip(long n) 
          从输入流中跳过并丢弃 n 个字节的数据。

可以看到,对文件都是字节的读取,没有对字符串等的更为快捷的操作。

示例:

FileInputStream in = new FileInputStream(file);
			byte[] b = new byte[15];
			in.read(b);
			System.out.println(new String(b));

FileOutputStream:


流的上层结构:


文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流。文件是否可用或能否可以被创建取决于基础平台。特别是某些平台一次只允许一个 FileOutputStream(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。 


FileOutputStream 用于写入诸如图像数据之类的原始字节的流。

FileOutputStream(File file) 
          创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 
FileOutputStream(File file, boolean append) 
          创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 
FileOutputStream(FileDescriptor fdObj) 
          创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一个到文件系统中的某个实际文件的现有连接。 
FileOutputStream(String name) 
          创建一个向具有指定名称的文件中写入数据的输出文件流。 
FileOutputStream(String name, boolean append) 
          创建一个向具有指定 name 的文件中写入数据的输出文件流。

操作方法:

void close() 
          关闭此文件输出流并释放与此流有关的所有系统资源。 
protected  void finalize() 
          清理到文件的连接,并确保在不再引用此文件输出流时调用此流的 close 方法。 
 FileChannel getChannel() 
          返回与此文件输出流有关的唯一 FileChannel 对象。 
 FileDescriptor getFD() 
          返回与此流有关的文件描述符。 
 void write(byte[] b) 
          将 b.length 个字节从指定 byte 数组写入此文件输出流中。 
 void write(byte[] b, int off, int len) 
          将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。 
 void write(int b) 
          将指定字节写入此文件输出流。

示例:

//boolean :true 写在文件末尾;false 写在文件开头。默认为false。
			FileOutputStream out=new FileOutputStream(file,true);
			out.write(b);

InputStreamReader:

int read() 
          读取单个字符。 
 int read(char[] cbuf, int offset, int length) 
          将字符读入数组中的某一部分。

BufferedReader:

从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。 
可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。 
通常,Reader 所作的每个读取请求都会导致对底层字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。例如, 
 BufferedReader in
   = new BufferedReader(new FileReader("foo.in"));
 将缓冲指定文件的输入。如果没有缓冲,则每次调用 read() 或 readLine() 都会导致从文件中读取字节,并将其转换为字符后返回,而这是极其低效的。 
通过用合适的 BufferedReader 替代每个 DataInputStream,可以对将 DataInputStream 用于文字输入的程序进行本地化。

BufferedReader(Reader in) 
          创建一个使用默认大小输入缓冲区的缓冲字符输入流。 
BufferedReader(Reader in, int sz) 
          创建一个使用指定大小输入缓冲区的缓冲字符输入流。

核心方法:

int read() 
          读取单个字符。 
 int read(char[] cbuf, int off, int len) 
          将字符读入数组的某一部分。 
 String readLine() 
          读取一个文本行。

跟其他的Reader不同,它有一个readLine()方法,可以大大的提高读取效率,使用时我们用其他的Reader来构造缓存流,然后按行读取,这样效率就会提高许多。

FileInputStream in = new FileInputStream(file);
			BufferedReader reader=new BufferedReader(new InputStreamReader(in));
			System.out.println(reader.readLine());

OutputStreamWriter:

OutputStreamWriter(OutputStream out) 
          创建使用默认字符编码的 OutputStreamWriter。 
OutputStreamWriter(OutputStream out, Charset cs) 
          创建使用给定字符集的 OutputStreamWriter。 
OutputStreamWriter(OutputStream out, CharsetEncoder enc) 
          创建使用给定字符集编码器的 OutputStreamWriter。 
OutputStreamWriter(OutputStream out, String charsetName) 
          创建使用指定字符集的 OutputStreamWriter。

方法:

void write(char[] cbuf, int off, int len) 
          写入字符数组的某一部分。 
 void write(int c) 
          写入单个字符。 
 void write(String str, int off, int len) 
          写入字符串的某一部分。

BufferedWriter:

将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。 
可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。 
该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性 line.separator 定义。并非所有平台都使用新行符 ('\n') 来终止各行。因此调用此方法来终止每个输出行要优于直接写入新行符。 
通常 Writer 将其输出立即发送到底层字符或字节流。除非要求提示输出,否则建议用 BufferedWriter 包装所有其 write() 操作可能开销很高的 Writer(如 FileWriters 和 OutputStreamWriters)。例如, 
 PrintWriter out
   = new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));
 将缓冲 PrintWriter 对文件的输出。如果没有缓冲,则每次调用 print() 方法会导致将字符转换为字节,然后立即写入到文件,而这是极其低效的。

BufferedWriter(Writer out) 
          创建一个使用默认大小输出缓冲区的缓冲字符输出流。 
BufferedWriter(Writer out, int sz) 
          创建一个使用给定大小输出缓冲区的新缓冲字符输出流。

方法:

void write(char[] cbuf, int off, int len) 
          写入字符数组的某一部分。 
 void write(int c) 
          写入单个字符。 
 void write(String s, int off, int len) 
          写入字符串的某一部分。

示例:

BufferedWriter write=new BufferedWriter(new FileWriter(file));
			write.write("hello");FileOutputStream out = new FileOutputStream(file, true);
			BufferedWriter write = new BufferedWriter(new OutputStreamWriter(
					out));
			write.write("hello");


BufferedReader和BufferedWriter:计算机访问外部设备非常耗时。访问外存的频率越高,造成CPU闲置的概率就越大。为了减少访问外存的次数,应该在一次对外设的访问中,读写更多的数据。为此,除了程序和流节点间交换数据必需的读写机制外,还应该增加缓冲机制。缓冲流就是每一个数据流分配一个缓冲区,一个缓冲区就是一个临时存储数据的内存。这样可以减少访问硬盘的次数,提高传输效率。所以尽量可能使用缓冲,这样的效率要高些。

标准IO:

程序的所有输入都可以来自于标准输入,所有输出也都可以来自于标准输出,所有的错误信息都可以发送到标准错误。标准IO的意义在于:我们很容易的把程序窜连起来,一个程序的标准输出可以作为另一个程序的标准输入。

java提供了Sytem.in,System.out,System.err。

控制台输入、输出时应用程序的基本功能:

·System.out提供向“标准输出”写出数据的能力(java.io.PrintStream类型)
·System.in提供从“标准输入”读入数据的能力(java.io.InputStream类型)
·System.err提供向“标准错误输出”写出数据的能力(java.io.PrintStream类型)

示例:

InputStream in = System.in;
		BufferedReader reader = new BufferedReader(new InputStreamReader(in));
		try {
			System.out.println(reader.readLine());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		PrintWriter writer = new PrintWriter(System.out,true);
		writer.println("hello");