IO流
1. 概述
Java中使用流技术来实现设备间数据的传输,其操作对象是数据,采用的方式是流(类似水流)。
2. 特点
- 按流的方向可分为输入流和输出流。
- 按流的内容可分为字节流和字符流。
- 按流的目标可分为键盘/屏幕,内存,文件和网络。
- 字节流的基类为InputStream/OutputStream,字符流的基类为Reader/Writer,且子类均以基类为后缀命名。
- 除了系统内置的标准输入输出流,其他流一般用完需要关闭,否则占用系统资源,特别是在多线程程序中导致效率低下。
字节流概述
- 字节流理论上可以处理任何数据。
- 字节流可以不经过缓冲直接向外围设备读写数据。
文件字节流
1. FileInputStream类
概述
- 源:文件,内容:字节,方向:输入。
- 可以将文件中的数据按单个字节读取,类型为int,若文件结束返回-1,否则返回该字节,高位补0。
- 可以将文件中的数据按多个字节读取到byte型数组中,若文件结束返回-1,否则返回读取到的字节数。
构造器
- FileInputStream(File file)
- FileInputStream(FileDescriptor fdObj)
- FileInputStream(String name)
常用方法
- 获取文件中还未读取剩余的字节数
int available() - 关闭流
void close() - 读取字节数据,文件末尾返回-1
int read()
int read(byte[] b)
int read(byte[] b, int off, int len) - 跳过读取字节数
long skip(long n)
示例
package io . bytestream ;import java . io . File ;import java . io . FileInputStream ;import java . io . IOException ;public class ByteFileInputDemo {public static void main ( String [] args ) throws IOException {File file = new File ( "temp\\file.txt" );// 一次读取一个字节FileInputStream fis = new FileInputStream ( file ); // FileNotFoundExceptionint ch = 0 ;while (( ch = fis . read ()) != - 1 ) { // IOException// FileInputStream.available(): 返回文件剩余字节数。System . out . println ( "available = " + fis . available () // IOException+ " ch = " + ( char ) ch );}fis . close (); // IOException// 有缓冲区的读取fis = new FileInputStream ( file );byte [] buf = new byte [ 1024 ]; // 长度可以定义成1024的整数倍。int len = 0 ;while (( len = fis . read ( buf )) != - 1 ) {System . out . println ( new String ( buf , 0 , len ));}fis . close ();}}
2. FileOutputStream类
概述
- 目标:文件,内容:字节,方向:输出。
- 可以选择新文件方式创建对象,也可以选择数据追加方式创建对象。
- 如果是新文件方式创建对象,如果该文件存在,则覆盖。
- 可以将文件中的数据按单个字节写入,类型为int,若文件结束返回-1,否则返回该字节,高位补0。
- 可以将文件中的数据按多个字节读取到byte型数组中,若文件结束返回-1,否则返回读取到的字节数。
构造器
- FileOutputStream(File file)
- FileOutputStream(File file, boolean append)
- FileOutputStream(String name)
- FileOutputStream(String name, boolean append)
常用方法
- 关闭流
void close() - 写入数据
void write(byte[] b)
void write(byte[] b, int off, int len) - 写入字节数据(int参数的低8位)
void write(int b)
示例
package io.bytestream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteFileOutputDemo {
private static final String LINE_SEPARATOR = System.lineSeparator();
public static void main(String[] args) {
File dir = new File( "temp");
if (!dir.exists()) {
dir.mkdir();
}
File file = new File( dir, "file.txt");
try {
fileOutput(file , "java" );
} catch (IOException e) {
System.out.println( "文件写入异常!" );
e.printStackTrace();
}
String str = LINE_SEPARATOR + "itheima";
appendFileOutput(file , str );
}
public static void appendFileOutput(File file, String data) {
// 1. fos需要在finally中释放文件,所以 fos声明必须在try-catch语句之外。
FileOutputStream fos = null;
try {
// 2. 创建对象和文件写入均可能有异常发生。
// FileOutputStream(File file, boolean append);续写。
fos = new FileOutputStream( file, true);
fos.write(data.getBytes());
} catch (IOException e) {
System.out.println( "文件创建异常!" );
e.printStackTrace();
} finally {
// 3. 关闭文件也可能有异常发生,这里由于不确定 fos是否成功创建对象,所以需要判断。
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
// 4. 一般地,关闭文件异常转换为RuntimeException强制终止程序。
System. out.println("文件关闭异常!" );
throw new RuntimeException();
}
}
}
}
public static void fileOutput(File file, String data) throws IOException {
// 输出流目的是文件,会自动创建。如果文件存在,则覆盖。
FileOutputStream fos = new FileOutputStream( file);
fos.write(data.getBytes());
fos.close();
}
}
缓冲字节流
1. BufferedInputStream类
概述
该类是InputStream类的带缓冲包装类,提供缓冲功能。
构造器
- BufferedInputStream(InputStream in)
- 指定缓冲区大小构建流
BufferedInputStream(InputStream in, int size)
常用方法
与FileInputStream类似
2. BufferedOutputStream类
概述
该类是OutputStream类的带缓冲包装类,提供缓冲功能。
构造器
- BufferedOutputStream(OutputStream out)
- 指定缓冲区大小构建流
BufferedOutputStream(OutputStream out, int size)
常用方法
与FileOutputStream类似
- 将缓冲区中的数据强制写入文件
void flush()
注意
- 读取单个字节时,返回类型为int,这时低8位是读取到的数据,如果到流的末尾,则返回-1。
- 写入单个字节时,写入的数据类型为int,但是只有低8位会被写入。
- 流中的byte和int之间的转换不是一般的数据类型转换,数据符号位不影响最终数据,即可以看成是无符号数据。
- 缓冲区的作用是尽可能避免外存设备和内存设备速度差别所造成的资源浪费。
- 带缓冲字节流原理是内置了一个缓冲区,每次读取或写入数据就必须经过缓冲区,每次读取都将读取缓冲区长度的数据,而写入时是当缓冲区满,或者flush亦或是close执行时,才将缓冲区数据写入外存,这样避免了外存的频繁访问,提高速率。
- BufferedInputStream.read(byte[])方法为InputStream类中的方法,并没有覆盖,如何使用BufferedInputStream类中的缓存呢?
因为结构如下:
BufferedInputStream.read(byte[])调用
->FilterInputStream.read(byte b[])调用
->FilterInputStream.read(byte b[], int off, int len)调用
->InputStream.read(byte b[], int off, int len),函数中调用
->InputStream.read(),而该方法为抽象方法,最终调用
->BufferedInputStream.read() - 必要时才使用flush(常在多线程并发访问时使用),频繁使用flush将降低写入效率,并且流不用时应及时关闭。
- 异常处理
a. 由于流必须关闭,所以应在finally中执行。
b. 因为流对象的建立可能报FileNotFoundException,而流关闭必须在finally中执行,所以流变量定义必须在try前,而对象的创建在try中。
c. 如果流没有创建成功,这时将无法进行关闭动作,所以finally中必须进行判断。
d. 一般流关闭错误较为严重,一般在catch中再次抛出RuntimeException终止程序。
案例
复制文件
package io.bytestream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteFileCopy {
public static void main(String[] args) {
File oldFile = new File( "temp//byte.ape");
File newFile1 = new File( "temp//byte_copy1.ape");
File newFile2 = new File( "temp//byte_copy2.ape");
// 使用自定义缓冲区复制文件
try {
copyFile(oldFile , newFile1 );
} catch (IOException e) {
e.printStackTrace();
}
// 使用BufferedInputStream和BufferedOutputStream复制文件
try {
copyFileUseBuf(oldFile , newFile2 );
} catch (IOException e) {
e.printStackTrace();
}
}
// 使用自定义缓冲区复制文件
public static void copyFile(File oldFile, File newFile) throws IOException {
FileInputStream oldFIS = new FileInputStream(oldFile);
FileOutputStream newFOS = new FileOutputStream(newFile);
byte[] buffer = new byte[1024];
int len = -1;
while (( len = oldFIS.read( buffer)) != -1) {
newFOS.write(buffer, 0, len);
}
oldFIS.close();
newFOS.close();
}
// 使用BufferedInputStream和BufferedOutputStream复制文件
public static void copyFileUseBuf(File oldFile, File newFile) throws IOException {
FileInputStream oldFIS = new FileInputStream(oldFile);
FileOutputStream newFOS = new FileOutputStream(newFile);
BufferedInputStream bis = new BufferedInputStream(oldFIS);
BufferedOutputStream bos = new BufferedOutputStream(newFOS);
int ch = -1;
while (( ch = bis.read()) != -1) {
bos.write(ch);
}
// 使用双层缓冲可以进一步提高效率,减少函数调用的开销
byte[] buffer = new byte[1024];
int len = -1;
while (( len = bis.read( buffer)) != -1) {
bos.write(buffer, 0, len);
}
bis.close();
bos.close();
}
}