java-为什么Try / Catch块会创建新的变量作用域?

例如:

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块中创建的对象不在该方法的其余部分范围内? 也许我不了解try/catch的工作原理,而只是看着Exceptions

trevor-e asked 2020-08-02T05:55:25Z
5个解决方案
50 votes

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

他们是。 在if块中声明的变量不在包含块的作用域内,原因与所有其他变量声明都位于发生作用域的范围内的原因相同:这是规范对其进行定义的方式。 :-)(更多内容,包括对您评论的回复。)

这是在if中创建的对象,可以在其外部访问该对象:

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中,所有块都创建作用域。 if的正文,while的正文,if的正文等—它们都创建了一个新的嵌套变量范围:

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

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

如果您考虑一下,这是有道理的:有些块是有条件的,例如定义29932236989136906906或while的主体的块。在上面的if中,可能没有声明过29932236989136906906(取决于2993223698913690690628的值), 这毫无意义,因为编译器当然没有运行时值的概念foo。因此,为了保持一致性,Java的设计者不得不让所有块都创建一个新的嵌套作用域。 (JavaScript的设计者采用了另一种方式-尽管没有添加块作用域,但根本没有块作用域-并且这种方法也使人们感到困惑。)

T.J. Crowder answered 2020-08-02T05:56:21Z
7 votes

在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章中定义)不在声明的语法中。

corsiKa answered 2020-08-02T05:57:17Z
5 votes

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

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

SiB answered 2020-08-02T05:57:42Z
3 votes

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

Tim Bender answered 2020-08-02T05:58:02Z
3 votes

每次使用方括号'{'时,您都在用C ++和Java表示新的作用域。 您尝试尝试一项操作需要一些内部设置,并且对名称进行范围界定可以快速跳出try块,而无需进行大量清理。

只要不存在名称冲突,某些语言就可以让您访问作用域之外的那些作用域变量(例如在Python中),但这需要内部堆栈结构稍有不同,并且无论如何仍会增加try catch的成本。

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

Pyrce answered 2020-08-02T05:58:32Z
translate from https://stackoverflow.com:/questions/11655020/why-does-a-try-catch-block-create-new-variable-scope