【问题标题】:Java: exceptions and 'might not have been initialized'Java:异常和“可能尚未初始化”
【发布时间】:2012-04-07 17:45:20
【问题描述】:

我对 Java 很陌生。我正在编写一个程序来读取文件,计算其 SHA1 校验和,并将结果写入另一个文件。在出现任何错误时,我都会调用一个函数 err_exit(),它会向 stderr 打印一条消息,并通过调用具有指定退出状态的 System.exit() 来终止执行。这大概是我的main() 函数的样子:

public static void main(String[] args) {
    String in_fname = "C:/tmp/test.txt"; // not reading args yet
    String out_fname = "C:/tmp/test.sign";
    byte[] file_data;
    String hexdigest;

    try {
        file_data = readFileAsByteArray(in_fname);
    }
    catch (java.io.IOException ex) {
        file_data = new byte[] {0};  // note this line well, please
        err_exit(2, "error opening input file '" + in_fname + "'");
    }

    try {
        hexdigest = hexdigestSha1(file_data);
    }
    catch (NoSuchAlgorithmException ex) {
        hexdigest = "";  // note this line well, please
        err_exit(3, "could not compute SHA1 message digest!");
    }

    try {
        writeFileFromString(out_fname, hexdigest);
    }
    catch (java.io.IOException ex) {
        err_exit(2, "error writing output file '" + out_fname + "'");
    }

    System.exit(0); // success
}

有两行我要求你注意。这两行的存在只是为了防止编译器抱怨变量可能没有被初始化。

就编译器所知,catch 块可能会继续。实际上err_exit() 永远不会返回,因此不会传递无效值。

那么,我的问题是:处理这类事情的常用 Java 习语是什么? try/catch 块的行有点难看;你会建议我让各种函数调用err_exit() 并且没有像这样显式的代码吗?我想我更喜欢显式检查,main() 函数是进行检查的正确位置,但我对反馈感兴趣。

如果我要使用 try/catch 块,这是消除编译器警告的好方法吗?

如果我在 Python 中执行此操作,我可能不会捕获异常,并让程序停止并返回堆栈跟踪。错误的堆栈跟踪不会让这个程序的用户感到震惊,因为那个用户就是我。我突然想到,如果我将main() 函数声明为throws Exception,那么我就无法捕获异常,它的行为类似于 Python。这是一个可怕的想法,会让思想正确的 Java 人回避我吗?

附:如果您有最喜欢的书/网页/我应该阅读的任何 Java 习语,请提及。

编辑:对于带有下划线的变量名,我深表歉意。我已经在我的真实程序中重命名了它们,但我将把它们留在这里。这实际上是因为我花了很多时间在 Python 和 C 编程;我使用的是 Python “PEP 8” 样式或通用 C 样式,请自行选择。

【问题讨论】:

  • 张贴声明这两个变量的行。
  • 我添加了缺失的东西以使其成为一个完整的功能。它看起来更好,因为 StackOverflow 现在正在做语法着色......以前不是!

标签: java


【解决方案1】:

就我个人而言,我喜欢“扔空;”在调用一个根本无法返回的方法之后。

【讨论】:

    【解决方案2】:

    编译器理解 throw 永远不会返回,它不理解 err_exit() 不会返回。如果您要使用您忽略的异常重新编写,那么您的代码可能会更干净,编译器更快乐。 您还可以考虑使用 Java 命名约定。

    public static void main(String[] args) {
      String inFname = "C:/tmp/test.txt"; // not reading args yet
      String outFname = "C:/tmp/test.sign";
      try {
        processFile(inFname, outFname);
      } catch (Exception e) {
        e.printStackTrace();
        throw e; // rethrow and let main() die
      }
    }        
    
    public static void processFile(String inFname, String outFname) 
        throws IOException, NoSuchAlgorithmException {
      Byte[] fileData = readFileAsByteArray(inFname);
      String hexDigest = hexdigestSha1(fileData);
      writeFileFromString(outFname, hexDigest);
    }
    

    在调用例程中,您可以捕获错误。如果您确实需要区分可能发生 IOException 的不同位置,那么您可以在例程中捕获并抛出描述性错误。

    对代码的最小修复就是在设置变量的 try catch 块之外将变量初始化为 null。那么编译器会很高兴。

    【讨论】:

    • 记住,这里是 Java 新手。将您的答案与 Jaco Van Niekerk 的答案结合起来,我想我通过将其传递给 new RuntimeException(mesg) 来指定一条消息,例如“无法读取文件 some_file_name”。
    • 如果我能接受两个答案,我也会接受这个。我确实赞成它。谢谢。
    【解决方案3】:

    如果我确定在访问失败后我会立即退出,并且没有尝试对 readFileAsByteArray 等调用的结果执行,我可能会在申报地点。像这样的...

    byte[] file_data = new byte[] {0};
    

    ...您的其余代码如下,在 catch 块中没有“虚假”分配。

    【讨论】:

      【解决方案4】:

      你似乎有很强的 C 背景。我假设编译器抱怨诸如 file_data 之类的变量。只需在 try-catch 块上方添加 file_data = null

      以上几点:

      1. 我认为将所有语句组合在一个块中是安全的。当抛出异常时,程序会立即跳转到正确的异常块。它看起来也不会那么难看。
      2. 通常不会在 catch-block 中调用另一个方法,因为任务应该(通常并且如果可能)简短而切题。处理错误,继续或退出(可能会重新抛出异常)。
      3. System.exit() 是邪恶的。如果您希望系统退出(即不可恢复的错误条件),而是抛出一个包装的 RuntimeException。 System.exit() 可能导致非干净存在。而是使用 throw new RuntimeException(e);
      4. 最后,如果我可能直言不讳,Java 约定是将方法/变量名称写为 errExit,而不是 err_exit。

      【讨论】:

      • 嗯。在我发布问题之前,我总是搜索 StackOverflow 以查看它是否已被回答。有很多关于错误“这个变量可能没有被初始化”的讨论,其中一些建议是“在你需要它之​​前不要初始化变量;你想让编译器帮助你,如果你声明/初始化编译器无法帮助您的变量。”您一般不同意该建议,还是仅在我在这里展示的情况下不同意?
      • 我看到你编辑了你的问题:-) 在警告方面,试试 byte[] file_data = null; RuntimeException 将为您显示堆栈跟踪...所以我认为这就是您想要的。
      • 我认为初始化以避免编译器抱怨很好。通常延迟初始化非常有意义,但仅在实际值(即非空)已知的情况下。我一直在使用“variable = null;”对于年龄的局部变量......如果有人告诉我这是不好的风格,我会感到非常惊讶(我肯定想要一个替代品)。
      • 我不确定我是否理解您对直言不讳的担忧,但我同意名称 err_exit() 没有使用 Java 习语。我会在我的真实程序中改变它,但我想我会放在这里。 (是的,我有很强的 C 背景;很有见地。)
      • 好吧,如果有人指出变量名称约定这样微不足道的事情,有些人会非常生气。是的,这绝对不是关键。 :-)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-02
      • 2013-05-22
      • 1970-01-01
      • 1970-01-01
      • 2012-03-25
      • 2015-07-04
      相关资源
      最近更新 更多