Java Applet - Seite 2

Neue Frage »

Auf diesen Beitrag antworten »
Karlito

Hallo tigerbine,

code:
1:
Trompeter trompeter;


gibt an, dass trompeter eine Variable vom Typ Trompeter ist.

code:
1:
Musiker trompeter;


gibt an, dass trompeter eine Variable vom Typ Musiker ist.

Jetzt erbt die Klasse Trompeter von der Klasse Musiker. Die Vererbung kann als eine "ist ein"- Beziehung angesehen werden. D.h. Jeder Trompeter ist auch ein Musiker, da jeder Trompeter alle Eigenschaften und Methoden von Musiker besitzt.

Andersherum funktioniert diese Beziehung jedoch nicht, da nicht alle Musiker Trompeter sind.

Es ist also so, dass entlang der Vererbungshierarchie alle spezialisierten Objekte auch den Typ aller geerbten Klassen annehmen kann. In Java ist es so, dass jedes Object auch vom Typ "Object" erbt. Somit ginge auch folgende Zuweisung:

code:
1:
Object trompeter = new Trompeter();


Nutzt man nun letzteres Beispiel, so kann man nur die Eigenschaften und Methoden nutzen, welche in der Klasse Object bereitgestellt werden (z.B. toString()).

Analog kann die Methode "mundstueckWechseln()", die zu der Klasse Trompeter gehört eben auch nicht mehr genutzt werden, wenn der Trompeter einer Variable des Typs Musiker zugewiesen wird.

VG,

Karlito
 
Auf diesen Beitrag antworten »
tigerbine

Zitat:
Original von Karlito


Nutzt man nun letzteres Beispiel, so kann man nur die Eigenschaften und Methoden nutzen, welche in der Klasse Object bereitgestellt werden (z.B. toString()).

Analog kann die Methode "mundstueckWechseln()", die zu der Klasse Trompeter gehört eben auch nicht mehr genutzt werden, wenn der Trompeter einer Variable des Typs Musiker zugewiesen wird.

VG,

Karlito


Dann habe ich es richtig verstanden, dass die beiden Deklarationen nicht äqivalent sind. Ich wunderte mich, dass in den java Dateien zum Buch eben die andere Deklaration als im Buch war. Aber so habe ich ja wieder was gelernt. Augenzwinkern Danke
Auf diesen Beitrag antworten »
tigerbine Erben

Folgende Situation, gob gesagt:

code:
1:
2:
3:
4:
5:
class A{
      method a() {} }

class B extends class A {
}


Nun erbt B ja von A. Wie muss ich die Methode a dann aufrufen? Mit super.a() oder this.a()?
Würde sie dann in Fall 2 überhaupt was in B finden, oder weil sie nichts findet in A nach der Methode suchen?
Auf diesen Beitrag antworten »
eulerscheZahl

code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
public class A {
	public void a(){
		System.out.println("a()");
	}
}



public class B extends A{
	public void b(){
		System.out.print("b() -> ");
		a();		
	}
}



public class Main {

	public static void main(String[] args) {
		A a = new A(); B b = new B();
		a.a(); //Ausgabe: a()
		b.a(); //Ausgabe: a()
		b.b(); //Ausgabe: b() -> a()
	}

}
 
Auf diesen Beitrag antworten »
tigerbine

Zitat:
Original von eulerscheZahl
code:
1:
2:
3:
4:
5:
6:
7:
8:
9:

public class Main {

	public static void main(String[] args) {
		A a = new A(); B b = new B();
		a.a(); //Ausgabe: a()
		b.a(); //Ausgabe: a()


D.h. ich kann auf "a" auf beiden wegen zugreifen, auch wenn sie nicht in b explizit implementiert ist (was ja ein überschreiben wäre?) Sucht der compiler dann einfach eine Ebene höher, ob er dort die Methode a findet?

Danke dir.
Auf diesen Beitrag antworten »
eulerscheZahl

B hat von A geerbt, das bezieht sich auf Variablen und Methoden, sofern sie public (Zugriff ist von überall aus möglich) oder protected (Zugriff durch eine Klasse und deren abgeleitete Klassen) ist. Auf Variablen/Methoden von A, die private sind, kann B nicht zugreifen.
Auf diesen Beitrag antworten »
tigerbine

Ok, danke Wink
Auf diesen Beitrag antworten »
tigerbine

Hallo, ich schon wieder.

Wir haben eine abstrakte Klasse mit einer abstracten Methode.
code:
1:
2:
3:
4:
5:
 public abstract class Drache {
  public abstract void spucken();
}

N

Nun eine davon abgeleitete Klasse, die dann die Methode spucken implementieren muss.

code:
1:
2:
3:
4:
5:
public class SchneckenDrache extends Drache {
  public void spucken() {
        //Spucke Schleim }
}

Nun möchte man einen Schneckendrachen erzeugen und ihn Schleim spucken lassen. Warum macht man das so:

code:
1:
2:
3:
4:
5:
6:
public static void main(String[] args){
  Drache drache = new Schneckendrache();
  drache.spucken();
}


Ich denke jetzt an das Trompeterbeispiel von Seite 3 Karlito (Leider darf ich noch keine Links posten). Da sagten wir, man kann in Drache nur benutzen, was in der Klasse Drache ist. Dort gibt es aber nur eine leere Methode spucken. Warum funktioniert das Schleimspucken dennoch? Und was ist der Unterschied zum Trompeter?

Hoffe ihr versteht mein Problem.

edit:
liegt es daran, dass die Methoden gleich heißen? Denn auf eine andersnamige Methode kann ich nicht zugreifen über die Deklaration Drache. Woher weiß java aber, dass es im Falle von spucken in der Objektreferenz schauen muss? Klappt auch bei nicht abstrakten Klassen und Methoden. Also woher weiß die Klasse drache, dass ihre Methode überschrieben wurde?
Auf diesen Beitrag antworten »
Karlito

Hallo tigerbine,

ein Drachen hat doch die Methode spucken. Ob sie implementiert ist oder nicht, ist nicht entscheidend. Eine Abstrakte Klasse ist eine Art Mischung zwischen Normalen Klassen und Interfaces... (Bei Interfaces sind keine Methoden implementiert, bei normalen Klassen sind alle Methoden implementiert und bei Abstrakten Klassen ist normalerweise ein Teil der Methoden implementiert und ein Teil nicht...).

Der Unterschied zum Trompeter ist, dass der Musiker ja die Methode mundstueckWechseln() nicht hat. Somit könnte man folgendes nicht machen:

code:
1:
2:
Musiker trompeter = new Trompeter();
trompeter.mundstueckWechseln(); //geht nicht, da Musiker die Methode nicht haben!


Entlang der Vererbungshierarchie kann das Verhalten von Methoden verändern, solange diese nicht dagegen geschützt sind. Du weißt ja bereits, dass alle Objekte von der Klasse Object erben, auch wenn man das nicht mit angibt. Somit haben alle Objekte auch zum Beispiel die Methode toString(). Oft ist es sinnvoll diese Methode zu überschreiben.

Beispiel:
code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
public class Trompeter extends Musiker{
	private string name;

	public Trompeter(string name){
		this.name = name;
	}

	public void mundstueckWechseln(){
		//WechsleMundstück
	}

	@Override //Kennzeichnung, dass man eine bereits existierende 
                  //Methode überschreibt
	public string toString(){
		return "Trompeter " + name;
	}
} 


Hier würde dann die Standardimplementierung von toString() überschrieben und das neue Verhalten wäre gültig. Probier am besten mal aus, was passiert, wenn du trompeter.toString() aufrufst bevor und nachdem du die Methode überschrieben hast.

VG,

Karlito
Auf diesen Beitrag antworten »
ed209

Zitat:

liegt es daran, dass die Methoden gleich heißen? Denn auf eine andersnamige Methode kann ich nicht zugreifen über die Deklaration Drache.

Wenn die Methode gleich heisst und die gleichen Übegabeparameter hat dann ist es für Java die passende Methode.

Zitat:

Also woher weiß die Klasse drache, dass ihre Methode überschrieben wurde?

Jedes Object weiß zu welcher Klasse es gehört und welche Methoden was machen.
Der Compiler stellt sicher, daß alle deklarierten Methoden auch implementiert werden. Probier einfach mal aus, was passiert wenn Du die
"spucken" methode weglässt.
Generell empfehle ich Dir, einfach mal ein paar unterschiedliche Sachen auszuprobieren und zu gucken wie der Java-Compiler und die Java-Runtime reagieren.

Gruss,
ED
Auf diesen Beitrag antworten »
tigerbine

Zitat:
Original von Karlito
Hallo tigerbine,

ein Drachen hat doch die Methode spucken. Ob sie implementiert ist oder nicht, ist nicht entscheidend. Eine Abstrakte Klasse ist eine Art Mischung zwischen Normalen Klassen und Interfaces... (Bei Interfaces sind keine Methoden implementiert, bei normalen Klassen sind alle Methoden implementiert und bei Abstrakten Klassen ist normalerweise ein Teil der Methoden implementiert und ein Teil nicht...).


Also was mich eben wunder ist, dass wenn ich drache.spucken() aufrufe, warum er dann nicht einfach nichts tut, die Methode ist da ja nicht mit Anweisungen gefüllt. Was wäre, wenn ich dort "Ich spucke nicht" eintragen würde. spuckt der drache dann, oder nicht. Kann ich erst morgen testen.

Danke schon mal wieder für eure Hilfe, aber meine Fragen bleiben euch sicher in nächster Zeit noch erhalten.
Auf diesen Beitrag antworten »
eulerscheZahl

Meinst du so?
code:
1:
2:
3:
4:
5:
6:
public class SchneckenDrache extends Drache {
	public void spucken() {
	        System.out.println("Ich spucke nicht"); 
	}
}

Wenn du
Drache drache = new SchneckenDrache();
schreibst, ist drache vom Typ SchneckenDrache, weil der dazugehörige Konstruktor zum erzeugen verwendet wurde. Da ist dann auch klar, welche Funktion mit drache.spucken(); aufgerufen wird. Wenn du den Drachen spucken lässt, pinselt er dir "Ich spucke nicht" in die Konsole.

Zitat:
Leider darf ich noch keine Links posten

Das war leider nötig, da hier viele Spambots unterwegs waren - wir haben hier mehr Miglieder als Beiträge und etliche dieser Account wurden zum Spammen erstellt unglücklich
Wenn du ein paar Beiträge außerhalb des Off-Topic verfasst, sollte sich das bald ändern (die genaue Grenze, ab wann man Links einfügen darf, kenne ich nicht).
Auf diesen Beitrag antworten »
tigerbine

Ich meinte das so:
code:
1:
2:
3:
4:
5:
6:
public class Drache {
	public void spucken(){
		System.out.println("Ich spucke nicht.");
	};


code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
public class SchneckenDrache extends Drache {
	public void spucken() {
		System.out.println("Ich spucke Schleim.");
	}
	public void rennen() {
		System.out.println("Ich renne schnell.");
	}


code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
public class Spiel {

	public static void main(String[] args) {
		
		Drache drache = new SchneckenDrache();
		drache.spucken();
		
	}


Nun kann ich über drache. nur die Methode "spucken" aufrufen. Die Methode "rennen" nicht. Das ging für mich konform zu den Aussagen von Karlito, weil ich die Deklaration über Drache und nicht SchneckenDrache gemacht habe. Drache besitzt keine Methode "rennen".

Nun kommt mein Problem. Warum wird dann nicht die Methode "spucken" von Drache ausgeführt? Warum kann man hier (und wird hier) eine Methode aus SchneckenDrache ausgeführt. Wieso "überwiegt" in diesem Fall der Konstruktor die Deklaration?

Die zwei Fälle passen für mich nicht zusammen. Sorry, aber die Leitung ist da wohl was länger. Augenzwinkern
Auf diesen Beitrag antworten »
ed209

TL;DR: Du hast immer einen Konstruktor, und von dem hängt ab wie die Methoden implementiert sind.

Welche Implementierung einer Methode auf einem bestimmten Objekt aufgerufen wird, hängt bei Java immer von der Klasse ab zu der das Objekt gehört.
Die Klasse bestimmt sich immer durch den Konstruktor der aufgerufen wird.
code:
1:
Drache drache

bestimmt lediglich den Typen der Variable drache.

Der Typ der Variable gibt an, dass die Variable bestimmte Methoden hat (in diesem Fall ist der Typ Drache und hat damit eine Methode "spucken" ohne Parameter und Rückgabewert), während die Klasse bestimmt wie die Methoden implementiert sind (in diesem Fall ist ein Schneckendrache der Schleim spuckt).
Auf diesen Beitrag antworten »
tigerbine

Prima. Mit "WAS und WIE" kann ich mir das nun merken. Tanzen
Auf diesen Beitrag antworten »
tigerbine Sets

Eine/erste Frage zum Thema Mengen. Ich möchte alle geraden Zahlen zwischen 1 und 40 in die Menge A packen.

code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
package de.galileocomputing.schroedinger.java.kapitel08;

import java.util.HashSet;
import java.util.Set;


public class SetA {

	
	public static void main(String[] args) {
		
		Set<Integer> A = new HashSet<>();
		for (int i=2; i <= 40; i=i+2) {
			A.add(i);
			System.out.println(A);
		}
		
	}

}


Nun wundere ich mich über die Ausgabe. Bis 14 ist für mich noch alles im Lot. Was ist dann los?

code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
[2]
[2, 4]
[2, 4, 6]
[2, 4, 6, 8]
[2, 4, 6, 8, 10]
[2, 4, 6, 8, 10, 12]
[2, 4, 6, 8, 10, 12, 14]
[16, 2, 4, 6, 8, 10, 12, 14]
[16, 2, 18, 4, 6, 8, 10, 12, 14]
[16, 2, 18, 4, 20, 6, 8, 10, 12, 14]
[16, 2, 18, 4, 20, 6, 22, 8, 10, 12, 14]
[16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 12, 14]
[16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 14]
[16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14]
[16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30]
[32, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]
[34, 32, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]
[34, 32, 2, 4, 36, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]
[34, 32, 2, 38, 4, 36, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]
[34, 32, 2, 38, 4, 36, 6, 8, 40, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]


edit: mit einem TreeSet bekomme ich eiene geordnete Ausgabe. Dennoch verwirrt mich die Unordnung, da die Zahlen in der for Schleife ja der Größe nach an die Menge übergeben werden.
Auf diesen Beitrag antworten »
eulerscheZahl

Wenn du beim HashSet erst die 4 und dann die 2 einfügst, ist die 2 trotzdem vor der 4.

Da ich dein Vorwissen nicht kenne, hole ich etwas weiter aus:
Stell dir vor, du hast ein Wörterbuch und willst dann einen Eintrag hinzufügen. Logischerweise fügst du ihn dort ein, wo er nach alphabetischer Reihenfolge hingehört und nicht ans Ende. Genau das tut Java hier auch. Es sortiert den Eintrag ein, auch wenn die Sortierung etwas seltsam scheinen mag.
Du kannst aber nicht nur ein Set<Integer> erstellen, sondern auch String, Double oder sogar eingene Klassen verwenden. Hierfür gibt es Funktionen, die das alles zu einer Zahl mit z.B. 4 Byte umwandeln können (was zur Folge hat, dass etwa 2 Zeichenketten des selben Hash haben können, das heißt dann Kollision). Nach diesem Hashwert sortiert werden die Zahlen dann in die Liste eingefügt.

Der Vorteil ist, dass man die Liste mit logarithmischer Zeit durchsuchen kann: will man wissen, ob ein Wert in der Liste steht, bildet man den Hash. Ist der größer als der des ersten Eintrags und kleiner als der des letzten, vergleicht man danach mit dem Eintrag in der Mitte. Nun prüft man wieder, in welchem der beiden Intervalle ( ]Anfang;Mitte[ oder ]Mitte;Ende[ ) der Wert liegt, konnte als in nur einem Schritt die Möglichkeiten auf die Hälfte reduzieren.
Auf diesen Beitrag antworten »
tigerbine

Danke für die Erklärung!
Auf diesen Beitrag antworten »
Karlito

Hallo euler, hallo tigerbine,

noch ein paar Anmerkungen:
Hashsets sind so konstruiert, dass möglichst schnell Dopplungen gefunden werden können. Dazu wird, wie von euler beschrieben eine Hashfunktion benutzt.
Der Zugriff auf das Hashset funktioniert jedoch anders als von euler beschrieben und hat auch in vielen Fällen eine bessere Laufzeit (O(1)!).

Erklärung:
Man stelle sich ein Array mit 10 Plätzen vor. Nehmen wir nun eine Funktion, welche einem Objekt (hier einer Zahl) einen Hashwert zuordnet. Bei einem Array mit 10 Werten benutzt man einfach eine Hashfunktion, welche die Werte 0-9 ausgibt. Dabei entspricht der Hashwert genau dem Index im Array.

Bei einem Set ist diese Art der Speicherung sehr günstig, da jedes Objekt in einem Set (entsprechend der mathematischen Menge) nur ein mal Vorkommen darf und die Ordnung keine Rolle spielt.

Fügt man nun ein Objekt in dieses Set ein, so wird der Haswert gebildet und geschaut, ob sich an dem Ort im Array bereits ein Objekt befindet. Wenn ja, wird das Objekt mit dem einzufügenden Objekt verglichen. Sind sie gleich, so wird das einzufügende Objekt verworfen. Sind sie ungleich (Kollision), dann wird an der Stelle im Array ein weiteres Objekt eingehängt. Dafür wird im Normalfall eine verkettete Liste verwendet.

Edit: Befindet sich an der Stelle kein Objekt, so wird eine neue verkettete Liste mit einem Element angelegt in an die Stelle im Array gelegt.

Beim Auslesen des gesamten Hashsets werden nun alle Zellen darauf geprüft, ob sie einen Inhalt haben oder nicht. Wenn ja wird der Inhalt ausgegeben. (Eventuell gibt es geschicktere Implementierungen).

Edit: dabei hängt die Reihenfolge von der verwendeten Hashfunktion bzw. den verwendeten Hashfunktonen ab. (siehe weiter unten)

Will man untersuchen, ob ein Objekt bereits in der Liste ist, so bildet man wie im Einfügevorgang einen Hashwert schaut am ensprechenden Index danach, ob das Objekt sich da befindet. Meistens sollte dabei nur ein oder kein Objekt gefunden werden und somit ist der Zugrif O(1). Da Hashfunktionen aber nicht ideal sind, und die Eingabewerte auch nicht, kann es dazu kommen, dass viele Kollisionen entstehen. Somit müsste die gesamte verkettete Liste nach dem Objekt durchsucht werden und die Suche hätte lineare Laufzeit.

Weiteres: Hashsets haben eine Initalgröße (s. Doku) und vergrößern sich automatisch und zwar bereits bevor das verwendete Array voll ist um Kollissionen zu vermeiden. Dabei wird die benutzte Hashfunktion dynamisch angepasst und teilweise werden nach dem Vergrößern mehre Arrays mit den dazugehörigen Hashfunktionen genutzt. Weiterhin ist das Ziel einer guten Hashfunktion für Hashtabellen eine gute Streuung um Kollisionen zu vermeiden. O(1) wird wie beschrieben als ideal angesehen und in der Realität selten erreicht, aber im Allgemeinen nähert es sich konstanter Laufzeit.

@euler: was du beschrieben hast ist binäre Suche und wird bei sortierten Arrays angewendet.

VG,

Karlito
Auf diesen Beitrag antworten »
eulerscheZahl

Danke für die Korrektur, werde ich mir merken. Daumen hoch
 
Neue Frage »
Antworten »


Verwandte Themen

Die Beliebtesten »
Die Größten »
Die Neuesten »