how2j.cn


工具版本兼容问题
所有的流,无论是输入流还是输出流,使用完毕之后,都应该关闭。 如果不关闭,会产生对资源占用的浪费。 当量比较大的时候,会影响到业务的正常开展。


本视频是解读性视频,所以希望您已经看过了本知识点的内容,并且编写了相应的代码之后,带着疑问来观看,这样收获才多。 不建议一开始就观看视频



6分37秒
本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)



步骤 1 : 在try中关闭   
步骤 2 : 在finally中关闭   
步骤 3 : 使用try()的方式   
步骤 4 : 练习-关闭流   
步骤 5 : 答案-关闭流   

在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个弊端;
如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 不推荐使用
package stream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class TestStream { public static void main(String[] args) { try { File f = new File("d:/lol.txt"); FileInputStream fis = new FileInputStream(f); byte[] all = new byte[(int) f.length()]; fis.read(all); for (byte b : all) { System.out.println(b); } // 在try 里关闭流 fis.close(); } catch (IOException e) { e.printStackTrace(); } } }
步骤 2 :

在finally中关闭

edit
这是标准的关闭流的方式
1. 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally.
2. 在finally关闭之前,要先判断该引用是否为空
3. 关闭的时候,需要再一次进行try catch处理

这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的有隐患try的方式,因为不麻烦~
package stream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class TestStream { public static void main(String[] args) { File f = new File("d:/lol.txt"); FileInputStream fis = null; try { fis = new FileInputStream(f); byte[] all = new byte[(int) f.length()]; fis.read(all); for (byte b : all) { System.out.println(b); } } catch (IOException e) { e.printStackTrace(); } finally { // 在finally 里关闭流 if (null != fis) try { fis.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
步骤 3 :

使用try()的方式

edit
把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
这种编写代码的方式叫做 try-with-resources, 这是从JDK7开始支持的技术

所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。
package stream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class TestStream { public static void main(String[] args) { File f = new File("d:/lol.txt"); //把流定义在try()里,try,catch或者finally结束的时候,会自动关闭 try (FileInputStream fis = new FileInputStream(f)) { byte[] all = new byte[(int) f.length()]; fis.read(all); for (byte b : all) { System.out.println(b); } } catch (IOException e) { e.printStackTrace(); } } }
步骤 4 :

练习-关闭流

edit Or  姿势不对,事倍功半! 点击查看做练习的正确姿势
拆分文件 中关闭流的风格,修改成 finally 方式

合并文件 中关闭流的风格,修改成 try() 方式
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
查看本答案会花费3个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
查看本答案会花费3个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
账号未激活 账号未激活,功能受限。 请点击激活
本视频是解读性视频,所以希望您已经看过了本答案的内容,带着疑问来观看,这样收获才多。 不建议一开始就观看视频

3分36秒 本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)


package stream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; public class TestStream { public static void main(String[] args) { int eachSize = 100 * 1024; // 100k File srcFile = new File("d:/eclipse.exe"); // splitFile(srcFile, eachSize); } /** * 拆分的思路,先把源文件的所有内容读取到内存中,然后从内存中挨个分到子文件里 * * @param srcFile * 要拆分的源文件 * @param eachSize * 按照这个大小,拆分 */ private static void splitFile(File srcFile, int eachSize) { if (0 == srcFile.length()) throw new RuntimeException("文件长度为0,不可拆分"); byte[] fileContent = new byte[(int) srcFile.length()]; // 为了在finally中关闭,需要声明在try外面 FileInputStream fis = null; try { fis = new FileInputStream(srcFile); fis.read(fileContent); } catch (IOException e) { e.printStackTrace(); } finally { // 在finally中关闭 try { if(null!=fis) fis.close(); } catch (IOException e) { e.printStackTrace(); } } int fileNumber; if (0 == fileContent.length % eachSize) fileNumber = (int) (fileContent.length / eachSize); else fileNumber = (int) (fileContent.length / eachSize) + 1; for (int i = 0; i < fileNumber; i++) { String eachFileName = srcFile.getName() + "-" + i; File eachFile = new File(srcFile.getParent(), eachFileName); byte[] eachContent; if (i != fileNumber - 1) eachContent = Arrays.copyOfRange(fileContent, eachSize * i, eachSize * (i + 1)); else eachContent = Arrays.copyOfRange(fileContent, eachSize * i, fileContent.length); // 为了在finally中关闭,声明在try外面 FileOutputStream fos = null; try { fos = new FileOutputStream(eachFile); fos.write(eachContent); System.out.printf("输出子文件%s,其大小是%,d字节%n", eachFile.getAbsoluteFile(), eachFile.length()); } catch (IOException e) { e.printStackTrace(); } finally { // finally中关闭 try { if(null!=fos) fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 合并的思路,就是从eclipse.exe-0开始,读取到一个文件,就开始写出到 eclipse.exe中,直到没有文件可以读 * * @param folder * 需要合并的文件所处于的目录 * @param fileName * 需要合并的文件的名称 * @throws FileNotFoundException */ private static void murgeFile(String folder, String fileName) { File destFile = new File(folder, fileName); // 使用try-with-resource的方式自动关闭流 try (FileOutputStream fos = new FileOutputStream(destFile);) { int index = 0; while (true) { File eachFile = new File(folder, fileName + "-" + index++); if (!eachFile.exists()) break; // 使用try-with-resource的方式自动关闭流 try (FileInputStream fis = new FileInputStream(eachFile);) { byte[] eachContent = new byte[(int) eachFile.length()]; fis.read(eachContent); fos.write(eachContent); fos.flush(); } System.out.printf("把子文件 %s写出到目标文件中%n", eachFile); } } catch (IOException e) { e.printStackTrace(); } System.out.printf("最后目标文件的大小:%,d字节", destFile.length()); } }


HOW2J公众号,关注后实时获知布最新的教程和优惠活动,谢谢。


问答区域    
2019-06-12 分分合合
bbcno1



源文件的是:lianxi.txt,大小是:18719 拆分后的文件名是:lianxi.txt-0,大小是:2048 拆分后的文件名是:lianxi.txt-1,大小是:2048 拆分后的文件名是:lianxi.txt-2,大小是:2048 拆分后的文件名是:lianxi.txt-3,大小是:2048 拆分后的文件名是:lianxi.txt-4,大小是:2048 拆分后的文件名是:lianxi.txt-5,大小是:2048 拆分后的文件名是:lianxi.txt-6,大小是:2048 拆分后的文件名是:lianxi.txt-7,大小是:2048 拆分后的文件名是:lianxi.txt-8,大小是:2048 拆分后的文件名是:lianxi.txt-9,大小是:287 文件的总个数为:15 总长度为:18719 文件个数为:10 合并后的文件目录为F:\testFile\lianxi11\lianxi.txt,合并后的文件大小为:18719
package file;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;

public class TestStream1 {
	
	public static void main(String []args) {
		split();
		merge();
	}
	
	public static void split() {
//		找到一个大于100k的文件,按照100k为单位,拆分成多个子文件,并且以编号作为文件名结束。
//		比如文件 eclipse.exe,大小是309k。
//		拆分之后,成为 
//		eclipse.exe-0
//		eclipse.exe-1
//		eclipse.exe-2
//		eclipse.exe-3
		//之前在TestStream里用自己的方式做了一次,现在来用Arrays.copyOfRange再 来做一次
		//这一次用的文件只有19K,所以会以2K,或者3K来做拆分						
		File f = new File("F:\\testFile\\lianxi.txt");
		System.out.println("源文件的是:"+f.getName()+",大小是:"+f.length());
		//文件的总长度
		long allLength = f.length();
		//得到要写入的字节数据的长度
		int num = (int) (allLength / 1024 / 2 +1);
		//创建文件数据对象
		File files[] = new File[num];
		//初始化文件
		for (int i = 0; i < files.length; i++) {
			files[i] = new File(f.getParent(), f.getName()+'-'+i);
		}
		
		FileInputStream fis =null;
		FileOutputStream fos = null;
		try {
			//FileInputStream fis = new FileInputStream(f);
			fis = new FileInputStream(f);
			byte all[] = new byte[(int)allLength];
			fis.read(all);
			for (int i = 0; i < files.length; i++) {
				byte []temp = null;
				if(i == files.length-1) {
					temp = Arrays.copyOfRange(all, i*1024*2, (int)allLength);
				}else {
					temp = Arrays.copyOfRange(all, i*1024*2, (i+1)*1024*2);
				}
				//FileOutputStream fos = new FileOutputStream(files[i]); 
				fos = new FileOutputStream(files[i]); 
				fos.write(temp);
			}
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			if(null!=fis) {
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(null!=fos) {
				try {
					fos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
		for (File file : files) {
			System.out.println("拆分后的文件名是:"+file.getName()+",大小是:"+file.length());
		}
	}

	public static void merge() {
		//=========================================================================
//		把上述拆分出来的文件,合并成一个原文件。
//
//		以是否能正常运行,验证合并是否正确
		//先定义一下要合并的文件
		File tagf = new File("F:\\testFile\\lianxi11\\lianxi.txt");
		tagf.getParentFile().mkdirs();
		//假设并不知道要合并的个数,只知道名称样式
		//合并文件的总长度
		long totalLength = 0;
		//要合并的文件个数
		int num = 0;
		
		//遍历目录下我们要合并的文件
		File f = new File("F:\\testFile");
		//目录下的所有文件
		File []files = f.listFiles();
		System.out.println("文件的总个数为:"+files.length);
		//找出要合并的文件是那些
		for (int i = 0; i < files.length; i++) {
			if(files[i].getName().contains("lianxi.txt-")) {
				//得到文件的总长度
				totalLength +=files[i].length();
				//文件个数
				num++;
			}
		}
		System.out.println("总长度为:"+totalLength);
		System.out.println("文件个数为:"+num);
		//创建目标文件的字节数据,用上面找到的长度和个数来创建字节数组tagbb和文件数组files2
		byte tagbb[] = new byte[(int)totalLength];
		File files2[] = new File[num];
		//临时变量
		int j = 0;
		//写入内容,内容要从上面的文件中来
		for (int i = 0; i < files.length; i++) {
			if(files[i].getName().contains("lianxi.txt-")) {
				//得到文件的总长度
				files2[j]=files[i];
				//文件个数
				j++;
			}
		}
		//文件中的内容合并到tagbb中
		//FileInputStream fis = null;
		//这次循环只有循环files2的个数了
		for (int i = 0; i < files2.length; i++) {
			try (FileInputStream fis =new FileInputStream(files2[i])){
				//创建字节输入流
				//fis = new FileInputStream(files2[i]);
				//临时的字节数组
				byte []temp = new byte[(int)files2[i].length()];
				//读入内容
				fis.read(temp);
				//将内容依次写入tagbb中,利用System.arraycopy的功能,下次不要傻傻自己写方法了
				System.arraycopy(temp, 0, tagbb, i*1024*2, temp.length);
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	
			
		}
		
		//创建字节输出流
		try (FileOutputStream fos = new FileOutputStream(tagf)){
			fos.write(tagbb);
			fos.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		String str = "合并后的文件目录为%s,合并后的文件大小为:%d";
		System.out.printf(str,tagf.getAbsolutePath(),tagf.length());
		
	}
	

}

							





回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2019-06-03 方法2中为什么声明在try里作用域无法达到finally?
siyuanbella
如题




2 个答案

guaya 答案时间:2019-07-09
变量写在外面,里面再去实例化可以finally关闭流

葡萄葡萄 答案时间:2019-06-11
就跟一个类一个方法一样,一对花括号代表一个作用域,作用域内的变量只能作用域中用,除非做特殊处理



回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到




2019-05-31 拆组文件的标准关闭流
2019-05-26 ZZ交作业 关闭流的方式
2018-04-07 flush()的作用
2018-03-22 站长的类后面是不是忘记写 【implements AutoColseable】了???
2018-02-05 没积分了了,呜呜呜呜,需要购买吗?有没有优惠政策
2018-02-05 我没积分了,需要购买吗?呜呜呜呜呜
2017-12-06 finally方式关闭,请问为什么要加一个if做判断呢?
2017-11-30 这个页面的视频打不开,其他正常
2017-11-26 为什么把null写在前面?
2017-10-24 try中要关闭多个资源怎么办呢?
2016-07-29 问题见图




提问之前请登陆
提问已经提交成功,正在审核。 请于 我的提问 处查看提问记录,谢谢
关于 JAVA 中级-I/O-关闭流的方式 的提问

尽量提供截图代码异常信息,有助于分析和解决问题。 也可进本站QQ群交流: 1001964108
提问尽量提供完整的代码,环境描述,越是有利于问题的重现,您的问题越能更快得到解答。
对教程中代码有疑问,请提供是哪个步骤,哪一行有疑问,这样便于快速定位问题,提高问题得到解答的速度
在已经存在的几千个提问里,有相当大的比例,是因为使用了和站长不同版本的开发环境导致的,比如 jdk, eclpise, idea, mysql,tomcat 等等软件的版本不一致。
请使用和站长一样的版本,可以节约自己大量的学习时间。 站长把教学中用的软件版本整理了,都统一放在了这里, 方便大家下载: http://how2j.cn/k/helloworld/helloworld-version/1718.html

上传截图