为什么Try/Catch块会创建新的变量范围?

tre*_*r-e 44 java scope try-catch

例如:

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

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

SomeObject someObject;
try
{
    someObject = new SomeObject();
    someObject.dangerousMethod();
}
catch(Exception e)
{
}
someObject.anotherMethod(); //works fine
Run Code Online (Sandbox Code Playgroud)

我只是想知道这个的设计原因.为什么在try/catch块内创建的对象不在方法的其余部分范围内?也许我不会深入了解try/catch除了观看Exceptions投掷之外的工作方式.

T.J*_*der 50

为什么在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
Run Code Online (Sandbox Code Playgroud)

注意区别.当变量声明定义了它的存在的范围,而不是其中对象创建.

但基于上面的方法名称,更有用的结构是:

SomeObject someObject = new SomeObject();
try
{
    someObject.dangerousMethod();
}
catch(Exception e)
{
}
someObject.anotherMethod();
Run Code Online (Sandbox Code Playgroud)

你的评论:

我想我为什么为try/catch块创建了另一个范围感到困惑.

在Java中,所有块都创建范围.的身体if,一个身体else的,while等等-它们都创建一个新的,嵌套的变量范围:

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

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

如果你考虑它,它是有道理的:有些块是有条件的,比如定义一个if或一个的主体while.在上面if,bar可能会或可能没有声明(取决于值foo),这没有任何意义,因为编译器当然没有运行时值的概念foo.因此,为了保持一致性,Java的设计人员会让所有块创建一个新的嵌套范围.(JavaScript的设计者走的是另一种方式 - 根本没有块范围,但是,虽然它被添加 - 但这种方法让人感到困惑.)

  • 我想他知道它的运作方式; 相反,他在问为什么决定这样做. (3认同)

cor*_*iKa 7

在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); }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

结果是

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
Run Code Online (Sandbox Code Playgroud)

但是,这将编译得很好.

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

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

IfThenStatement:
    if ( Expression ) Statement
Run Code Online (Sandbox Code Playgroud)

声明定义为(14.5)

Statement:
    StatementWithoutTrailingSubstatement
    LabeledStatement
    IfThenStatement
    IfThenElseStatement
    WhileStatement
    ForStatement

StatementWithoutTrailingSubstatement:
    Block
    EmptyStatement
    ExpressionStatement
    AssertStatement
    SwitchStatement
    DoStatement
    BreakStatement
    ContinueStatement
    ReturnStatement
    SynchronizedStatement
    ThrowStatement
    TryStatement
Run Code Online (Sandbox Code Playgroud)

那么块,表达式语句或空语句就可以了.但是声明(在第6章中定义)不在声明的语法中.


SiB*_*SiB 5

变量或对象的范围在范围内(由花括号{}定义),在该范围内定义它.

因为try catch启动了一个新的作用域,可以抛出一些错误,所以try catch中定义的对象在它的作用域之外是不可用的.


Pyr*_*rce 5

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

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

此外,它只是在 Java 中定义范围定义的方式——正如许多其他答案所指出的那样。