【问题标题】:Why I have access to finalize() method in other sub class of another package?为什么我可以访问另一个包的其他子类中的 finalize() 方法?
【发布时间】:2013-07-13 09:12:54
【问题描述】:

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

谁能解释一下 finalize()method 有什么特殊情况。我有一个不满意的答案,为什么 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 很感兴趣。也许我犯了一些大错。

【问题讨论】:

  • 我不太明白你的问题是什么:你想知道为什么垃圾收集器可以调用受保护的 finalize()?
  • @AdrianShum 没错

标签: java core finalize


【解决方案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 方法查找(用非常简单的术语)发生的事情是在当前类中查找任何方法,如果没有,则爬上类层次结构,直到找到所需的方法。在上面的示例中,当在SubClass 对象上调用foo() 方法时,SubClass 类包含方法定义,以便使用实现,并且类层次结构不会向上爬升。当调用finalize() 方法时(因为已请求System.gc()),该方法将首先在SubClass 类中查找,但由于它不包含finalize() 的实现,其父类(@987654337 @) 被搜索。 BaseClass 确实包含 finalize() 的实现,因此被使用,并将一行打印到标准输出。

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

【讨论】:

    【解决方案2】:

    我的导师告诉我,protected 只在它的类、同一个包和它的子类中有作用域

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

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

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

    【讨论】:

      【解决方案3】:

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

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

      希望这能澄清您的疑问。

      【讨论】:

      • 那么为什么除了 finalize() 任何其他受保护的成员或方法都没有被调用。
      【解决方案4】:

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

      【讨论】:

      • 我知道当对象不可用时,GC 会调用 finalize() 方法。但是,如果它不适用,为什么没有关于最终确定的规则,那么为什么它受到保护。你能解释更多吗。我的导师告诉我 protected 不适用于其他子类。
      • 稍微澄清一下:系统保留了一个覆盖finalize并且尚未发现已被放弃的对象列表。列表中的任何对象都不能被销毁,但是列表是唯一强引用的对象,在宇宙中的任何地方,都将被识别为废弃,从上述列表中删除,并添加到需要清理的东西的列表。清理完成后,通常在 Universe 中的任何地方都不会引用该对象,并且它将不复存在。
      【解决方案5】:

      我不太明白你的问题是什么:你想知道为什么垃圾 收集器可以调用受保护的 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,具有扩展FooBar 类,并覆盖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() 方法。

      【讨论】:

        【解决方案6】:

        在 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;
        

        以及从./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);
        }   
        

        【讨论】:

          猜你喜欢
          • 2019-11-17
          • 2013-11-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-09-27
          • 2015-03-05
          • 2014-04-16
          相关资源
          最近更新 更多