我的try语句之后的所有内容都必须包含在try语句中以访问其中的变量吗?

时间:2010-04-30 16:30:48

标签: java exception exception-handling

我正在学习java,有一件事我发现我不喜欢,通常是我有这样的代码:

import java.util.*;
import java.io.*;

public class GraphProblem
{
    public static void main(String[] args)
    {
        if (args.length < 2)
        {
            System.out.println("Error: Please specify a graph file!");
            return;
        }


        FileReader in = new FileReader(args[1]);
        Scanner input = new Scanner(in);

        int size = input.nextInt();
        WeightedGraph graph = new WeightedGraph(size);

        for(int i = 0; i < size; i++)
        {
            graph.setLabel(i,Character.toString((char)('A' + i)));
        }

        for(int i = 0; i < size; i++)
        {
            for(int j = 0; j < size; j++)
            {
                graph.addEdge(i, j, input.nextInt());
            }
        }

        // .. lots more code

    }
}

我的FileReader周围有一个未捕获的异常。

所以,我必须将它包装在try-catch中以捕获该特定异常。我的问题是try { } 必须包含我想要使用我的FileReader(in)或我的Scanner(输入)的方法之后的所有内容吗?

如果我没有将该程序的其余部分包装在该try语句中,那么它之外的任何内容都无法访问in / input,因为它可能尚未初始化或已在其范围之外初始化。因此,我无法将try-catch隔离出来,只是说出初始化FileReader的部分,然后立即关闭try语句。

那么,让try语句包含将要访问其中初始化变量的代码的所有部分是“最佳实践”吗?

谢谢!

7 个答案:

答案 0 :(得分:9)

如果您对FileLader构造函数之后的包装代码感到满意,则可以在try / catch块之外声明FileReader,如下所示:

FileReader fr = null;
try
{
    fr = new FileReader(args[1]);
}
catch (IOException e)
{
    // handle
}
// code that uses fr

这是一个合理的设计,我经常使用它。请确保在以下代码中正确处理fr为空的可能性(即构造函数引发异常)。

答案 1 :(得分:3)

这不是try / catch块的问题。问题是变量作用域,并且由于已检查的异常,您必须具有try / catch块,从而建立新的作用域。

您还有另一种选择 - 从您的方法中将已检查的异常声明为throws

public static void main(String[] args) throws IOException {
    // ...code here...
}

这对于main方法来说是完全合法的。

如果你确实想要处理异常,就像你应该在一个更大的程序中一样。您可以围绕代码问题块定义特定的try / catch,并通过在该范围之外声明变量来使用范围之外的变量,因为许多人已经回答:

FileReader fr = null; // must be initialized here if the exception handling code 
                      // does not exit the method
try {
    fr = new FileReader(fileName);
} catch (IOException ex) {
    // log, print, and/or return
    // if you return or exit here then subsequent code can assume that fr is valid
}

您还可以将代码移动到另一个处理异常的方法:

private static FileReader openReader(String fileName) {
    try {
        return new FileReader(fileName);
    } catch (IOException ex) {
        // log/print exception
        return null; // caller must then expect a null
        // or
        throw new RuntimeException(...); // throw a RuntimeException of some kind (may not be good practice either)
    }
}

您还可以将文件处理代码移动到另一个方法。这可能会更好,并允许您更正确地遵循最终成语中的打开/关闭:

FileReader fr = null;
try {
    fr = new FileReader(fileName);
    Scanner input = new Scanner(fr);

    processInput(input);
} catch (IOException ex) {
    // log/print exception
} finally {
    if (fr != null) {
        try {
            fr.close();
        } catch (IOException ex) {
            // empty
        }
    }
}

private static void processInput(Scanner in) throws IOException {
    // ...processing here
}

对于close部分,您可以使用第三方库(Apache File Utils)或编写一个简单的方法来提供不会抛出异常的静态安全关闭方法。

你真的应该考虑将大型方法分解成更小的单元,这通常会为你提供更清晰的处理异常的方法。

答案 2 :(得分:2)

没有。您可以这样声明:

FileReader in = null;
Scanner input = null;

try {
   in = new FileReader(args[1]);
   input = new Scanner(in);
} catch(IOException ioe) {
   //
}

答案 3 :(得分:0)

是和否。 Try启动它自己的本地范围,所以当你这样做时:

try
{
   FileReader reader = new FileReader(args[1]);

   // Stuff
}
catch(Exception e) { }

reader变量仅在try {}块的约束内可见。这是首选行为。如果你想在外面使用它(在文件i / o的情况下它不被推荐)你需要在try / catch之外声明它。一个恰当的例子是一面旗帜:

boolean isValidFile = false;
try
{
   FileReader reader = new FileReader(args[1]);

   // Do Stuff

   isValidFile = true;
}
catch(Exception e)
{
   // Handle Errors
}

System.out.print("Valid File: ");
System.out.println(isValidFile);

答案 4 :(得分:0)

如果您刚学习,可以让您的方法抛出异常。它不是处理异常的好方法,但是如果你只是在学习,你可能希望更多地关注你的代码所做的逻辑。一旦你对此感到满意,那么你可以正确地研究处理异常。

因此,您的代码应如下所示,您不必担心try / catch语句:

public class GraphProblem {
    public static void main(String[] args) throws Exception {
        //your code
    }
}

同样,这不是最佳做法,但它允许您专注于业务逻辑而不是错误处理。一旦您熟悉了业务逻辑,那么您可以深入了解异常处理,这是一个自身的主题。

答案 5 :(得分:0)

block内声明的局部变量是scoped到该块。

  

那么,让try语句包含将要访问其中初始化变量的代码的所有部分是“最佳实践”吗?

通常,您应该尽量减少变量的范围,以使它们尽可能地缩小。 此问题已在Effective JavaCode Complete等内容中介绍。

此类代码是NullPointerExceptions

的配方
FileReader in = null;
try {
  in = new FileReader(filename);
} catch(IOException e) {
  //handle error, but now what?
}
// Code that needs "in", but what if it is null
// because of a FileNotFoundException?
// This code is junk.

实际上,所有局部变量声明都应包含赋值。

FileReader in = new FileReader(filename);

如果您遵循此经验法则,您最终会得到更好的代码。


// .. lots more code

如果你有巨大的try / catch语句,听起来你的方法太大了。这是一个代码组织问题,而不是专门针对try / catch的问题。

  public static void main(String[] args) {
    if (args.length < 1) {
      System.out.println("Error: Please specify a graph file!");
      return;
    }
    try {
      processGraphFile(args[0]);
    } catch (IOException e) {
      // Deal with error
    }
  }

  private static void processGraphFile(String filename) throws IOException {
    FileReader in = new FileReader(filename);
    try {
      Scanner scanner = new Scanner(in);
      processGraph(scanner);
      if (scanner.ioException() != null) {
        throw scanner.ioException();
      }
    } finally {
      in.close();
    }
  }

  //other methods

暂且不说:

  • 请注意,Java数组从索引0开始,而不是1
  • FileReader在学习时很方便,但由于编码问题通常应该避免;在代码离开您的计算机之前,这不太可能成为问题

答案 6 :(得分:-2)

你一定要遵循上面的模式。如果我从SCJP学习中记得,编译器不会尝试优化try块中的任何内容,所以你应该尽可能地尝试捕获。