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

Java 为什么我可以访问另一个包的其他子类中的finalize()方法?,java,core,finalize,Java,Core,Finalize,我第一次接触Java是因为我想学习Java。 我的简单问题是关于java.lang.Object中的finalize()方法。为什么我在我的另一个类中只能访问这个受保护的方法而不是其他受保护的方法。我的导师告诉我,受保护的方法在它的类、同一个包及其子类中只有作用域。我读了这个 有人能解释一下finalize()方法有什么特殊情况吗。我有一个不满意的答案,为什么finalize()受到保护 我的代码如下: //Creating Package Foo package Foo; class A {

我第一次接触Java是因为我想学习Java。 我的简单问题是关于java.lang.Object中的finalize()方法。为什么我在我的另一个类中只能访问这个受保护的方法而不是其他受保护的方法。我的导师告诉我,受保护的方法在它的类、同一个包及其子类中只有作用域。我读了这个

有人能解释一下finalize()方法有什么特殊情况吗。我有一个不满意的答案,为什么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()的情况下,才会调用它,而不是在其他情况下。 问我的导师,他拒绝回答,他说你犯了一些错误,我会看看,但他没有回答我


请想一想我是来爪哇的。可能我犯了一些大错误。

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

A ^ | B ^ | C ^ | D 因此D显然继承了一个受保护的方法的属性,该方法可被D访问,因此被调用


希望这能澄清您的疑问。

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
    }

}


这正如预期的那样工作,我不认为
finalize()
方法与Java中的任何其他方法有任何不同。可以认为有点不同的是,
finalize()
方法通常只由JVM垃圾回收器本身调用,如下所述:

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

还请注意,Josh Bloch强烈警告不要在以下情况下使用终结器:

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

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

具有重写的
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");
    }
}
还有一个驱动程序类,它有一个运行一切的基本主方法:

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

现在考虑一个子子类,它重写<代码>终结()/<代码>:

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");
    }
}
和稍微修改的
驱动程序
类:

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
希望这是意料之中的事。这里唯一值得注意的是:

  • 我们没有在任何类中重写
    toString()
    ,因此使用
    对象.toString()
    实现
  • 变量
    sc
    的类型不是决定所用方法实现的因素,而是
    sc
    引用的实际对象的类型
  • 我的导师告诉我,protected只在它的类、同一个包及其子类中有作用域

    你误解了他<代码>受保护分别提供对所有这些作用域的访问。这些条件不必同时满足

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

    然而,这一切都无关紧要。您没有调用受保护的方法。您正在调用一个系统方法,该方法执行一些其他代码,这些代码最终会调用
    finalize()
    ,并且该代码可以通过JVM欺骗或通过
    java.lang
    包进行访问,与
    Object
    相同,这将使它能够访问
    对象的受保护成员。

    在OpenJDK中,使用JNI的
    GetMethodID()
    从本机代码调用
    finalize()。请参见
    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;
    
    以及从
    /jdk/src/share/native/java/lang/ref/Finalizer.c
    中的本机代码对
    finalize()的实际调用:

    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);
    }   
    

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

    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);
    }   
    
    package foo;
    public class Foo {
        protected void foo() {
            System.out.println("foo");
        }
    }
    
    package foo;
    public class FooRunner {
        public void runFoo(Foo foo) {
            foo.foo();
        }
    }
    
    package bar;
    public class Bar extends Foo {
        @Override
        public void foo() {
            System.out.println("bar");
        }
    }
    
    // this is still valid
    fooRunner.runFoo(new Bar());