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