为什么我可以访问另一个包的其他子类中的finalize()方法?

时间:2013-07-15 13:26:02

标签: java core finalize

我是Java新手第一次尝试学习Java。 我的简单问题是java.lang.Object中的finalize()方法。为什么我在其他类中访问这个受保护的方法而不是其他受保护的方法。我的导师告诉我,protected只在其类,相同的包及其子类中具有范围。Here我读过这个。

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

//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的兴趣。也许我犯了一个大错误。

6 个答案:

答案 0 :(得分: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");
    }
}

不覆盖终结的子类:

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

一个带有基本主要方法的驱动程序类来运行所有内容:

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()对象上调用SubClass方法时,SubClass类包含方法定义,以便使用实现,并且类层次结构不会更高。调用finalize()方法时(因为已请求System.gc()),将首先在SubClass类中查找该方法,但由于该方法不包含finalize()的实现1}}搜索其父(BaseClass)。 BaseClass确实包含finalize()的实现,以便使用,并在stdout上打印一行。

现在考虑再次覆盖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引用的实际对象的类型

答案 1 :(得分:1)

  

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

你错误地理解了他。 protected可以分别访问所有这些范围,这些条件并非都必须同时满足。

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

但是,这一切都无关紧要。您没有调用受保护的方法。您正在调用一个系统方法,该方法执行最终调用finalize()的其他代码,而代码可以通过JVM技巧或java.lang包中的代码访问,与Object相同,后者可以访问Object受保护的成员。

答案 2 :(得分:0)

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

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

希望这能澄清你的怀疑。

答案 3 :(得分: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();
    }

}

答案 4 :(得分:0)

在OpenJDK中,使用JNI的finalize()从本机代码调用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);
}   

答案 5 :(得分:0)

  

我不太明白你的问题:你想知道为什么垃圾   收藏家可以调用受保护的finalize()吗? - Adrian Shum

     

@AdrianShum Yes Exactly - Java_begins 1分钟前

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

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

然而,并不一定意味着它是专门处理的。我不知道为什么你得出一个不应该发生的结论。

虽然您看到方法受到保护,但实际上在Object中声明了finalize()方法。这意味着,如果有人有权访问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();
    }
}

然后,即使我有另一个包barBar类扩展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是在另一个包中定义的,但包foo中的FooRunner仍然可以通过将其视为Foo来间接运行foo()方法。