Objekt soll anderes Objekt zerstören

Frezl

Erfahrenes Mitglied
Hallo allerseits,

ich habe ein kleines Problem mit meinen Objekten in Java: Ich hab zwei Hühner erschaffen, die munter gackern, scharren und Eier legen können. Und außerdem kann ein Huhn ein anderes attackieren, bis dieses keine Lebenspunkte mehr hat und stirbt. Jedenfalls sterben soll, denn leider gackert das Huhn nach seinem Tod munter weiter. Das Problem ist, dass Java soweit ich weiß keinen echten Destruktor für Objekte hat. Mit finalize() wird dem Garbage Collector ja nur empfohlen, das Objekt zu löschen. Aber der tuts nur, wenn er gerade Lust dazu hat. Selbes Spiel mit System.gc().

Das einzige, was funktioniert, ist der Aufruf von Elfi = null; um das Objekt Elfi zu löschen. Aber ich hab nicht rausgefunden, wie ich das aus einem anderen Objekt heraus bewerkstelligen kann. Aber seht selbst:

Main.java
Java:
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package zoo;

/**
 *
 * @author Frezl
 */
public class Main {

    // METHODEN

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        // Hühner
        Huhn Elfi = new Huhn("Elfriede", 430, "braun", 2);
        Huhn Berta = new Huhn("Berta", 410, "weiß", 3);

        // Leben auf dem Hühnerhof
        Elfi.gackern();
        Berta.gruessen();
        Berta.scharren();
        Berta.attackieren(Elfi);
        Berta.attackieren(Elfi);
        Berta.scharren();
        Elfi.gackern();
        Elfi.gackern();
        Berta.attackieren(Elfi);
        Berta.attackieren(Elfi);
        Berta.attackieren(Elfi);
        Berta.attackieren(Elfi);
        Berta.scharren();
        Elfi.gackern();
        Elfi.gackern();
        Berta.attackieren(Elfi);
        Berta.attackieren(Elfi);
        Berta.attackieren(Elfi);
        Berta.attackieren(Elfi); // hier stirbt Elfi, da die Lebenspunkte auf 0 sinken

        // leider lebt der Geist von Elfi aber munter weiter :-P
        Elfi.gackern();
        Elfi.scharren();
        Ei Osterei = Elfi.eiLegen();

        // erst wenn Gott das Huhn mit eigener Hand tötet, ist es erlöst
        Elfi = null;
        Elfi.gackern(); // und kann auch nicht mehr gackern... (Was zu einer NullPointerException führt)
    }

}

Huhn.java
Java:
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package zoo;

/**
 *
 * @author Frezl
 */
public class Huhn {

// ATTRIBUTE
    String Name;
    float Gewicht;
    String Farbe;
    int Alter;
    int Lebenskraft;

// METHODEN
    void gackern() {
        System.out.println(Name + ": gack");
    }

    void gruessen() {
        System.out.println(Name + ": Hallo, ich bin " + Name + ".");
    }

    void scharren() {
        System.out.println(Name + ": *scharr*");
    }

    void attackieren(Huhn Huhn) {
        Huhn.Lebenskraft = Huhn.Lebenskraft - 10;
        System.out.println("> " + Huhn.Name + " hat jetzt noch " + Huhn.Lebenskraft + " Lebenspunkte.");

        if(Huhn.Lebenskraft <= 0) {
            Huhn.finalize(); // Hier soll das attackierte Huhn getötet werden
            System.out.println("> " + Huhn.Name + " wurde von " + this.Name + " getötet.");
        }
    }

    Ei eiLegen() {
        System.out.println("> " + Name + " hat ein Ei gelegt.");

        return new Ei("braun", 45);
    }

// KONSTRUKTOR
    Huhn(String Name, float Gewicht, String Farbe, int Alter) {
        this.Name = Name;
        this.Gewicht = Gewicht;
        this.Farbe = Farbe;
        this.Alter = Alter;
        this.Lebenskraft = 100;

        System.out.println("> " + Name + " wurde geboren.");
    }

// DESTRUKTOR
    @Override
    protected void finalize() {
        System.out.println(Name + ": *röchel*");
        System.out.println("> " + Name + " ist gerade gestorben.");
    }

}

Ei.java
(Der Vollständigkeit halber, obwohl diese Klasse mit dem Problem nix zu tun hat)
Java:
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package zoo;

/**
 *
 * @author Frezl
 */
public class Ei {

    // ATTRIBUTE
    String Farbe;
    float Gewicht;

    // KONSTRUKTOR
    Ei(String F, float G) {
        Farbe = F;
        Gewicht = G;
    }

}

Weiß jemand Rat? Oder muss ich tatsächlich darauf warten, bis sich der Sensenman bemüßigt fühlt, das Huhn von seinen Leiden zu erlösen?

Gruß und Dank,
Frezl
 
Zuletzt bearbeitet:
Klar, du rufst ja die Methoden auf weiter auf. Soweit ich weiß wird finalize() aufgerufen, sobald der Collector das Tier zur Hölle schickt. Du musst wohl vor jedem Aufruf die HP prüfen lassen. (Oder alternativ in der Methode selbst).
Du könntest auch die Methode mit return verlassen, aber ich denke dass ist nicht sonderlich schön. Wenn du Threads verwendest musst du übrigens ebenfalls einfach die run() verlassen, um die Handlung einzustellen. Versteht sich ja von selbst.

Übrigens kann sie warscheinlich noch immer Gackern, der Aufruf führt nur ins leere, weil ja die Variable auf kein Hühnchen mehr zeigt.
 
Hallo!

Zuallererst bitte ich dich darum java tags zu verwenden, solltest du Java Code posten.

Zum Thema:

Wieso überprüfst du nicht in der gackern() methode (sowie in den anderen) ob die lebenspunkte >0 sind?

Du könntest aber auch ein boolean attribut erstellen, welches zum Beispiel "tot" heißen könnte, samt getter und setter Methode ("public void setTot(boolean tot)" und "public boolean istTot()")

Hat das Hünchen das Zeitliche gesegnet, setzt du "tot" auf true.
Natürlich müssen dann auch die Methoden wie gackern überprüfen ob das Huhn "tot" ist (also tot = true).

Ich frage mich ja warum bei dir nach dem Tod des Huhns das Objekt mitsterben muss?!
Was hat der Tod des Huhns mit dem Tod des Objekts auf dem Heap zu tun?
Das sind doch Äpfel und Birnen.

Du musst dir nämlich keine Sorgen machen.
Wenn das tote Huhn später im Code nicht mehr verwendet wird (keine Referenzen mehr bestehen), wird das Objekt beim nächsten GC Cycle aus dem Heap genommen.

mfg
Martin
 
Zuletzt bearbeitet:
@mccae:

Wenn du mir sagst, wo ich die Tags finde, verwende ich sie gerne...

So wie du mir das beschreibst, finde ich das überhaupt nicht intuitiv. Die OOP soll es dem Programmierer doch erleichtern, die Wirklichkeit im Code abzubilden. In der Wirklichkeit ist es aber nicht so, dass das Huhn denkt "Bin ich tot? Ja? Dann darf ich jetzt nicht mehr atmen.", sondern es ist einfach tot und tut garnix mehr. Fertig. Für micht sind das nicht Äpfel und Birnen. Das Objekt im Code ist das Huhn aus dem echten Leben. Und dieses Huhn muss ich doch irgendwie töten können, wenn ich es will. Sonst hab ich als Programmierer ja gar keine Kontrolle mehr über mein Programm. Außerdem verkompliziert es die Sache doch ungemein, wenn ich in jeder Funktion erst mal prüfen muss, ob das Objekt noch existiert oder nicht. Viel sinnvoller ist es doch, dass beim Aufruf einer Methode eines nicht existierenden Objekts eine Exception geworfen wird. So wie das im echten Leben auch wäre. Wenn das Huhn atmet, obwohl es tot ist, dann ist was paradoxes passiert und das Universum kollabiert. Oder sowas. Keine Ahnung, ich gehe davon aus, dass das im echten Leben nicht passieren kann, weil die Natur ein gutes Exception Handling hat...

Viele Grüße,
Frezl
 
Ich schreibe sie immer selbst.

[ j a v a ]
[ / j a v a ]

Wenn du ne Exception gethrowt haben willst, dann throw sie doch im Todesfall, damit verlässt er sofort die Stelle wenn du sie abfängst, bei einem Programm in der Shell beendet er falls du nicht abfängst, bei einer GUI bemüt er sich weiterzumachen.

Ansonst könntest du bei einen Angriff je nach verbleibenden HP this oder nulll zurückgeben, und in die Hühnerreferenz speichern. Damit throwt er jedes mal ne NullPointer. Aber das wäre wohl auch nicht sehr schön.
 
Hey Kai,

danke für den Tipp mit den Java-Tags!

Mit der Exception hast du mich glaube ich falsch verstanden. Ich will keine Exception, wenn das Huhn stirbt. Die Exception soll dann geworfen werden, wenn ich das tote Huhn gackern lassen will. Wenn ich also eine Methode in einem nicht existierenden Objekt aufrufen will. Und das genau passiert ja auch, wenn ich in meiner main-Funktion den Pointer des Objekts auf null setzte:

Java:
public static void main(String[] args) {

  // [...]

  Elfi = null;
  Elfi.gackern(); // hier wird logischerweise eine Exception geworfen.
}

Was mir aber immer noch fehlt ist ein Destruktor, mit dem sich das Objekt selbst zerstören kann. Dass ich mit dem Aufruf
Java:
Elfi.sterben();
das Objekt Elfi zerstöre. Und außerdem eine Möglichkeit, wie ein Objekt ein anderes zerstören kann, sodass
Java:
Berta.toete(Elfi);
ebenfalls das Objekt Elfi zerstört.

Ist es nachvollziehbar, um was es mir geht?

Viele Grüße,
Fred
 
Zuletzt bearbeitet:
Das ist aber nicht Java-Stil, jedenfalls nicht soweit ich das beurteilen kann. Wie schon gesagt wurde, kannst du intern nen Flag setzen, wenn ein Huhn tot ist und dann wird eben darauf reagiert. Wenn du willst, kannst du dir eine eigene Exception schreiben (vielleicht HuhnIstTotException), die bei jeder Methode des Huhns geworfen werden kann. Ob das sinnig ist, ist eine andere Sache, ich glaube eleganter wäre einfach zu prüfen, ob das Flag "tot" gesetzt ist. Andererseits ersparrt man sich mit der Exception das explizite Prüfen des Flags, aber man muss dann eben Exception-Handling betreiben.

Dein Problem ist, dass du versuchst etwas zu "pulverisieren". Jetzt denk aber mal nach. Wenn ein Huhn tot ist, dann ist es doch nicht weg. Sein Körper ist weiterhin vorhanden, es reagiert nur auf nichts. Genauso ist es auch mit den Objekten. Erst wenn du das Objekt nirgeds mehr referenzierst, dann wird es vom GC verworfen.
 
Mkay, wenn ich das Huhn stofflich betrachte. Aber das was entscheidet, ist das Gehirn des Huhns und das ist tot, oder auch im Hühnerhimmel. Aber vielleicht ist es auch etwas übertrieben von mir, die Realität 1:1 abbilden zu wollen. Denn wenn es so ist, dürfte ich auch das Huhn nicht dazu zwingen, zu gackern... Denn das müsste das Huhn dann von selbst tun, wenn es will.

Halten wir also fest: In Java gibt es keinen echten Destruktor. Hab ich das soweit korrekt verstanden?

Trotzdem verstehe ich nicht, warum es keine Möglichkeit gibt, gerade diesen letzten Pointer zu löschen. Wenn ich es richtig verstehe, gibt es ja zwei Pointer, die auf das Huhn Elfriede zeigen.


Zum einen gibt es die Variable Elfi, die auf das Huhn Elfriede zeigt:
Java:
        Huhn Elfi = new Huhn("Elfriede", 430, "braun", 2);

Zum anderen gibt es dann den internen Zeiger Huhn des Objekts Berta, der in diesem Moment auf das Huhn Elfriede zeigt:
Java:
        Berta.attackieren(Elfi);

Und genau aus dieser Methode
Java:
    void attackieren(Huhn Huhn) {
        Huhn.Lebenskraft = Huhn.Lebenskraft - 10;
        System.out.println("> " + Huhn.Name + " hat jetzt noch " + Huhn.Lebenskraft + " Lebenspunkte.");
 
        if(Huhn.Lebenskraft <= 0) {
            // Hier soll das attackierte Huhn getötet werden
            System.out.println("> " + Huhn.Name + " wurde von " + this.Name + " getötet.");
        }
    }
heraus, will ich ja den Pointer Elfi = null setzen.

Ist das ganz sicher überhaupt nicht möglich, oder steh ich nur auf dem Schlauch? Das einzige, was ich bis jetzt geschafft hab, war den internen Zeiger Huhn = null zu setzen. Aber das hilft mir leider nicht weiter :-P

Viele Grüße,
Frezl
 
Zuletzt bearbeitet:
Du sprichst zwar von Pointern, aber hast ihre Funktionsweise scheinbar noch nicht begriffen. Pointer zeigen auf eine bestimmte Adresse. In Java machen das Objekte auch. Der Punkt ist aber, dass die Pointer auch nur Variablen sind. Nur weil du einen solchen Zeiger mit null inistialisierst (oder löschst, kommt drauf an, aus welchem Winkel man die Sache sieht) ist das Originalobjekt doch nicht verschwunden. Lediglich die Referenz von der Variable zu dem Objekt ist verschwunden, alle anderen aktiven Referenzen bleiben aber weiterhin bestehen, auf sie hat das keine Auswirkungen.

Verstehste was ich meine? Ob nun Pointer-Variablen in C/C++ oder Objektvariablen in Java, beim Initialisieren mit null passiert nicht mehr als dass du das "Seil", das die Variable mit dem Objekt verbindet, "durchschneidest". Das Objekt selbst ist aber rein formal noch da.

In C/C++ kann man explizit den Speicher dann freigeben, muss man sogar, wenn man der Meinung ist, dass man das Objekt nicht mehr braucht. In Java gibt es den Luxus, dass der GC einem diese Arbeit abnimmt, einzige Bedingung ist aber, dass es keine Referenzen geben darf, die auf das Objekt zeigen. Und natürlich muss der GC aufräumen, aber das ist mehr oder weniger Zufall. Für den Ablauf des Programms ist das aber nicht wichtig, weil sobald keine Referenz mehr vorhanden ist, ist das Objekt so zu sagen "tot", ob es im Orbit des RAMs rumschwebt oder wirklich verworfen wurde interessiert dabei keinen mehr, weil man darauf ohnehin nicht mehr zugreifen kann, da alle Referenzen getrennt wurden.
 
Stimmt, und du kannst es setzen. Dazu musst du die Variable, die auf Elfi zeigt außerhalb der Methode platzieren, um den Zugriff auszuweiten. Dann braucht Elfi natürlich noch eine Referenz auf die Klasseninstanz, die ihre Variable enthält, und du kannst sie nach belieben (zurück)setzen.

Ich glaube das sind aber keine echten Pointer, in C++ kann auf Speicheraddressen als chars zugreifen, in Java nicht. (VM!)
 
Zurück