当Java中的对象部分初始化时(构造函数中发生异常)

3
我听说可以编写以下类似的代码:
SomeClass obj = null;

try {
    obj = new SomeClass();
} catch ( Exception e ) {
...
}
...
if ( obj != null ) { // here it holds true
...
}

请问,如果我们假设构造函数SomeClass可能会抛出异常,那么这种情况是否可能发生?在什么条件下会发生呢?

另一个例子:

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.junit.Test;

public class Sample{

    static class A {

        public static A s;

        public A(Collection c) {
            c.add(this);
            s = this;
            throw new RuntimeException();
        }
    }

    @Test
    public void testResource() throws Exception {
        List l = new ArrayList();
        A a = null;
        try {
            a = new A(l);
            fail("Oops");
        } catch (Throwable e) {
        }
        assertTrue(l.size() == 1);
        assertNull(a);
        assertNotNull(A.s);
        assertNotNull(l.get(0));
    }

}

你能否提供那个可疑信息来源的链接? - Laurent Pireyn
@Laurent:也许是这个:https://dev59.com/d2025IYBdhLWcg3wl3O- - Harry Joy
@HarryJoy,请查看Hazok在https://dev59.com/U2025IYBdhLWcg3wT0Lq的回答。非final类抛出异常显然是一种安全漏洞。我还没有想好如何证实或否定这一点。 - Alonso del Arte
7个回答

2

这是完全可能的。

不过我不确定是否理解了你的问题:如果构造函数抛出异常,obj 可能为空。


2

你可以编写这样的代码,它会编译并正常运行。但是永远不会出现obj“部分初始化”的情况。它要么被初始化,要么没有。具体来说,如果这一行抛出了Exception

obj = new SomeClass();

那么,对于 obj 的值而言,就好像那行代码从未被执行一样。在该行之前,您将 obj 初始化为 null,因此如果抛出异常,则 obj 仍将保持为 null。在这种情况下,程序将不会进入最后的 if 块。
或者,如果没有抛出异常,则 obj 将不再是 null,并且最终的 if 块中的代码将被执行。

2

首先,您需要声明并初始化一个变量。

SomeClass obj = null;

在下面的代码中,你需要(1)创建一个新的实例并(2)存储引用。
try {
   obj = new SomeClass();
} catch ( Exception e ) {
   ...
}

现在我们假设实例化(步骤1)失败并抛出异常:JVM将继续执行下一个处理该异常的catch块。因此,步骤2不会被执行,变量将保持其实际状态。
现在可能会有一个实例被部分初始化。老实说,我不知道,这是JVM内部的事情。但由于没有任何东西持有对这个部分初始化实例的引用,它将在下一个机会被垃圾回收。

+1 有人可能会认为,部分初始化的实例在被 GC 回收之前一直存在于内存中,因为构造函数只是在实例化后初始化对象的特殊方法。看起来 JVM 要取消这种行为需要额外的工作。 - Paul Bellora

1

这是完全可能的;仔细看:如果构造函数抛出异常,obj 仍将是 null


1
如果SomeClass构造函数抛出异常,obj将为NULL。

1

即使调用构造函数,对象对我来说仍然为空:

public static void main(String[] args) {
    TestCls cls = null;
    try {
        cls = new TestCls("");
    } catch (Exception e) {
        e.printStackTrace();
    }
    if(cls == null)
        System.out.println("hi");
}

public TestCls(String str) throws Exception{
    throw new Exception();
}

输出:-

java.lang.Exception
hi
    at TestCls.<init>(TestCls.java:57)
    at TestCls.main(TestCls.java:48)

结论:如果类构造函数抛出异常,则对象将为NULL。

1
构造函数可能会抛出异常,就像 Java 中的任何其他调用一样。例如,如果尝试在空引用上调用方法,则可能会抛出 NullPointerException 异常。请考虑以下示例:
public class ConstructionTest {

    static ConstructionTest obj;


    public ConstructionTest() {
        String s = null;
        s.hashCode();
    }

    public static void main(String[] args) {

        try {
            obj = new ConstructionTest();
        } catch ( Exception e ) {
            System.err.println(e);
        }

        if ( obj != null ) { 
            System.err.println("Not Null");

        } else {
            System.err.println("Is Null");
        }

    }

}

由于构造函数中的 s 为空,因此会抛出 NullPointerException,因此会打印出 "Is Null"。在 Java 中没有“部分构造对象”的概念 - 当您在 Java 中使用 new 时,它要么起作用并返回新实例化的对象,要么不起作用并返回异常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接