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 の他のメソッドと異なって扱われるとは思いません。少し異なると考えられるのは、 JavaDocfinalize()で概説されているように、メソッドは通常、JVM ガベージ コレクター自体によってのみ呼び出されることです。

オブジェクトへの参照がなくなったとガベージ コレクションが判断したときに、オブジェクトのガベージ コレクタによって呼び出されます。

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 が要求されたため) 、メソッドは最初にクラスで検索されますが、そのメソッドにはその親の実装が含まれていないため( ) が検索されます。do には使用される soの実装が含まれており、1 行が stdout に出力されます。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アクセスを個別に提供します。これらの条件は、すべて同時に満たす必要はありません。

したがって、A から継承するクラスがあるため、その保護されたメンバーにアクセスできます。

しかし、それはすべて無関係です。保護されたメソッドを呼び出していません。最終的に を呼び出す他のコードを実行するシステム メソッドを呼び出してfinalize()おり、そのコードは、の保護されたメンバーへのアクセスを提供する とjava.lang同じように、JVM のトリックまたはパッケージ内にあるという利点のいずれかによってアクセスできます。ObjectObject

于 2013-07-16T08:31:10.087 に答える
0

期待どおりに動作していると思います。クラスDに入ってインスタンス化が行われた後、実行するものがないため、 finalize() メソッドが呼び出されます。そしてあなたの質問への答えは

^ | B ^ | C ^ | D は明らかに A のプロパティを継承するため、保護されている finalize メソッドは D にアクセスできるため、呼び出されます。

これで疑問が解消されることを願っています。

于 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

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 に答える