前言与结论
在 java 项目开发中,文件流是接触很频繁的一类对象,这类对象使用完后一定要及时正确地关闭,否则会消耗服务器内存资源,那么怎样利用 close() 方法去最佳地关闭它们呢?这是本文的重点。为便于开发友友们解决问题,首先给出本文的结论:
1、如果多个文件流对象之间有依赖关系,那么只需关闭其中一个文件流即可(建议关闭最后一个使用的流),其他文件流也会自动关闭,不必书写一连串的 try-catch 关闭语句。(见情形一)
2、如果多个文件流对象之间没有依赖关系,那么需要手工逐个关闭,关闭顺序不限,这个时候需要书写一连串的 try-catch 关闭语句,也可以写一个通用的关闭方法来简化代码。(见情形二)
情形一、多个文件流对象之间有依赖关系
代码1:
import java.io.*;
public class ExceptionTest {
public static void main(String[] args) throws IOException {
//创建文件流
FileInputStream fileStream = new FileInputStream("D:\\迅雷\\迅雷下载\\电影集.txt");
InputStreamReader input = new InputStreamReader(fileStream);
BufferedReader reader = new BufferedReader(input);
//关闭文件流
//fileStream.close();
try {
//读文件流
reader.read();
System.out.println("文件流未关闭");
} catch (IOException e) {
e.printStackTrace();
}
}
}
结果1:
代码2:
import java.io.*;
public class ExceptionTest {
public static void main(String[] args) throws IOException {
//创建文件流
FileInputStream fileStream = new FileInputStream("D:\\迅雷\\迅雷下载\\电影集.txt");
InputStreamReader input = new InputStreamReader(fileStream);
BufferedReader reader = new BufferedReader(input);
//关闭文件流
fileStream.close();
try {
//读文件流
reader.read();
System.out.println("文件流未关闭");
} catch (IOException e) {
e.printStackTrace();
}
}
}
结果2:
代码3:
import java.io.*;
public class ExceptionTest {
public static void main(String[] args) throws IOException {
//创建文件流
FileInputStream fileStream = new FileInputStream("D:\\迅雷\\迅雷下载\\电影集.txt");
InputStreamReader input = new InputStreamReader(fileStream);
BufferedReader reader = new BufferedReader(input);
//关闭文件流
reader.close();
try {
//读文件流
fileStream.read();
System.out.println("文件流未关闭");
} catch (IOException e) {
e.printStackTrace();
}
}
}
结果3:
分析:
如上图三种情况,首先生成了三个流,如果不对三个流做 close 操作,那么这三个流都是能正常读取的,不会报错;对其中任意一个流做了 close 操作,另外两个流也都无法正常访问,报了异常;代码 2 与代码 3 就是为了证明当多个流有依赖关系时,关闭顺序是不限的,但是推荐只关闭最后使用的那个流(reader)。
情形二、多个文件流对象之间没有依赖关系
代码1:
import java.io.*;
public class ExceptionTest {
public static void main(String[] args) throws IOException {
//创建文件流
FileInputStream fileStream1 = new FileInputStream("D:\\迅雷\\迅雷下载\\电影集.txt");
FileInputStream fileStream2 = new FileInputStream("D:\\照片集\\1.jpg");
FileInputStream fileStream3 = new FileInputStream("D:\\照片集\\2.jpg");
//关闭文件流
fileStream1.close();
try {
//读文件流
fileStream1.read();
System.out.println("文件流未关闭");
} catch (IOException e) {
e.printStackTrace();
}
}
}
结果1:
代码2:
import java.io.*;
public class ExceptionTest {
public static void main(String[] args) throws IOException {
//创建文件流
FileInputStream fileStream1 = new FileInputStream("D:\\迅雷\\迅雷下载\\电影集.txt");
FileInputStream fileStream2 = new FileInputStream("D:\\照片集\\1.jpg");
FileInputStream fileStream3 = new FileInputStream("D:\\照片集\\2.jpg");
//关闭文件流
fileStream1.close();
try {
//读文件流
fileStream2.read();
System.out.println("文件流未关闭");
} catch (IOException e) {
e.printStackTrace();
}
}
}
结果2:
代码3:
import java.io.*;
public class ExceptionTest {
public static void main(String[] args) throws IOException {
//创建文件流
FileInputStream fileStream1 = new FileInputStream("D:\\迅雷\\迅雷下载\\电影集.txt");
FileInputStream fileStream2 = new FileInputStream("D:\\照片集\\1.jpg");
FileInputStream fileStream3 = new FileInputStream("D:\\照片集\\2.jpg");
//关闭文件流
fileStream3.close();
try {
//读文件流
fileStream1.read();
System.out.println("文件流未关闭");
} catch (IOException e) {
e.printStackTrace();
}
}
}
结果3:
分析:
从如上几幅图可知,互不依赖的多个流互不影响,关闭时只能手工逐个关闭,关闭顺序也不限,但逐个关闭的时候一定要注意不能把多个流的关闭语句放在同一个 try 语句块中,因为如果关闭其中一个流时出现了异常,那么后面几个流的关闭语句是不执行的,这就会带来内存溢出,最佳的关闭方式应该是一个 try-catch 语句只关闭一个流,多个流使用多个 try-catch ,并且关闭流的try-catch 语句块一定要写在 finally 之中,告诉程序这些流的关闭是必须要执行的,解决代码如下:
解决方案:
import java.io.*;
public class ExceptionTest {
public static void main(String[] args) throws IOException {
//申明变量并赋初始值,注意必须在try外面申明和赋值,否则finally中的这三个变量将无法访问
FileInputStream fileStream1 = null, fileStream2 = null, fileStream3 = null;
try {
//文件流赋值
fileStream1 = new FileInputStream("D:\\迅雷\\迅雷下载\\电影集.txt");
fileStream2 = new FileInputStream("D:\\照片集\\1.jpg");
fileStream3 = new FileInputStream("D:\\照片集\\2.jpg");
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} finally {
//确保必须执行文件流关闭语句
try {
if (fileStream1 != null)
fileStream1.close(); //关闭 fileStream1文件流
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fileStream2 != null)
fileStream2.close(); //关闭 fileStream2文件流
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fileStream3 != null)
fileStream3.close(); //关闭 fileStream3文件流
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
有读者觉得上述代码太过冗余和笨重,如果使用的流不止三个,那么逐个 try-catch 语句去关闭显得不雅观,这里再介绍一种最佳的解决方案,将关闭流的一大扒 try-catch 代码封装成一个方法,将每个要关闭的流作为变量传过去执行 close 就可以了,下面附上终极最佳方案:
终极最佳方案:
import java.io.*;
public class ExceptionTest {
public static void main(String[] args) throws IOException {
//申明变量并赋初始值,注意必须在try外面申明和赋值,否则finally中的这三个变量将无法访问
FileInputStream fileStream1 = null, fileStream2 = null, fileStream3 = null;
try {
//文件流赋值
fileStream1 = new FileInputStream("D:\\迅雷\\迅雷下载\\电影集.txt");
fileStream2 = new FileInputStream("D:\\照片集\\1.jpg");
fileStream3 = new FileInputStream("D:\\照片集\\2.jpg");
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} finally {
//通过调用关闭流的自定义方法来简化代码
streamClose(fileStream1, fileStream2, fileStream3);
}
}
//接收任意多个流变量,类型均为 AutoCloseable
public static void streamClose(AutoCloseable... stream) {
//循环流变量逐一调用close函数
for (AutoCloseable t : stream) {
if (t != null) {
try {
t.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}