黑马程序员技术交流社区

标题: 在学习IO流时易犯的一个小错误 [打印本页]

作者: obvilion    时间: 2016-9-2 23:41
标题: 在学习IO流时易犯的一个小错误
这个小问题是,今天我们小组的一个组员出现的;
之后陆续的在其它几个小组成员的代码上也出现了同样的问题;
所以我就提上一嘴吧.
组员代码如下
[AppleScript] 纯文本查看 复制代码

package com.heima.test;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
//文件过滤筛选拷贝:把指定目录下(不包含子目录)的所有图片,复制到另一个指定目录下(使用文件过滤器来过滤图片)
public class Lianxi2 {
        public static void main(String[] args) throws IOException {
                System.out.println("请输入指定复制源文件夹路径:");
                File file1 = getFile();
                System.out.println("请输入指定存放文件夹路径:");
                File file2 = getFile();
                //用一个集合来存放符合拷贝文件的绝对路径
                List<String> list = new ArrayList<>();
                //寻找所有符合条件的文件并将路径存入集合
                seek(file1,list);
                //从集合读取文件路径,复制到新的文件夹路径
                copy(file2,list);       
        }
        private static void copy(File file2, List<String> list) throws IOException {
                File[] filelist = file2.listFiles();
                if (filelist != null) {
                        for (String string : list) {
                                BufferedInputStream bts = new BufferedInputStream(new FileInputStream(string));
                                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file2));
                                int x;
                                while((x = bts.read()) != -1) {
                                        bos.write(x);
                                        bts.close();
                                        bos.close();

                                }
                        }
                }
        }
        private static void seek(File file1, List<String> list) {
                File[] filelist = file1.listFiles();
                if (filelist != null) {
                        for (File file : filelist) {
                                if (file.isFile() && file.getName().endsWith("jpg")) {
                                        list.add(file.getAbsolutePath());
                                }else if (file.isDirectory()) {
                                        seek(file,list);
                                }
                        }
                }else{
                        System.out.println("文件夹下为空,请检查后输入!");
                }
        }
        //测试样本路径file1                G:\图片\地图
        //测试接收路径file2                H:\新建文件夹
        private static File getFile() {
                Scanner sc = new Scanner(System.in);
                while(true) {       
                        String line = sc.nextLine();
                        File file = new File(line);
                        if (!file.isDirectory()) {
                                System.out.println("非文件夹路径,请重新输入:");
                        }else{
                                System.out.println("文件夹路径输入成功!");
                                return file;
                        }
                }
        }
}

红色部分为错误代码或者需要调整的部分,海军蓝标记的代码需要删除.
这段代码,创建了3个方法分别用于录入,迭代遍历和复制,思路很清晰,而且层次分明.
但就是由于对IO流的概念并没有理解透彻,照着Demo模板模仿下来,又希望添上一些自己的想法,结果demo运行没问题.
自己写的代码各种bug,而且在未能理解正确的概念之前,对着异常也半天难以找出错误.
下面我就来说这段代码的核心错误:
错误的理解:IO流操作的是File对象或者说路径;
正确的理解应该是:通过IO流实现内存与硬盘的交互时;IO流所操作的一定是文件!!!
可能会有人感到奇怪;所有IO流的构造方法里,不都是传File对象和路径的么?
的确在构造时传一个文件夹路径,或者说一个文件夹File对象;
在创建时,的确,编译器不会报错;
但是当在之后调用的write等方法时,一开始运行就会报错;
IO流在此时的目的只有一个就是对文件进行操作;
无论是传File对象还是传入一个路径,最终的目的,只是为了通过路径或者说File对象在机器的硬盘上
找到对应的文件,以便对其进行操作.
在输出路径上写个文件夹路径,难道说JVM会自动在那个文件夹中创建一个文件并拷贝过去么?
还是说,JVM会把文件内容直接写到文件夹上?这个JVM没有那么智能哈.
下面附上正确的代码;
[AppleScript] 纯文本查看 复制代码

package Test;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
//文件过滤筛选拷贝:把指定目录下(不包含子目录)的所有图片,复制到另一个指定目录下(使用文件过滤器来过滤图片)
public class Tr {
        public static void main(String[] args) throws IOException {
                System.out.println("请输入指定复制源文件夹路径:");
                File file1 = getFile();
                System.out.println("请输入指定存放文件夹路径:");
                File file2 = getFile();
                //用一个集合来存放符合拷贝文件的File对象
                List<File> list = new ArrayList<>();
                //寻找所有符合条件的文件并将File对象存入集合
                seek(file1,list);
                //从集合读取文件路径,复制到新的文件夹路径
                copy(file2,list);       
        }
        private static void copy(File file2, List<File> list) throws IOException {
//这里我们需要判断的不是file2是否为空,而是我们是否有需要拷贝的文件;即list是否为空
                if (!list.isEmpty()) {
//在外部创建变量;在内部创建对象,可以在拷贝完成之后一次性关流;
                        BufferedOutputStream bos = null;
                        BufferedInputStream bts = null;
                        for (File file : list) {
                                int x;
//读取的是文件对象
                                bts = new BufferedInputStream(new FileInputStream(file));
//写出的也是文件对象,但是通过得到目标文件夹的全路径名和被拷贝文件的文件名中间加上'\\'组成最终的拷贝路径
                                bos = new BufferedOutputStream(new FileOutputStream(file2.getAbsolutePath()+"\\"+file.getName()));
                                while((x = bts.read()) != -1) {
                                        bos.write(x);
                                }
                        }
                        bts.close();
                        bos.close();
                }
        }
        private static void seek(File file1, List<File> list) {
                File[] filelist = file1.listFiles();
                if (filelist != null) {
                        for (File file : filelist) {
                                if (file.isFile() && file.getName().endsWith("jpg")) {
                                        list.add(file);
                                }else if (file.isDirectory()) {
                                        seek(file,list);
                                }
                        }
                }else{
                        System.out.println("文件夹下为空,请检查后输入!");
                }
        }
        //测试样本路径file1                G:\图片\地图
        //测试接收路径file2                H:\新建文件夹
        @SuppressWarnings("resource")
        private static File getFile() {
                Scanner sc = new Scanner(System.in);
                while(true) {       
                        String line = sc.nextLine();
                        File file = new File(line);
                        if (!file.isDirectory()) {
                                System.out.println("非文件夹路径,请重新输入:");
                        }else{
                                System.out.println("文件夹路径输入成功!");
                                return file;
                        }
                }
        }
}

再次重复:正确的理解应该是,通过IO流实现内存与硬盘的交互时;IO流所操作的一定是文件!!!

作者: obvilion    时间: 2016-9-2 23:56
撒泼打滚求币
急求
作者: blueblueblue    时间: 2016-9-5 09:43
哈哈哈哈哈,学霸学霸学霸
作者: 初英    时间: 2016-9-5 11:09
IO流最容易犯的小错误就是忘了关流,往往在我们写一个关流和不关流都没区别的时候会遗忘关流
作者: wb277379322    时间: 2016-9-5 11:46
分析的很到位啊
作者: obvilion    时间: 2016-9-5 13:00
初英 发表于 2016-9-5 11:09
IO流最容易犯的小错误就是忘了关流,往往在我们写一个关流和不关流都没区别的时候会遗忘关流 ...

关流其实是小问题,容易查出来,我说的这个是理解上的错误,理解出错了,这次改了,在没有纠正错误观念之前还是会犯
作者: 赢无翳    时间: 2016-9-5 23:57
先标记一下回去慢慢看
作者: huihui520111    时间: 2016-9-6 00:19
对,,用心想想就知道,肯定是文件




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2