Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Hallo,
nehmen wir an es gibt die Klassen Account und ChargedAccount. ChargedAccount erbt von Account. Jetzt wird diese Zeile geschrieben.
Account c1 = new ChargedAccount("...");
1. Von welchemTyp ist das Objekt c1 und warum (Account oder ChargedAccount)?
2. Warum schreibt man nicht einfach ChargedAccount c1 = new ChargedAccount("..."); ?
3. Warum ist es beim definieren eines Objekts erlaubt zwei unterschiedliche Datentypen zu wählen (hier z.B. Account und ChargedAccount)?
4. Warum ist es nicht umgekehrt möglich? Also so: ChargedAccount c1 = new Account("...");
1. Die Variable c1 ist vom Typ Account, referenziert jedoch ein Objekt vom Typ ChargedAccount
2. Weil man es eben manchmal lieber allgemein halten möchte. Für die weitere Logik ist es nur wichtig, dass du einen Account hast, ob das jetzt im Detail ein ChargedAccount ist, ist für den Rest Logik erstmal egal.
3. Grundlagen der Vererbung. Auf der rechten Seite der Zuweisung muss nur etwas stehen, was von dem links stehenden Typ erbt. Die Vererbung ist eine "A ist ein B" Beziehung. Da ChargedAccount von Account erbt, ist somit jeder ChargedAccount auch ein Account. So wie eben jeder Mensch auch ein Säugetier ist.
4. Einfaches Beispiel: Jeder Mensch ist ein Säugetier, aber nicht jedes Säugetier ist ein Mensch. Daher wäre Mensch m = new Säugetier() falsch
Und was ist, wenn ich die Methode c1.transfer(...) aufrufe. Diese Methode ist in beiden Klassen enthalten.
Wo, wird die Methode aufgerufen. In der Klasse Account oder ChargedAccount und warum?
Die in ChargedAccount, es ist ja bekannt dass dieses Objekt vom Typ ChargedAccount ist, also wird auch diese Methode aufgerufen. Polymorphismus .....
Edit:
Gibt es in einem Vererbungszweig einer Klassenhierarchie mehrere Methoden auf unterschiedlicher Hierarchieebene, jedoch mit gleicher Signatur, wird erst zur Laufzeit bestimmt, welche der Methoden für ein gegebenes Objekt verwendet wird (Dynamisches Binden). Bei einer mehrstufigen Vererbung wird jene Methode verwendet, die direkt in der Objektklasse (d. h. jene Klasse, von der das Objekt ein Exemplar ist) definiert ist, oder jene, die im Vererbungszweig am weitesten „unten“ liegt (d. h. die Methode, die von der Vererbung her am nächsten ist)
Die Variable ist vom Typ Account. Im ersten Posting hast du aber gefragt, von welchem Typ das Objekt ist, und das ist vom Typ ChargedAccount. Genauer gesagt vom Typ ChargedAccount, Account und Object.
Die Variable ist vom Typ Account. Das Objekt auf das sie zeigt ist vom Typ ChargedAccount. Also wird die Methode in der Klasse ChargedAccount aufgerufen wenn du c1.transfer(...) aufrufst. Das ist ja gerade der Sachverhalt den ich dir noch dazu geschrieben hatte.
Und kennst du einen logischen Grund oder ein Bsp., warum man zwei unterschiedliche Datentypen wählt?
Weil, ich sehe da kein Grund warum man das so Account c1 = new ChargedAccount("..."); macht und nicht so ChargedAccount c1 = new ChargedAccount("...");
Es könnte ja sein dass es noch eine Klasse UnchargedAccount gibt die ebenfalls von Account abgeleitet ist und deine Variable c1 im verlaufe des Programmes auch mal auf eine Instanz von UnchargedAccount zeigen soll.
Häufiger kommt es wohl vor dass du eine Liste von Accounts hast in der dann Instanzen verschiedener Accounttypen gespeichert werden können.
Die Variable ist vom Typ Account. Das Objekt auf das sie zeigt ist vom Typ ChargedAccount. Also wird die Methode in der Klasse ChargedAccount aufgerufen wenn du c1.transfer(...) aufrufst. Das ist ja gerade der Sachverhalt den ich dir noch dazu geschrieben hatte.
Nein ist es nicht. Deine Variable referenziert ein Objekt.
So als pseudo-reales Beispiels:
Betrachte einfach Häuser als Objekte vom Typ Haus. Um aber ein Haus "benutzen" zu können, musst du es irgendwie referenzieren können. Dabei ist dann die Adresse (z.B. "Hauptstraße 1") die Referenz auf das Objekt Haus. Deine Variable kennt also nur die Adresse, aber wenn man dieser Adresse folgt, erhält man das tatsächliche Haus Objekt.
Man kann z.B. EIne Klasse "Bank" erstellen, die alle Accounts verwalten soll. Sie bekommt eine Klassenvariable zugewiesen, die eine Liste der Accounts darstellt:
Java:
List<Account> accounts = new ArrayList<>();
Dazu besitzt sie Eine Methode zum hinzufügen neuer Konten:
Java:
public void addAccount(Account acc) {
accounts.add(acc);
}
Nun kannst du an diese Methode alle Konten übergeben. Darunter auch Sparbücher etc, die ja auch Konten sind, nur eine spezielle Form. Für so etwas is das sehr nützlich. Wenn du diese Konten dann in den anderen Methoden bearbeiten willst (z.B. Kontostand abrufen) Wirst du nur Methoden aufrufen können, die in Account definiert sind, da du ja in der Klasse Bank nicht weißt, um was für ein konto es sich handelt, es behandelt alle gleich, ob nun ein wirkliches Konto oder ein Sparbuch, beide werden gleich behandelt. Wird aber nun eine Methode aus Account aufgerufen mit der gleichen Signatur wie in Sparbuch, wird, obwohl von einer Account-Referenzvariablen aufgerufen, der COde in Sparbuch aufgerufen, da das Objekt ja immernoch ein Sparbuch ist und es selber das genau weiß.
Ganz genau. Die Referenzvariable zeigt auf das Objekt, das du ihm zugewiesen hast. Der Typ der Referenzvariable sagt, was für Methoden du von diesem objekt ausführen kannst.. Das zugewiesene Objekt und der Typ der Variable müssen allerdings kompatibel sein, ansonsten würde das ganze nichtmal kompilieren.
Genauer würde man sagen: "c1 ist ein Zeiger auf ein Objekt der Klasse ChargedAccount." Die Klasse ChargedAccount ist erstmal eine Schablone dafür, wie die Objekte dieser Klasse aussehen, was für Eigenschaften diese Objekte haben. Mit "new ChargedAccount(..)" erstellst du Objekte dieser Klasse. c1 ist dann ein Zeiger auf eines dieser Objekte.
Nein. Genau genommen würde man sagen c1 ist ein Zeiger auf ein Objekt der Klasse Account ... und ChargedAccount ist ebenfalls ein Account. Das hatten wir oben ja schon.
Für das Verständnis ist es sinnvoll, zwischen Compilierungszeitpunkt und Ausführungszeitpunkt zu unterscheiden. Der Compiler trifft seine Entscheidungen ausschließlich auf Basis der Deklaration, also Account c1;. Für ihn ist c1 ein Account-Objekt und er akzeptiert deshalb nur Methodenaufrufe, die ein Account-Objekt "kann". Selbst dann, wenn er wie im BeispielAccount c1 = new ChargedAccount();theoretisch wissen könnte, dass gerade ein ChargedAccount gespeichert wurde.
Die VM hingegen weiß zum Ausführungszeitpunkt, ob gerade ein Account, ChargedAccount oder SonstwasAccount referenziert wird und kann deshalb die Methode aus der passenden Klasse aufrufen.
Für das Verständnis ist es sinnvoll, zwischen Compilierungszeitpunkt und Ausführungszeitpunkt zu unterscheiden. Der Compiler trifft seine Entscheidungen ausschließlich auf Basis der Deklaration, also Account c1;. Für ihn ist c1 ein Account-Objekt und er akzeptiert deshalb nur Methodenaufrufe, die ein Account-Objekt "kann". Selbst dann, wenn er wie im BeispielAccount c1 = new ChargedAccount();theoretisch wissen könnte, dass gerade ein ChargedAccount gespeichert wurde.
Die VM hingegen weiß zum Ausführungszeitpunkt, ob gerade ein Account, ChargedAccount oder SonstwasAccount referenziert wird und kann deshalb die Methode aus der passenden Klasse aufrufen.
Wenn ich folgendes mache:
1. Account c1;
Hier deklariere ich die Variable c1 vom Typ Account?
2. Account c1 = new ChargedAccount();
Hier erstelle ich ein neues Objekt vom Typ ChargedAccount?
3. Wenn ich diese Zeile programmiere? Account c1 = new ChargedAccount();
Kann ich mir den Speicherbereich, so wie im Anhang vorstellen?
Wieviel Speicher wird für ein Objekt reserviert?
Sofern es sich dabei um eine Methode handelt die bereits in Account bekannt ist, dann ja. Andernfalls musst du zuerst auf einen passenden Typ/Interface, welches diese Methode bereitstellt (also naheliegenderweise auf ChargedAccount) casten. Sonst kommt es bereits (vor der Ausführung) beim Kompilieren zu einem Fehler.
Wenn ich folgendes mache:
3. Wenn ich diese Zeile programmiere? Account c1 = new ChargedAccount();
Kann ich mir den Speicherbereich, so wie im Anhang vorstellen?
Wieviel Speicher wird für ein Objekt reserviert?
Ersteinmal: So viel Speicher, wie es eben braucht
Zu beachten ist, dass aufgrund von padding immer 8byte-Blöcke benutzt werden. Hinzu kommen 8byte für die Klassen-Definition des Objekts. Bei drei int's und einem byte macht das z.B. 3*4 + 1*1 + 8 = 21 byte. Hinzu 3 byte fürs padding um auf ein vielfaches von 8 zu kommen, also werden 24 byte genutzt.
Zusätzlich 4 bytes für die Referenz auf diese Objekt, quasi deiner Variablen c1. In deinem Bildchen hättest du also bei 1004 nur die Referenz auf das tatsächliche Objekt stehen und ab 1016 dann (nach vorherigen Überlegen) mindestens 16 byte für das Objekt.
Es IST möglich, von c1 aus auf Methoden von ChargedAccount zuzugreifen, hier musst du aber manuell eingreifen.
Um das zu erreichen, musst du dem Compiler sagen, er soll c1 als ChargedAccount behandeln.
In diesem Fall musst du aber sicherstellen, dass es sich bei dem von c1 aus referenzierten Objekt tatsächlich um einen ChargedAccount handelt, sonst kriegst du eine ClassCastException.
Sofern es sich dabei um eine Methode handelt die bereits in Account bekannt ist, dann ja. Andernfalls musst du zuerst auf einen passenden Typ/Interface, welches diese Methode bereitstellt (also naheliegenderweise auf ChargedAccount) casten