MichiLee: Heap/Steak und Static in Java

Nabend Forum,
hätte eine kleine Frage zur folgende Seite mit Heap und Stack:
http://blogs.escde.net/jochen/archive/2007/06/13/79.aspx

Was ist denn nur ganz grob der Vorteil, wenn man in der Programmiersprache noch zwischen Speicherstellen wie Heap und Stack unterscheiden?
Ein Werteobjekt ist ja kein Referenz-Typ.
An sich wäre aber praktisch das "ms" ja eine Referenz?

"
MyStruct ms;
 ms.SetI(1);
"

Wir haben noch folgenden Code zum Anschauen bekommen, was an Semaphoren angelehnt sei:

  
class Semaphore {  
  
  protected int n;  
  
  public Semaphore (int n) {  
  
    this.n = n;  
  
  }  
  
  public synchronized void warten () {  
  
    while (n <= 0)  
  
      try {  
  
        wait();  
  
      } catch(Exception e) {  
  
        e.printStackTrace();  
  
      }  
  
    -- n;  
  
  }  
  
  public synchronized void signalisieren () {  
  
    if (++ n > 0)  
  
      notify();  
  
  }  
  
}  
  
  
  
//  
  
// Skript "Synchronisation", p6  
  
//  
  
class A extends Thread {  
  
  private float konto;  
  
  
  
  public void run() {  
  
  
  
    System.out.println("A:wait(s)");  
  
    example.s.warten();  
  
  
  
    System.out.println("A: Lesen Konto");  
  
    konto = example.account;  
  
  
  
    // wait for a random time  
  
    try {  
  
      sleep((long)(Math.random() * 1000),0);  
  
    } catch (InterruptedException e) {}  
  
  
  
    System.out.println("A: 5.1 Euro addieren");  
  
    konto += 5.1F;  
  
  
  
    System.out.println("A: Schreiben Konto");  
  
    example.account = konto;  
  
  
  
    System.out.println("A:signal(s)");  
  
    example.s.signalisieren();  
  
  }  
  
}  
  
  
  
class B extends Thread {  
  
  private float konto;  
  
  
  
  public void run() {  
  
  
  
    System.out.println("B:wait(s)");  
  
    example.s.warten();  
  
  
  
    System.out.println("B: Lesen Konto");  
  
    konto = example.account;  
  
  
  
    // wait for a random time  
  
    try {  
  
      sleep((long)(Math.random() * 1000),0);  
  
    } catch (InterruptedException e) {}  
  
  
  
    System.out.println("B: 4.5 Euro abziehen");  
  
    konto -= 4.5F;  
  
  
  
    System.out.println("B: Schreiben Konto");  
  
    example.account = konto;  
  
  
  
    System.out.println("B:signal(s)");  
  
    example.s.signalisieren();  
  
  }  
  
}  
  
  
  
  
  
public class example {  
  
  public static float account = 25.5F;  
  
  public static Semaphore s = new Semaphore(1);  
  
  
  
  public static void main(String[] args) {  
  
    A a = new A();  
  
    B b = new B();  
  
  
  
    a.start();  
  
    b.start();  
  
  
  
    try {  
  
      a.join();  
  
      b.join();  
  
    } catch (InterruptedException e) {}  
  
  
  
    System.out.println("Neuer Kontostand: " + example.account);  
  
  }  
  
}  
  
  

Da ja in Example die Variable account statisch ist, würden ja Klasse A und B diese Klassenvariable jedesmal überschreiben ne? (Hab jetzt leider kein Netbeans und Eclipse vor Ort zum testen. Ich frage mich nur, obwohl A und B von Thread abgeleitet ist, warum das gleich beim Objektbilden zu nem Thread wird, da ja A a = new A() aufgerufen wird, anstatt Thread a = new A();
Ich schau mir nochmals die Objektbildung und die Hierarchie an, echt übel, was man innerhalb von 1-2 Monaten vergessen und wieder durcheinanderbringen kann :-)
Vielleicht liegts auch an der Uhrzeit

Grüße

  1. http://blogs.escde.net/jochen/archive/2007/06/13/79.aspx
    Was ist denn nur ganz grob der Vorteil, wenn man in der Programmiersprache noch zwischen Speicherstellen wie Heap und Stack unterscheiden?

    Obacht, darum geht es in dem Artikel eigentlich nicht, die Überschrift ist etwas schlecht gewählt.

    Ein Werteobjekt ist ja kein Referenz-Typ.

    Eben. Genau darum geht es.

    An sich wäre aber praktisch das "ms" ja eine Referenz?

    MyStruct ms;
    ms.SetI(1);

    Nein, ms ist hier ein Speicherbereich, eine Wertvariable, wie es bei Microsoft heißt. Eine Referenz steht in obj aus der darauf folgenden Zeile, die du leider nicht mitzitiert hast:

    object obj = (object) ms;

    Mit Heap und Stack hat das Ganze nur intern zu tun. Der Punkt bei der ganzen Angelegenheit ist, dass man in bestimmten Situationen nicht den Speicherbereich, also den Wert an sich haben will, sondern die Adresse desselben, also eine Referenz auf einen Wert.

    Beim Boxing wird ein Speicherbereich reserviert, der Wert der zu verschachtelnden Variablen in diesen Speicherbereich kopiert und die Adresse des Speicherbereichs zurückgegeben. Im obigen Beispiel landet die Adresse in der Variablen obj (die übrigens ihrerseits auf dem Stack liegt, aber das tut –wie gesagt– nichts zur Sache).

    Dass lokale Variablen auf dem Stack angelegt werden (wie ms) und man Speicher nur aus dem Systemspeicher (Heap) reservieren kann (wie für den in obj verschachtelten Wert von ms), liegt in der Natur der Dinge und hat mit dem Sinn und Zweck der ganzen Aktion, eine Referenz auf einen Wert statt eines Wertes zu erhalten, Nullkommagarnichts zu tun.

    Von Microsoft ist das unter http://msdn.microsoft.com/de-de/library/cc749744.aspx#XSLTsection130121120120 recht anschaulich beschrieben.

    // Skript "Synchronisation", p6

    class A extends Thread {

    class B extends Thread {

    A a = new A();
        B b = new B();

    Ich frage mich nur, obwohl A und B von Thread abgeleitet ist, warum das gleich beim Objektbilden zu nem Thread wird, da ja A a = new A() aufgerufen wird, anstatt Thread a = new A();

    Schau doch nochmal hin: A ist eine Unterklasse von Thread. Kaufst du ein A, bekommst auch einen Thread. Die Funktionalität der Klasse hängt an der Klasse und nicht am Typ der Variablen.

    1. Hi,
      dankeschön. Dürfte ich nur noch kurz die genannten Wörter nochmals aufgreifen, ob ich es "grob" verstanden habe.

      Mit Heap und Stack hat das Ganze nur intern zu tun. Der Punkt bei der ganzen Angelegenheit ist, dass man in bestimmten Situationen nicht den Speicherbereich, also den Wert an sich haben will, sondern die Adresse desselben, also eine Referenz auf einen Wert.

      Beim Boxing wird ein Speicherbereich reserviert, der Wert der zu verschachtelnden Variablen in diesen Speicherbereich kopiert und die Adresse des Speicherbereichs zurückgegeben. Im obigen Beispiel landet die Adresse in der Variablen obj (die übrigens ihrerseits auf dem Stack liegt, aber das tut –wie gesagt– nichts zur Sache).

        
      MyStruct ms; //[1]  
      ms.SetI(1); //[2]  
      object obj = (object) ms; //[3]  
      ((MyStruct)obj).SetI(5); //[4]  
      
      

      In der ersten Zeile wird ein Speicherbereich/Wertebereich (Stack) erzeugt/reserviert.
      In Zeile zwei die Zahl 1 darin gespeichert.
      Zeile drei reserviert nun im Systemspeicher (Heap) Speicher und kopiert den Wert von dem Speicherbereich "ms" in den Heap. Unboxing?

      In Zeile 4, wird ein Speicherbereich/Wertebereich (Stack) reserviert und der Wert der Variable "obj" in den Stack kopiert. Man erhält dann die Adresse des Speicherbereichs.

      Ist das so richtig herum oder vertausche ich Zeile drei und vier?

      Grüße

      Schau doch nochmal hin: A ist eine Unterklasse von Thread. Kaufst du ein A, bekommst auch einen Thread. Die Funktionalität der Klasse hängt an der Klasse und nicht am Typ der Variablen.

      Ja wie gesagt, ich schau mir das alles nochmals an. Bisher hatten wir das in der Vorlesung nur so benutzt:

      Mutterklasse var = new AbgeleiteteKlasse()
      Macht aber, wie ich sehe kein Sinn, da man nur die Elemente dann von abgeleiteteKlasse nutzen könnte. Andersherum wie folgt würde eh nicht funktionieren:

      AbleiteteKlasse var = new Mutterklasse()
      Ist mir aber nun wieder klar, wenn ich es genau wieder anschaue.
      Die Klasse A und B sind zwar von Thread abgeleitet und haben folglich deren Funktionalität. Muss man aber nicht die Methode "run" der Mutterklasse explizit aufrufen in Klasse A und B, wenn man die Funktionalität (Parallelität) nutzen will.
      Ah shit, jetzt sehe ich es. Auch der aufruft mit start().
      Dürfte wohl gestern noch an der Uhrzeit gelegen haben :-) Sorry für den letzten Teil.

      Grüße

      1. MyStruct ms; //[1]
        ms.SetI(1); //[2]
        object obj = (object) ms; //[3]
        ((MyStruct)obj).SetI(5); //[4]

        
        > In der ersten Zeile wird ein Speicherbereich/Wertebereich (Stack) erzeugt/reserviert.  
        > In Zeile zwei die Zahl 1 darin gespeichert.  
        > Zeile drei reserviert nun im Systemspeicher (Heap) Speicher und kopiert den Wert von dem Speicherbereich "ms" in den Heap. Unboxing?  
          
        Nein, Boxing, du nimmst ja deine schöne ms-Variable und packst ihren Kram in die object-Kiste. Ansonsten richtig.  
          
        
        > In Zeile 4, wird ein Speicherbereich/Wertebereich (Stack) reserviert und der Wert der Variable "obj" in den Stack kopiert. Man erhält dann die Adresse des Speicherbereichs.  
          
        Richtig bis auf den letzten Satz, der gehört zu Zeile 3, dort landet eine Adresse in der Variablen obj.  
          
        Dieser Vorgang nennt sich Unboxing.  
          
        Der Autor wollte mit diesem Beispiel darauf hinweisen, dass man in Zeile 4 mitnichten das Objekt aus Zeile 3 verändert und schon gar nicht jenes aus Zeile 1, sondern ein drittes, das sich gleich am Ende dieser Zeile wieder in Wohlgefallen auflöst.  
          
        
        > Mutterklasse var = new AbgeleiteteKlasse()  
        > Macht aber, wie ich sehe kein Sinn, da man nur die Elemente dann von abgeleiteteKlasse nutzen könnte.  
          
        Äh, nein. var ist vom Typ Mutterklasse, du kannst über var also nur Elemente von Mutterklasse benutzen. Dass es sich eigentlich um AbgeleiteteKlasse handelt, kann der Compiler nicht wissen, er sieht ja nur "Aha, var ist vom Typ Mutterklasse".  
          
        
        > AbleiteteKlasse var = new Mutterklasse()  
          
        Das geht in die Hose (wenn's der Compiler überhaupt annimmt). var ist ein Objekt vom Typ Mutterklasse und kennt dementsprechend nichts von AbgeleiteteKlasse. Mit dieser Konstruktion könntest du (wenn der Compiler nicht meckern täte, siehe oben) auf Dinge zugreifen, die überhaupt nicht vorhanden sind.  
          
        Du kannst  
          
        Mutterklasse var = new MutterKlasse()  
        Mutterklasse var = new AbgeleiteteKlasse()  
        AbgeleiteteKlasse var = new AbgeleiteteKlasse()  
          
        nehmen, sonst nix.  
          
        
        > Die Klasse A und B sind zwar von Thread abgeleitet und haben folglich deren Funktionalität. Muss man aber nicht die Methode "run" der Mutterklasse explizit aufrufen in Klasse A und B  
          
        Richtig, das ist der Sinn der Vererbung.  
          
        Es gibt allerdings durchaus die Möglichkeit, bei einer abgeleiteten Klasse explizit eine Methode der Mutterklasse aufzurufen. Das macht man, wenn die abgeleitete Klasse die Mutter-Methode durch eigenen Code ersetzt, also überschrieben hat, und man diesen Code umgehen will.