终止一个对象与使它无效一样吗?

Is terminating an object is the same as nulling it?

所以我一直在经历"Effective Java 2nd Ed."

在第 7 项中,他谈到不使用终结器,因为它们会导致很多问题。

但是我们可以“提供一个明确的终止方法”而不是使用终结器,其中一个例子就是 close 语句。而且我不明白什么是“终止语句以及它们与终结器之间的区别是什么?

我得出的结论是,终止一个对象就像使它无效,从而释放资源。但我想我不太了解其中的区别。所以我感谢任何帮助。

谢谢!

But instead of using finalizers we can " provide an explicit termination method" and an example of those is the close statement .

作者引用了 close() 方法,该方法提供了一种清理使用资源释放的对象的方法。

例如,当您创建和操作 InputStreamOutputStream 时,您不想依赖 Java 终结器(对于某些子classes 这些接口。例如,FileInputStream class 定义了 finalize() 方法)释放与流关联的资源,但您想使用该方法由 API 提供:void close() 因为它作为终结器更可靠。

java.sql.Statement 的工作方式相同:它提供了一个 close() 方法来释放与语句实例关联的 JDBC 资源。

I came to the conclusion that terminating an object is like nulling it thus the resourses is released .

将对象分配给 null 不需要释放所有应该释放的资源。此外,如果该对象或该对象的某个字段仍被另一个活着的对象引用,则该对象将不会被垃圾收集变得难以辨认

最后,垃圾回收可能也需要一些时间。
如果我们不需要使用该对象,为什么要等待?

显式终止方法与 finalize() 之间的主要区别在于不能保证调用第二种方法。它最终在垃圾收集期间被调用,老实说这可能永远不会发生。让我们考虑以下三个 类.

class Foo {
    @Override
    public void finalize() {
        System.out.println("Finalize Foo");
    }
}

class Bar implements Closeable {
    @Override
    public void close() {
        System.out.println("Close Bar");
    }
}

class Baz implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("Close Baz");
    }
}

第一个覆盖继承自Objectfinalize()方法。 FooBar 实现了由 ARM(自动资源管理)处理的两个接口。

Foo foo = new Foo();
new Foo();
try (Bar bar = new Bar(); Baz baz = new Baz()) { // this is ARM 
    System.out.println("termination example");
}
Bar bar = null;
try {
    bar = new Bar();
    // ...
} finally {
    if (bar != null) {
        bar.close();
    }
}

这个例子应该return:

termination example
Close Baz
Close Bar
Close Bar

Foofinalize() 方法从未被调用,因为 Foo 未被垃圾回收。 JVM 有可用资源,因此为了性能优化,它不执行垃圾收集。此外 - 尽管完成了应用程序,但资源未被垃圾回收。即使是 Foo 的第二个创建实例也不是垃圾收集器,因为 JVM 有足够的资源可以茁壮成长。

第二个使用 ARM 的要好得多,因为它创建了两种资源(一个实现 java.io.Closeable 和一个实现 java.lang.AutoCloseable,值得一提的是 Closeable 扩展了 AutoCloseable,这就是它可用于 ARM 的原因)。 ARM 保证关闭这两种资源,在另一个抛出时关闭一个,等等。第二个展示了类似于 ARM 的东西,但是省去了很多不必要的样板代码。

让你成为更好的开发者的东西:

但它仍然不完美。程序员仍然需要记住关闭对象。 Java 中没有析构函数迫使开发人员要么记住关闭资源,要么记住使用 ARM,等等。有一个很好的设计模式(Venkat Subramaniam 很好地解释了)- Loan Pattern。贷款模式的简单示例:

class Loan {
    private Loan() {
    }

    public Loan doSomething(int m) {
        System.out.println("Did something " + m);
        if (new Random().nextBoolean()) {
            throw new RuntimeException("Didn't see that commming");
        }
        return this;
    }

    public Loan doOtherThing(int n) {
        System.out.println("Did other thing " + n);
        return this;
    }

    private void close() {
        System.out.println("Closed");
    }

    public static void loan(Consumer<Loan> toPerform) {
        Loan loan = new Loan();
        try {
            toPerform.accept(loan);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            loan.close();
        }
    }
}

你可以这样使用它:

class Main {
    public static void main(String[] args) {
        Loan.loan(loan -> loan.doOtherThing(2)
                .doSomething(3)
                .doOtherThing(3));
    }
}

减轻了开发者关闭资源的负担,因为已经为他处理好了。如果其中一个方法抛出异常,那么它就会被处理,开发人员就不必费心了。 close 方法和构造函数是私有的,不会诱使开发人员使用它们。