Klassenvariable Da man fr jede Kuh bzw jede
Klassenvariable
Da man für jede Kuh bzw. jede Henne auf dem Markt den gleichen Preis für ein Liter Milch, bzw. den gleichen Preis für ein Ei bekommt, müsste man für jede Kuh und jede Henne den gleichen Literpreis, bzw. den gleichen Eierpreis festlegen. Schreiben Sie ein passendes Programm dazu. Erzeugen Sie mehrere Kühe und Hennen.
class Henne{ private String name; private double lege. Leistung; private double eier. Preis; public Henne(String p. Name, double p. Lege. Leist, double p. Eier. Preis){ name = p. Name; lege. Leistung = p. Lege. Leist; eier. Preis = p. Eier. Preis; } } public void set. Eier. Preis( double p. Eier. Preis){ eier. Preis = p. Eier. Preis; } Dieser Preis gilt für jede Henne
class Kuh{ private String name; private double milch. Leistung; private double liter. Preis; public Kuh(String p. Name, double p. Milch. Leis, double p. Liter. Preis){ name = p. Name; milch. Leistung = p. Milch. Leist; liter. Preis = p. Liter. Preis; } } public void set. Liter. Preis( double p. Liter. Preis){ liter. Preis = p. Liter. Preis; } Dieser Preis gilt für jede Kuh
public class Main. Klassenvar{ public static void main( String[] args){ Kuh k 1=new Kuh( "Elsa", 15, 70); Kuh k 2=new Kuh( "Frida", 10, 70); Henne h 1=new Henne( "Ute", 3, 20); Henne h 2=new Henne( "Pute", 5, 20); } } Dieser Preis gilt für jede Kuh Dieser Preis gilt für jede Henne
Da es viel Schreibaufwand macht, für jede Kuh den gleichen Literpreis und für jede Henne den gleichen Eierpreis festzulegen, ist es viel sinnvoller, dies mit einer einzigen Anweisung zu machen. Zuerst wird dazu eine sogenannte Klassenvariable definiert. Eine Klassenvariable unterscheidet sich von einem "normalen" Attribut durch den Vorsatz "static".
In der Klasse Kuh bzw Henne wird deshalb die Klassenvariable "liter. Preis" bzw. "eier. Preis" wie folgt definiert: private static double liter. Preis; private static double eier. Preis;
Es gibt zwei Möglichkeiten mit einer Methode auf eine Klassenvariablen zuzugreifen: 1) Mit der "normalen" Methode (Exemplarmethode), die wir bis jetzt kennen gelernt haben. 2) Mit der sogenannten Klassenmethode, die sich von der "normalen" Methode (Exemplarmethode) durch den Vorsatz static unterscheidet. In der Klasse Kuh wird dies also z. B. wie folgt realisiert:
In der Klasse Kuh wird dies also z. B. wie folgt realisiert: public static void set. Liter. Preis( double p. Liter. Preis){ liter. Preis = p. Liter. Preis; }
Eigenschaften der Klassenmethoden: 1) Klassenmethoden können nur auf Klassenvariablen zugreifen. 2) Klassenmethoden können nur Klassenmethoden der gleichen Klasse aufrufen. Dagegen haben alle Exemplarmethoden Zugriff auf die Klassenvariablen und Klassenmethoden.
Durch die Benutzung des Klassennamens (und nicht des Namens eines Objekts einer Klasse) wird dann der Literpreis (bzw. Eierpreis) für jedes Objekt einer Klasse festgelegt. In der Klasse Kuh wird dies also z. B. wie folgt realisiert:
Kuh. set. Literpreis(20); setzt für jede Kuh den Preis für ein Liter Milch auf 20 (Cent). Die Methode (hier set. Literpreis ) nach der Benutzung des Klassennamens - muss dann allerdings eine Klassenmethode sein!
Bemerkung: Hier wird der Bezeichner static immer nach dem Bezeichner für den Zugriffschutz verwendet. Die Bezeichner für den Zugriffschutz und der Bezeichner static können aber auch in der Reihenfolge vertauscht werden. Dies ist dem Kompiler egal Also statt: private static double literpreis; ist auch folgendes möglich:
Also statt: private static double literpreis; ist auch folgendes möglich: static private double literpreis;
Realisieren Sie das vorige Programm mit Hilfe von Klassenmethoden und Klassenvariablen.
class Henne{ private String name; private double lege. Leistung; private static double eier. Preis; public Henne(String p. Name, double p. Eier. Gewicht){ lege. Leistung =p. Eier. Gewicht; } } public static void set. Eier. Preis( double p. Eier. Preis){ eier. Preis=p. Eier. Preis; } Warum wird hier eine Klassenmethode implementiert und keine Exemplarmethode? Kommt gleich (siehe Anweisung in main)
class Kuh{ private String name; private double milch. Leistung; private static double liter. Preis; public Kuh(String p. Name, double p. Milch. Leistung){ milch. Leistung =p. Milch. Leistung; } public static void } } set. Liter. Preis( double p. Liter. Preis){ liter. Preis=p. Liter. Preis; Warum wird hier eine Klassenmethode implementiert und keine Exemplarmethode? Kommt gleich (siehe Anweisung in main)
public class Main. Klassenvar 2{ public static void main( String[] args){ Kuh k 1 = new Kuh("Elsa", 15); Kuh k 2 = new Kuh("Frida", 10); Henne h 1 = new Henne("Ute", 3); Henne h 2 = new Henne("Pute", 5); Kuh. set. Liter. Preis(70); Henne. set. Eier. Preis(20); } Die Methode (hier set. Literpreis bzw. } set. Eier. Preis ) - nach der Benutzung des Klassennamens - muss eine Klassenmethode sein! (siehe frühere Folie)
Bemerkung 1
In einem UML werden Klassenvariablen und Klassenmethoden unterstrichen dargestellt. Beispiel siehe nächste Folie
Kuh. . . - liter. Preis: double. . . unterstrichen + set. Liter. Preis(lp: double). . . unterstrichen
Bemerkung 2
Vorbelegung von Klassenvariablen
Eine Klassenvariable kann - muß aber nicht - bei der Deklaration (mit static) mit einem Vorgabewert (mit Hilfe des Gleichheitszeichens) initialisiert werden. Wird sie nicht durch einen Vorgabewert initialisiert, wird sie automatisch durch Standardvorbelegungen vorinitialisiert. (gleiche Standardvorbelegungen wie bei den Attributen einer Klasse). Also. . .
Datentyp boolean char byte short int long float double Referenz standardmäßige Vorbelegung false u 0000 0 0 L 0. 0 f 0. 0 null
Beispiel
x wird mit 0 vorinitialisiert class Test { private static int x; private static int y = 2; . . . } y wird mit 2 initialisiert
Schreiben Sie ein Programm, in dem (in einer Schleife) z. B. 10 Hennen erzeugt werden. Danach soll in main(. . . ) die Anzahl der Hennen auf dem Bildschirm ausgegeben werden. Realisieren Sie dies mit Hilfe einer entsprechenden Klassenvariablen. Zusätzlich sollen die Hennen (mit 101 beginnend) durchnummeriert werden. Erstellen Sie danach die Hennen h 1 und h 2. Geben Sie jeweils alle Attribute von h 1 und h 2 auf dem Bildschirm aus.
In der folgenden Lösung werden aus Gründen der Vereinfachung ein paar Attribute und Methoden der Klasse Henne eingespart. Man kann also in diesem “übertragenen“, “abgespeckten“ Fall von einer “gerupften“ Henne sprechen:
Um weniger Platz zu verbrauchen schreibt man im Folgenden abkürzend: aus statt System. out. println
class Henne{ private String name; private static int hennen. Anzahl; private int nummer; public Henne(String p. Name){ name = p. Name; hennen. Anzahl++; nummer=hennen. Anzahl+100; } Welchen Anfangswert hat diese Klassenvariable ?
class Henne{ private String name; private static int hennen. Anzahl; private int nummer; public Henne(String p. Name){ name = p. Name; hennen. Anzahl++; nummer=hennen. Anzahl+100; } Da sie nicht explizit (d. h. durch = ) mit einem bestimmten Wert vorinitialisiert wird, wird sie automatisch mit 0 vorbelegt.
class Henne{ private String name; private static int hennen. Anzahl; private int nummer; public Henne(String p. Name){ name = p. Name; hennen. Anzahl++; 101 nummer=hennen. Anzahl+100; } Welche Nummer hat dann die erste erzeugte Henne (d. h. welchen Wert hat das Attribut nummer des ersten erzeugten Objekts vom Typ Henne) ?
public void set. Name(String pname){ name = pname; } public String get. Name(){ return name; } public static int get. Anzahl(){ return hennen. Anzahl; } } public int get. Nummer(){ return nummer; }
package hennennummerieren 1; public class Startklasse{ public static void main(. . . ){ Henne h; for(int i=0; i<10; i++){ h = new Henne("Henne"+i); aus("nummer="+h. get. Nummer()); } aus("anz="+Henne. get. Anzahl()); Henne h 1=new Henne("Ute"); Henne h 2=new Henne("Heike"); aus("nrh 1="+h 1. get. Nummer()); aus("nrh 2="+h 2. get. Nummer()); } Auf welche Hennen kann man am Ende der Schleife zugreifen? }
package hennennummerieren 1; public class Startklasse{ public static void main(. . . ){ Henne h; for(int i=0; i<10; i++){ h = new Henne("Henne"+i); aus("nummer="+h. get. Nummer()); } aus("anz="+Henne. get. Anzahl()); Henne h 1=new Henne("Ute"); Henne h 2=new Henne("Heike"); aus("nrh 1="+h 1. get. Nummer()); Nur auf die letzte Henne, weil in h (Pointer) der jeweilige Speicher aus("nrh 2="+h 2. get. Nummer()); (genauer die Adresse davon) der vorigen Hennen überschrieben } Was passiert dann mit den alten Hennen (genauer mit dem wurde. } zugehörigen Speicherplatz ? )
package hennennummerieren 1; public class Startklasse{ public static void main(. . . ){ Henne h; for(int i=0; i<10; i++){ h = new Henne("Henne"+i); aus("nummer="+h. get. Nummer()); } aus("anz="+Henne. get. Anzahl()); Henne h 1=new Henne("Ute"); Henne h 2=new Henne("Heike"); aus("nrh 1="+h 1. get. Nummer()); aus("nrh 2="+h 2. get. Nummer()); } Er wird irgendwann von der Java-Müllabfuhr } (= Garbage Collector) entsorgt!
package hennennummerieren 1; public class Startklasse{ public static void main(. . . ){ Henne h; for(int i=0; i<10; i++){ h = new Henne("Henne"+i); aus("nummer="+h. get. Nummer()); } aus("anz="+Henne. get. Anzahl()); Henne h 1=new Henne("Ute"); Henne h 2=new Henne("Heike"); aus("nrh 1="+h 1. get. Nummer()); aus("nrh 2="+h 2. get. Nummer()); } Welchen Wert hat das Attribut nummer von h 1 und h 2 ? }
package hennennummerieren 1; public class Startklasse{ public static void main(. . . ){ Henne h; for(int i=0; i<10; i++){ h = new Henne("Henne"+i); aus("nummer="+h. get. Nummer()); } aus("anz="+Henne. get. Anzahl()); Henne h 1=new Henne("Ute"); Henne h 2=new Henne("Heike"); aus("nrh 1="+h 1. get. Nummer()); aus("nrh 2="+h 2. get. Nummer()); } h 1. nummer = 111 und h 2. nummer = 112 }
Bemerkung Jede Variable (die ein Attribut bezeichnet) bzw. Methode, die in einem Programm verwendet wird, hat einen Ort bzw. eine Quelle an der sie gespeichert wird. Eine statische Methode hängt an der Klasse, eine nicht statische Methode hängt an der Instanz einer Klasse. Sofern man nicht explizit eine andere Quelle (die Instanz eines anderen Objekts z. B. ) angibt, füllt der Compiler automatisch die Quellen entsprechend dem Vorsatz this bzw. mit dem Klassennamen auf. WICHTIG (Merksatz): In einer static-Methode einer deklarierenden Klasse C hat man keine this-Referenz auf eine Instanz von C. Beispiel siehe nächste Folie:
class A { private double wert; static void static. Method(A a) { a. instance. Method(); new A(). instance. Method(); wert=quadriere(4); geben Sie die Quellen der } Variablen und Methoden in static. Method(. . . ) an. void instance. Method() { System. out. println("Hallo"); } public static double quadriere(double zahl) { return zahl * zahl; } Ergebnis siehe nächste Folie. }
Quelle von instance. Method() class A { private double wert; static void static. Method(A a) { Quelle von instance. Method() a. instance. Method(); new A(). instance. Method(); this. wert=A. quadriere(4); } Quelle von wert (wird vom Compiler intern ergänzt). void instance. Method() { System. out. println("Hallo"); } Warum bringt der Compiler eine Fehlermeldung? public static double quadriere(double zahl) { return zahl * zahl; } Quelle von quadriere(4) } (wird vom Compiler intern ergänzt).
class A { private double wert; static void static. Method(A a) { a. instance. Method(); new A(). instance. Method(); this. wert=A. quadriere(4); } this macht Probleme void instance. Method() { System. out. println("Hallo"); } public static double quadriere(double zahl) { return zahl * zahl; } Merksatz (siehe eine frühere Folie) } In einer static-Methode (hier static. Method) der Klasse A hat man keine this-Referenz auf eine Instanz der Klasse A
Die Methode main(. . . ) ist immer als static deklariert. Was ist deshalb falsch am folgenden Programm?
public class Main. Test 1{ public static void main( String[] args){ double wert; wert = verdopple(4); System. out. println(wert); } public double verdopple( double p. Wert){ return(2*p. Wert); } main(. . ) ist immer static, also. . . } ist main(. . . ) eine Klassenmethode Die Quelle von verdopple(4) darf kein this sein (da main eine static. Methode ist). Also wird sie vom Compiler zu Main. Test 1. verdopple(4) ergänzt. Aber verdopple ist keine static. Methode.
public class Main. Test 1{ public static void main( String[] args){ double wert; wert = verdopple(4); System. out. println(wert); } } public double verdopple( double p. Wert){ return(2*p. Wert); } muss verdopple(. . . ) auch eine Klassenmethode werden.
public class Main. Test 1{ public static void main( String[] args){ double wert; wert = verdopple(4); System. out. println(wert); } } } double p. Wert){ return(2*p. Wert);
public class Main. Test 1{ public static void main( String[] args){ double wert; wert = verdopple(4); System. out. println(wert); } public static double verdopple( } } double p. Wert){ return(2*p. Wert);
- Slides: 48