58

例如:

try
{
    SomeObject someObject = new SomeObject();
    someObject.dangerousMethod();
}
catch(Exception e)
{
}
someObject.anotherMethod(); //can't access someObject!

但是您可以在try/catch块之前声明它,然后它可以正常工作:

SomeObject someObject;
try
{
    someObject = new SomeObject();
    someObject.dangerousMethod();
}
catch(Exception e)
{
}
someObject.anotherMethod(); //works fine

我只是想知道这样做的设计原因。为什么在try/catch块内创建的对象不在方法其余部分的范围内?也许我并没有深入了解 a 是如何try/catch工作的,只是看着Exceptions被抛出。

4

5 回答 5

59

为什么在 try/catch 块中创建的对象不在方法其余部分的范围内?

他们是。在块中声明的变量try/catch不在包含块的范围内,原因与所有其他变量声明在它们发生的范围内都是本地的:这就是规范定义它的方式。:-)(更多下文,包括对您评论的回复。)

这是在 a中创建的对象try/catch,可在其外部访问:

SomeObject someObject = null;
try
{
    someObject = new SomeObject();
    someObject.dangerousMethod();
}
catch(Exception e)
{
}
someObject.anotherMethod(); // This is fine -- unless the SomeObject
                            // constructor threw the exception, in which
                            // case someObject will be null

注意区别。声明变量的位置定义了它存在的范围,而不是创建对象的位置。

但是根据上面的方法名称等,更有用的结构是:

SomeObject someObject = new SomeObject();
try
{
    someObject.dangerousMethod();
}
catch(Exception e)
{
}
someObject.anotherMethod();

回复您的评论:

我想我对为什么甚至为 try/catch 块创建另一个范围感到困惑。

在 Java 中,所有块都创建范围。an 的主体、an的主体、aif的主体等——它们都创建了一个新的嵌套变量范围:elsewhile

if (foo) {
    SomeObject bar = new SomeObject();
}
bar.doSomething(); // <== Compilation error, `bar` is not defined

(事实上​​,即使是没有任何控制结构的块也会创建一个。)

如果你仔细想想,它是有道理的:有些块是有条件的,比如定义ifor主体的块while。在上面ifbar可能已经或可能没有被声明(取决于 的值foo),这是没有意义的,因为编译器当然没有运行时值的概念foo。所以可能是为了一致性,Java 的设计者让所有块创建一个新的嵌套范围。(JavaScript的设计者走了另一条路——根本没有块作用域,尽管它正在被添加——这种方法让人们感到困惑。)

于 2012-07-25T17:11:01.150 回答
10

在 Java 中,只要有一{ }对,就可以创建一个新范围。

考虑以下

class ScopeTest {
    public static void main(String[] args) {
        int i = 0;
        { int j = 0; System.out.println(j); }
        { int j = 2; System.out.println(j); }
    }
}

try/catch 只是遵循这个习惯用法,并强制{ }创建一对。

要回应您对非括号 if 语句的跟进,请考虑:

class MultiRTree {
    public static void main(String...args) {
        boolean b = args.length == 0;
        if(b) String s = new String("hello");
    }
}

结果是

c:\files\j>javac ScopeTest.java
ScopeTest.java:4: not a statement
        if(b) String s = new String("hello");
              ^
ScopeTest.java:4: ';' expected
        if(b) String s = new String("hello");
                    ^
2 errors

但是,这将编译得很好。

class ScopeTest {
    public static void main(String...args) {
        boolean b = args.length == 0;
        if(b) new String("hello");
    }
}

为什么会这样,根据 JLS 第 14 章第 9 节,如果定义为:

IfThenStatement:
    if ( Expression ) Statement

而语句定义为(14.5)

Statement:
    StatementWithoutTrailingSubstatement
    LabeledStatement
    IfThenStatement
    IfThenElseStatement
    WhileStatement
    ForStatement

StatementWithoutTrailingSubstatement:
    Block
    EmptyStatement
    ExpressionStatement
    AssertStatement
    SwitchStatement
    DoStatement
    BreakStatement
    ContinueStatement
    ReturnStatement
    SynchronizedStatement
    ThrowStatement
    TryStatement

因此,块、表达式语句或空语句都可以。但是声明(在第 6 章中定义)不在语句的语法中。

于 2012-07-25T17:14:43.513 回答
5

变量或对象的范围在定义它的范围内(由大括号 {} 定义)。

由于 try catch 启动了一个新的范围,其中可能会引发一些错误,因此在 try catch 中定义的对象在其范围之外不可用。

于 2012-07-25T17:12:46.410 回答
5

每次使用方括号“{”时,您都在用 C++ 和 Java 表达一个新的范围。您尝试尝试某个操作需要一些内部设置,并且限定名称允许快速跳出 try 块而无需进行大量清理。

只要不存在名称冲突(如在 Python 中),某些语言将允许您访问范围之外的那些范围变量,但这需要稍微不同的内部堆栈结构,并且无论如何仍然可能增加 try catch 的成本。

这也是Java中定义范围定义的方式——正如许多其他答案所指出的那样。

于 2012-07-25T17:18:39.400 回答
3

try/catch创建一个新范围的原因很简单,因为它是一个块级元素。事实上,简单地{}随机放置在一个方法中将创建一个具有自己本地范围的新代码块。

于 2012-07-25T17:14:53.590 回答