4

我是 Java 新手 第一次尝试学习 Java。我的简单问题是关于 java.lang.Object 中的 finalize () 方法。为什么我可以在我的其他类中访问这个唯一受保护的方法而不是其他受保护的方法。我的导师告诉我,受保护的只有它的类、同一个包和它的子类中的范围。在这里我读到了这个。

有人可以解释一下 finalize()method 是否有任何特殊情况。我有一个不满意的答案,为什么 finalize ()在这里受到保护 我的代码如下:

//Creating Package Foo
package Foo;
class A
{
    protected finalize() 
    { 
        System.out.println("This is finalize method of Class A");
    }
}

// Creating Another Pacakage Foo1
package Foo1;
import Foo.*;
class B extends A
{
}
class C extends B
{
}
class D extends C
{
    public void foo() {
        C ob = new C();
        ob = null;
        System.gc(); // Why class A finalize() is getting call
    }
}

只有在 finalize () 的情况下才会被调用,而不是在另一种情况下。问我的导师他拒绝回答他说你做错了我会看但他没有回复我。

请想想我很喜欢 java 。也许我犯了一些大错误。

4

6 回答 6

1

这按预期工作,我认为该finalize()方法与 Java 中的任何其他方法没有任何不同。可以认为有点不同的是,该finalize()方法通常仅由 JVM 垃圾收集器本身调用,如JavaDoc中所述:

当垃圾收集确定不再有对该对象的引用时,由对象上的垃圾收集器调用。

另请注意,Josh Bloch 强烈警告不要在Effective Java中使用终结器:

终结器是不可预测的,通常很危险,而且通常是不必要的。它们的使用会导致行为不稳定、性能不佳和可移植性问题。终结器有一些有效的用途......但根据经验,您应该避免使用终结器。

考虑以下示例,该示例与您的示例类似:

具有重写finalize()方法的基类。

public abstract class BaseClass {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("BaseClass finalisation occured");
    }
}

不覆盖 finalize 的子类:

public class SubClass extends BaseClass {
    public void foo() {
        System.out.println("SubClass Foo'd");
    }
}

还有一个驱动类,它有一个基本的 main 方法来运行所有东西:

public class Driver {
    public static void main(String[] args) {
        SubClass sc = new SubClass();
        sc.foo();
        sc = null;
        System.gc();        
    }
}

我们得到的输出如下:

SubClass Foo'd
BaseClass finalisation occured

Java 方法查找(用非常简单的术语)发生的情况是在当前类中查找任何方法,如果没有,则爬升类层次结构,直到找到所需的方法。在上面的示例中,当在对象foo()上调用方法时,类包含方法定义以便使用实现,并且类层次结构不会向上爬升。当方法被调用时(因为 a已经被请求),该方法将首先在类中查找,但由于它不包含其父 ( ) 的实现,因此会被搜索。确实包含使用的so that 的实现,并将一行打印到标准输出。SubClassSubClassfinalize()System.gc()SubClassfinalize()BaseClassBaseClassfinalize()

现在考虑一个再次覆盖的子类finalize()

public class OverridenSubClass extends SubClass {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("Overriden finalize called, which calls super's finalize first");
    }
}

还有一个稍微修改的Driver类:

public class Driver {

    public static void main(String[] args) {
        OverridenSubClass sc = new OverridenSubClass();
        sc.foo();
        System.out.println(sc.toString());
        sc = null;
        System.gc();
        System.exit(0);
    }
}

产生以下输出:

SubClass Foo'd
finalize.OverridenSubClass@7150bd4d
BaseClass finalisation occured
Overriden finalize called, which calls initial finalize first

希望这和预期的一样。这里唯一需要注意的是:

  1. 我们不会toString()在我们的任何类中覆盖,因此Object.toString()使用了实现。
  2. 变量的类型sc不是决定使用的方法实现的因素 - 它是由所引用的实际对象的类型sc
于 2013-07-16T07:44:30.690 回答
1

我的导师告诉我,protected 仅在其类、同一包及其子类中具有范围

你误会他了。protected分别提供对所有这些范围的访问这些条件不必同时满足。

因此,您有从 A 继承的类,因此可以访问其受保护的成员。

然而,这一切都无关紧要。您没有调用受保护的方法。您正在调用一个系统方法,该方法执行一些最终调用的其他代码finalize(),并且代码可以通过 JVM 诡计或由于在java.lang包中进行访问,与 相同Object,这将为其提供对Object受保护成员的访问权限。

于 2013-07-16T08:31:10.087 回答
0

我认为它按预期工作。进入 D 类并发生实例化之后,就没有更多的东西要执行,因此 finalize() 方法被调用。你的问题的答案是

一个^ | 乙^ | C ^ | D 所以 D 显然继承了 A 的属性,因此 D 可以访问受保护的 finalize 方法,因此它被调用。

希望这能澄清你的疑问。

于 2013-07-15T13:31:59.173 回答
0

finalize垃圾收集器在开始销毁实例时调用它,因为它不再可用(不再有其他对象引用该实例)。

内置的垃圾收集器有些特殊,它可以调用方法,而不管它的访问修饰符。

有关访问修饰符的标准规则仅适用于您的 Java 类。

编辑

子类可能需要重写finalize以释放系统资源,即使它们位于不同的包中。finalize如果是私有方法,那将是不可能的。另一方面,不希望从外部调用该方法,因为这通常会导致不一致的状态(例如在仍在使用实例时释放或销毁系统资源),因此 public 不是该方法的选项

下面这个简单的例子可能会更清楚一点。

package foo;

public class A {

    public void _public() {}

    void _default() {}

    private void _private() {}

    @Override
    protected void finalize() throws Throwable {
        freeSomeResources();  // implementation not shown but it should be clear
    }

}

package bar;

import foo.A;

public class B extends A {

    @Override
    public void _public() {}        // fine

    @Override
    void _default() {}              // compile error -> can't access from other package

    @Override
    private void _private() {}      // compile error -> private method


    @Override                       // fine - subclass may override
    protected void finalize() throws Throwable {
        super.finalize();
    }

}
于 2013-07-15T13:32:06.810 回答
0

我不太明白你的问题是什么:你想知道为什么垃圾收集器可以调用受保护的 finalize()?– 阿德里安沉

@AdrianShum 是的,完全正确 – Java_begins 1 分钟前

如果这是您的问题,那么我将尝试讨论它。

首先,finalize() 旨在成为一种由系统垃圾收集器调用的特殊方法,如果它是一种特殊情况,它的处理方式不同,而可访问性被简单地忽略了,我不会感到惊讶。

但是,这并不意味着它是特殊处理的。我不知道你为什么得出一个不应该发生的结论。

尽管您看到该方法受到保护,但 finalize() 方法实际上是在 Object 中声明的。这意味着,如果有人有权访问,Object#finalize()那么它可以调用它。

只举一个普通方法的例子。

假设有一个foo包:

package foo;
public class Foo {
    protected void foo() {
        System.out.println("foo");
    }
}

我们在同一个包中有一个 FooRunner,它可以访问 Foo#foo 因为它们在同一个包下。

package foo;
public class FooRunner {
    public void runFoo(Foo foo) {
        foo.foo();
    }
}

然后,即使我有另一个包bar,具有Bar扩展类Foo和覆盖foo()方法,FooRunner 仍然可以获取 Bar 实例并访问其foo()

package bar;
public class Bar extends Foo {
    @Override
    public void foo() {
        System.out.println("bar");
    }
}

// this is still valid
fooRunner.runFoo(new Bar());

尽管 Bar 是在另一个包中定义的,但包中的 FooRunnerfoo仍然可以运行 foo() 方法,间接地将其视为Foo.

于 2013-07-16T09:12:06.767 回答
0

在 OpenJDK 中,finalize()从本机代码调用,使用 JNI 的GetMethodID(). 请参阅顶部的评论java.lang.ref.Finalizer

/* A native method that invokes an arbitrary object's finalize method is
   required since the finalize method is protected
 */
static native void invokeFinalizeMethod(Object o) throws Throwable;

finalize()以及从本机代码的实际调用./jdk/src/share/native/java/lang/ref/Finalizer.c

JNIEXPORT void JNICALL 
Java_java_lang_ref_Finalizer_invokeFinalizeMethod(JNIEnv *env, jclass clazz,
                                                  jobject ob)
{   
    jclass cls;
    jmethodID mid;

    cls = (*env)->GetObjectClass(env, ob);
    if (cls == NULL) return;
    mid = (*env)->GetMethodID(env, cls, "finalize", "()V");
    if (mid == NULL) return;
    (*env)->CallVoidMethod(env, ob, mid);
}   
于 2013-07-16T08:43:18.143 回答