【问题标题】:Java: error handling with try-catch, empty-try-catch, dummy-returnJava:使用 try-catch、empty-try-catch、dummy-return 处理错误
【发布时间】:2010-05-05 12:39:51
【问题描述】:

搜索使用递归定义的函数,很容易抛出异常。我尝试了 3 种方法来处理异常:

  1. 用 empty-try-catch() 忽略
  2. add-dummy-return 因异常而停止错误传播
  3. 抛出一个特定的例外。 (这部分我不太明白。如果我抛出异常,我可以强制它在其他地方继续,而不是继续旧的异常抛出路径吗?)

一些我并不真正关心的异常,例如在执行过程中删除的文件-异常(NullPointer),但有些我真的很喜欢未知的东西。

可能的例外情况:

    // 1. if a temp-file or some other file removed during execution -> except.
    // 2. if no permiss. -> except.
    // 3. ? --> except.

代码对整个程序非常重要。我之前添加了clittered-checks、try-catchs、avoided-empty-try-catchs,但它确实模糊了逻辑。这里的一些石头结果会使以后的代码更容易维护。由于一些随机临时文件删除,跟踪随机异常很烦人!您将如何处理关键部分的异常?

代码

public class Find
{
        private Stack<File> fs=new Stack<File>();
        private Stack<File> ds=new Stack<File>();
        public Stack<File> getD(){ return ds;}
        public Stack<File> getF(){ return fs;}

        public Find(String path)
        {
                // setting this type of special checks due to errs
                // propagation makes the code clittered
                if(path==null)
                {
                        System.out.println("NULL in Find(path)");
                        System.exit(9);
                }
                this.walk(path);
        }

        private void walk( String path )
        {
                File root = new File( path );
                File[] list = root.listFiles();

                //TODO: dangerous with empty try-catch?!
                try{
                        for ( File f : list ) {
                                if ( f.isDirectory() ) {
                                        walk( f.getAbsolutePath() );
                                        ds.push(f);
                                }
                                else {
                                        fs.push(f);
                                }
                        }
                }catch(Exception e){e.printStackTrace();}
        }
}

here.重构的代码

【问题讨论】:

    标签: java exception


    【解决方案1】:

    这是我可以编写的代码中最易读的:

    import java.util.*;
    import java.io.*;
    
    public class Find {
        List<File> files = new ArrayList<File>();
        List<File> dirs = new ArrayList<File>();
        List<Exception> excs = new ArrayList<Exception>();
        public Find(String path) {
            walk(new File(path));
        }
        void walk(File root) {
            for (File child : getChildren(root)) {
                if (isDirectory(child)) {
                    dirs.add(child);
                    walk(child);
                } else if (isFile(child)){
                    files.add(child);
                }
            }
        }
    

    (续)

        boolean isDirectory(File f) {
            try {
                return f.isDirectory();
            } catch (SecurityException e) {
                excs.add(e);
                return false;
            }
        }
        boolean isFile(File f) {
            try {
                return f.isFile();
            } catch (SecurityException e) {
                excs.add(e);
                return false;
            }
        }
        List<File> getChildren(File root) {
            File[] children;
            try {
                children = root.listFiles();
            } catch (SecurityException e) {
                excs.add(e);
                return Collections.emptyList();
            }
            if (children == null) {
                excs.add(new IOException("IOException|listFile|" + root));
                return Collections.emptyList();
            }
            return Arrays.asList(children);
        }
    }
    

    以下是一些重要的观察结果:

    • 无需检查path 是否为null
      • File(String pathname) 抛出 NullPointerException 如果 pathname == null
    • 不需要像原始代码那样从StringFileString 等。
      • 改为使用File
    • Effective Java 2nd Edition 第 25 条:列表优于数组
    • 可能抛出的File 方法被封装到非抛出的辅助方法中
      • 递归部分的主要逻辑就是这样干净的
    • File.listFiles()File.isFile()File.isDirectory(),每个 throws SecurityException
      • 事实证明,listFiles() 不是抛出 IOException,而是返回 null
        • 这是手动翻译成IOException
    • 如果捕获到任何异常,只需返回不会干扰walk 的内容
      • 来自getChildren() 的空列表
      • false 来自 isFile(File)isDirectory(File)
    • catch (Exception e) 一般都不好,所以我们只用catch (SecurityException e)
    • 您实际上可以使用日志框架代替excs.add 来记录异常

    【讨论】:

    • 您可能对我的比较感兴趣,顺便说一下 Apache 许可下的代码和等效代码。他们使用了linkedList:D
    【解决方案2】:

    忽略带有空捕获的异常通常是危险的。您必须确保您将捕获的异常对执行没有重要性。

    为了保持方法的逻辑干净,可以在另一个方法中提取错误处理代码。您可以在那里放置所有必要的代码来识别错误的来源并在需要时将其堆叠起来。

    catch(Exception e){
        handleException();
    }
    
    
    private void handleException throws Exception() {...}
    

    如果您关心跟踪递归中的异常,您可以在方法参数中携带一个列表来堆栈异常,并在执行完成后立即处理它们。

    private void walk(String path, List<Exception> listExceptions) {...}
    

    通过这种方式,您可以忽略子路径上的错误,同时跟踪它并继续在树的其余部分执行。

    【讨论】:

      【解决方案3】:

      取决于您希望如何处理错误。

      如果你现在用你的代码遍历一个包含 100 个文件的目录,而第二个文件导致异常,会发生什么?好吧,你在 System.out 中得到了一个堆栈跟踪,walk 方法将终止,没有任何事情发生。 Find.getF() 将只包含第一个文件,您的程序的其余部分不会知道出了什么问题。

      这可能不是你想要的样子?

      如果你知道你不关心一些错误(比如找不到文件),那么在你的循环中放置一个 try/catch 块。在 catch 正文中,您只需记录该特定文件到底出了什么问题,然后继续循环。通常你不想在这里记录完整的堆栈跟踪,只需要一行。

      如果你想以某种方式处理意外的异常,那么首先决定你想如何处理它们(只需登录并继续下一个文件?发送电子邮件?显示用户对话框?终止程序​​?),然后决定应该使用哪个类负责处理意外错误。

      如果您发现类的调用者应该处理意外错误,那么捕获并重新抛出您自己的异常是告诉调用者出现问题的好方法。 如果您决定 Find 类应该处理意外错误,则将处理放入您的 catch 块中。如果您希望循环在出现意外错误后继续,请移除外部 try/catch 并在循环内执行所有捕获操作。

      【讨论】:

        【解决方案4】:

        Poly-SO 混合代码与 Apache Commons FileUtils 的比较:iterateFiles 和 listFiles

        Apache commons-io 在 FileUtils 中有类似的方法 iterateFiles 和 listFiles,由 Bozho 建议。 参数检查以多种方式完成,但绝不是“System.exit(9)”! 他们比较为空,用文件类型检查它的存在(可用的方法), 他们在 listFiles-implementation 中使用静态的linkedList——poly 在书中建议。

        他们重用该字段来匹配所有目录:

        TrueFileFilter.INSTANCE 真实过滤器的单例实例(来自 Apache API,单例?)

        这两个方法是唯一使用 IOFileFilter 作为参数的方法。 我不确定它的含义。他们肯定可以重用他们的代码。

        有一些非常简洁——我认为不错——评估点,没有模糊的虚拟变量。 请看 (a?b:c) 的评估,省去愚蠢的傻瓜和 if 子句。

            return listFiles(directory, filter,
                (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE));
        

        方法所在的 FileUtils 类只有 4 个字段值——每个字段大约有 2.5 个方法! 现在我为我的班级感到羞耻。 一个显着的区别是异常的使用。 他们使用它们,但是——显然是由于 FileUtils 类的不同目标——他们让用户来处理它们,列表中没有集中的集合。 没有额外的声明。

        总结

        • 相似之处:linkedList 和列表
        • 区别:inits 更少,decs 更少,方法的字段密度更小——简洁
        • 不同的目标:SO 的类最终用户案例,FileUtils 更多的后端
        • 差异(自然):在 SO 中处理异常,但在 FileUtils 中没有(也许这就是它如此干净的原因)

        我喜欢 cmets,尤其是 source——我觉得,比起阅读琐碎的 API,更适合用于教育目的。希望你也:)

        Apache Commons:FileUtils.java、listFiles、iterateFiles - 代码片段

        /**
         * Finds files within a given directory (and optionally its
         * subdirectories). All files found are filtered by an IOFileFilter.
         * <p>
         * If your search should recurse into subdirectories you can pass in
         * an IOFileFilter for directories. You don't need to bind a
         * DirectoryFileFilter (via logical AND) to this filter. This method does
         * that for you.
         * <p>
         * An example: If you want to search through all directories called
         * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code>
         * <p>
         * Another common usage of this method is find files in a directory
         * tree but ignoring the directories generated CVS. You can simply pass
         * in <code>FileFilterUtils.makeCVSAware(null)</code>.
         *
         * @param directory  the directory to search in
         * @param fileFilter  filter to apply when finding files.
         * @param dirFilter  optional filter to apply when finding subdirectories.
         * If this parameter is <code>null</code>, subdirectories will not be included in the
         * search. Use TrueFileFilter.INSTANCE to match all directories.
         * @return an collection of java.io.File with the matching files
         * @see org.apache.commons.io.filefilter.FileFilterUtils
         * @see org.apache.commons.io.filefilter.NameFileFilter
         */
        public static Collection listFiles(
                File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
            if (!directory.isDirectory()) {
                throw new IllegalArgumentException(
                        "Parameter 'directory' is not a directory");
            }
            if (fileFilter == null) {
                throw new NullPointerException("Parameter 'fileFilter' is null");
            }
        
            //Setup effective file filter
            IOFileFilter effFileFilter = FileFilterUtils.andFileFilter(fileFilter,
                FileFilterUtils.notFileFilter(DirectoryFileFilter.INSTANCE));
        
            //Setup effective directory filter
            IOFileFilter effDirFilter;
            if (dirFilter == null) {
                effDirFilter = FalseFileFilter.INSTANCE;
            } else {
                effDirFilter = FileFilterUtils.andFileFilter(dirFilter,
                    DirectoryFileFilter.INSTANCE);
            }
        
            //Find files
            Collection files = new java.util.LinkedList();
            innerListFiles(files, directory,
                FileFilterUtils.orFileFilter(effFileFilter, effDirFilter));
            return files;
        }
        
        
        /**
         * Allows iteration over the files in given directory (and optionally
         * its subdirectories).
         * <p>
         * All files found are filtered by an IOFileFilter. This method is
         * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)}.
         *
         * @param directory  the directory to search in
         * @param fileFilter  filter to apply when finding files.
         * @param dirFilter  optional filter to apply when finding subdirectories.
         * If this parameter is <code>null</code>, subdirectories will not be included in the
         * search. Use TrueFileFilter.INSTANCE to match all directories.
         * @return an iterator of java.io.File for the matching files
         * @see org.apache.commons.io.filefilter.FileFilterUtils
         * @see org.apache.commons.io.filefilter.NameFileFilter
         * @since Commons IO 1.2
         */
        public static Iterator iterateFiles(
                File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
            return listFiles(directory, fileFilter, dirFilter).iterator();
        }
        

        //****剪掉一部分******//

        /**
         * Finds files within a given directory (and optionally its subdirectories)
         * which match an array of extensions.
         *
         * @param directory  the directory to search in
         * @param extensions  an array of extensions, ex. {"java","xml"}. If this
         * parameter is <code>null</code>, all files are returned.
         * @param recursive  if true all subdirectories are searched as well
         * @return an collection of java.io.File with the matching files
         */
        public static Collection listFiles(
                File directory, String[] extensions, boolean recursive) {
            IOFileFilter filter;
            if (extensions == null) {
                filter = TrueFileFilter.INSTANCE;
            } else {
                String[] suffixes = toSuffixes(extensions);
                filter = new SuffixFileFilter(suffixes);
            }
            return listFiles(directory, filter,
                (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE));
        }
        
        /**
         * Allows iteration over the files in a given directory (and optionally
         * its subdirectories) which match an array of extensions. This method
         * is based on {@link #listFiles(File, String[], boolean)}.
         *
         * @param directory  the directory to search in
         * @param extensions  an array of extensions, ex. {"java","xml"}. If this
         * parameter is <code>null</code>, all files are returned.
         * @param recursive  if true all subdirectories are searched as well
         * @return an iterator of java.io.File with the matching files
         * @since Commons IO 1.2
         */
        public static Iterator iterateFiles(
                File directory, String[] extensions, boolean recursive) {
            return listFiles(directory, extensions, recursive).iterator();
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-03
          • 1970-01-01
          • 2018-10-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多