【问题标题】:Why does a Try/Catch block create new variable scope?为什么 Try/Catch 块会创建新的变量范围?
【发布时间】:2012-07-24 04:43:30
【问题描述】:

例如:

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 抛出。

【问题讨论】:

  • Java 中的每个块都定义了一个新的范围。不只是尝试块。不遵循一般规则将是非常不一致的。

标签: java scope try-catch


【解决方案1】:

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

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

这是一个在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

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

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

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

你的评论:

我想我对为什么还要为 try/catch 块创建另一个作用域感到困惑。

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

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

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

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

【讨论】:

  • 打败我!这个范围是为了防止抛出异常(即使在被声明的对象的构造函数中)。如果尝试从未到达该实例化语句,我们将无法继续使用该对象。
  • 我想我很困惑为什么还要为 try/catch 块创建另一个作用域。
  • 我想他知道它是如何工作的;相反,他在问为什么决定这样做。
  • @Atlos:因为在 Java 中,所有块都创建范围。 if 的主体、else 的主体、while 的主体等等。它们都创建了自己的变量范围。这就是语言的定义方式。它的优点是您可以在更接近使用它们的位置声明事物;它的缺点是这样做会导致您编写长函数,而实际上您应该将它们分解为子函数。 :-)
  • @T.J.Crowder 这比保持所有块一致更有意义。如果我想让它从抛出异常的方法中分配一个值,然后在其他地方使用它,我发现必须在块之外声明所有变量是很乏味的。估计没有办法解决。
【解决方案2】:

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

【讨论】:

  • 如果你有一个if 语句单行语句,但你没有使用{ } 对呢?比如if(true) SomeObject someObject = new SomeObject()someObject 会在方法的范围内还是编译器会为我插入隐含的括号?
  • @Atlos - 它做了一些尴尬的事情。只能说,不允许!我会编辑它。
  • 基本上,new SomeObject(); 是允许的,因为它是一个声明,但 SomeObject so = new SomeObject() 是不允许的,因为它是一个声明(即使它包含一个声明)。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

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

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

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 2019-08-02
        • 1970-01-01
        • 2015-12-05
        • 1970-01-01
        • 2011-07-25
        • 2011-02-20
        • 2010-09-10
        • 1970-01-01
        相关资源
        最近更新 更多