Einführung in die Programmierung
Posted on July 14, 2023 (Last modified on December 7, 2023) • 39 min read • 8,307 wordseinige Notizen meiner EProg Vorlesung an der ETH Zürich
Diese Zusammenfassung bezieht sich auf diese Veranstaltung: 252-0027 | Einführung in die Programmierung (EProg), HS 2021/22, Doziert von Thomas R. Gross an der ETH Zürich
Es werden die Grundlagen (Anweisungen und Datenstrukturen) für das Erstellen von einfachen Progammen in Java vermittelt. Die Vorlesung „Einführung in die Programmierung“ vermittelt das theoretische Wissen zu Java. Dieses Wissen ersetzt allerdings auf keinen Fall die Übung vom selber programmieren.
Diese Notizen hätte ich ohne zusätzlichen Unterlagen sowie weiterer Literatur nicht schreiben können:
Danke an Jan Stauffer, für seine Zusammenfassung zu EProg
Ein ganz grosses Merci an den einzig wahren Ruben Schenk. Seine Webseite rwgs.ch kennt jeder ETH-CS Student.
„Sprechen Sie Java?“ von Hanspeter Mössenböck diente mir als ergänzende Literatur
Eine Programmiersprache legt fest, wie eine Lösung eines Problems beschrieben werden kann. Je mehr Regeln (also Beschränkungen) eine Programmiersprache hat, desto aufwendiger wird die Entwicklung. Eine Programmiersprache definiert erlaubte Anweisungen, aus denen ein Programm in dieser Sprache besteht. Es werden auch Wertebereiche und Formate definiert.
Eine Art, eine Programmiersprache zu beschreiben, ist die
E – Extended
B – Backus
N – Normal
F – Form
EBNF ist eine Erweiterung der Backus-Naur-Form (BNF). Die BNF wurde ursprünglich von Niklaus Wirth, einem Schweizer Informatiker, zur Darstellung der Syntax von Pascal eingeführt. EBNF ist eine formale Metasyntax (Metasprache). Benutzt wird sie um kontextfreie Grammatiken darzustellen.
Jede Beschreibung hat einen Namen und besteht aus einer Reihe EBNF-Regeln. Die Reihenfolge spielt bei Mengen keine Rolle. Jede Regel besteht aus:
EBNF- Beschreibungen bestehen aus 4 Elementen, die „control forms“ genannt werden:
Grafische Darstellung von EBNF Regeln
LHS ⟸ a1 a2 a3
<digit> ⟸ 1 2 3 4 5 6 7 8 9 0
Die Beschreibung wird von links nach rechts gelesen, die Reihenfolge ist wichtig. Jeder Name darf nur einmal definiert werden und die einzelnen Namen werden mit einem Abstand getrennt.
LHS ⟸ a1 | a2
<searchmachine> ⟸ Google | DuckDuckGo | Bing
Die Alternative werden mit einem Stroke („|“) getrennt. Die Reihenfolge spielt keine Rolle. Jede Alternative folgt den EBNF Regeln.
LHS ⟸ [ a1 | a2 ]
<closed_answer> ⟸ [ yes | no ]
Optionen werden in „square brackets“ ( „[“ sowie „]“ ) ausgedrückt. Die Optionen können gewählt werden, müssen aber nicht.
LHS ⟸ { a1 | a2 }
<digit_sequence> ⟸ { 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 }
Elemente in einer Wiederholung, dargestellt zwischen zwei „curly braces“ ( „{“ sowie „}“ ), können beliebig oft wiederholt werden, es sind also auch null Wiederholungen möglich.
LHS ⟸ a1 | LHS
---------------------------
<number99> ⟸ 99
<number998> ⟸ <number99> 8
Eine EBNF-Regel ist direkt rekursiv, wenn ihr Name in der Definition verwendet wird:
r ⟸ A | A r
Folgende Regel ist indirekt rekursiv:
<name1> ⟸ A name2
<name2> ⟸ B name1 | C
zB Definition ganzer Zahlen:
<digit> ⟸ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<integer> ⟸ [ + | − ] digit { digit }
https://de.wikipedia.org/wiki/Wp-Kalkül
Ctrl
+ Shift
+ F
: Auto FormatCtrl
+ Shift
+ O
: Clean up imports and imports needed libariesF1
: alle CommandsVererbung von Klassen
Fehler:
super
. And here, as ‘fct3()’ is not in ‘Gamma’ but in ‘Epsilon’, also the attribute from ‘Epsilon’ is used.Das Grundgerüst eines einfachen Java Programm sieht so aus:
public class name {
public static void main(String[] args) {
instruction;
instruction;
...
}
}
Jedes ausführbare Java Programm besteht aus mindestens einer Klasse class
, welche eine Methode enthält, die verschiedene Anweisungen ausführt. Es muss eine Methode main
existieren, die verschiedene Anweisungen instructions
enthält.
Die Klasse class
sollte dem Namen*.java* entsprechen. Programmnamen sowie Klassen beginnen in der Regel mit einem _Gross_buchstaben.
Kommentare können verwendet werden, um Java-Code zu erklären und ihn besser lesbar zu machen. Sie können auch verwendet werden, um die Ausführung beim Testen von alternativem Code zu verhindern:
//
. Jeglicher Text zwischen //
und dem Ende der Linie wird von Java ignoriert./*
und enden mit /
. Der Text dazwischen wird von Java ignoriert./* This comment goes across
several lines. */
System.out.println("Hello World");
// The program is ready here. This comment has only one line.
Sonderzeichen können von Java mittels print
dank dem Backslash \\
ausgegeben werden.
Eine Methode/ Funktion ist eine Abfolge von Anweisungen mit einem Namen. Sie strukturieren den Code und ermöglichen die Reduzierung von wiederholendem Code. Die Anweisungen einer Methode werden nur ausgeführt, wenn die Methode aufgeruft wird. Eine Ausnahme bildet hierbei die main
Methode – sie wird aufgerufen, wenn das Programm ausgeführt wird.
Die Abfolge der Anweisungen wird als control flow bezeichnet.
In ihrer einfachsten Form haben Methoden keine Parameter.
// Declaration
public static void method() { // method header
statement; // method body
}
// Call
method()
Die Deklaration einer Methode besteht aus einem Kopf (header) und einem Rumpf (body). Im Header wird der Name der Methoder sowie eventuelle Parameter definiert.
Das Schlüsselwort static
gibt an, das die Methode statisch aufgerufen wird. void
hingegen besagt, dass die Methode keinen Wert zurückliefert.
Modifier Datentyp Methodenname (Parameterliste)
{
Anweisungen;
}
Aus Lesbarkeitsgründen sollte der Name der Methode ausdrücken, was sie leistet. Somit beginnt der Name zumeist mit einem Verb. Der erste Buchstabe einer Methode wird klein geschrieben. Sofern der Methodename aus mehreren Wörtern besteht, sind Folgeworte mit einem Grossbuchstaben zu beginnen.
Manchmal ist es erforderlich, eine Methode den Gegebenheiten anzupassen. Ein Parameter ist ein Wert, der beim aufrufen der Methode übergeben wird. Der übergebene Wert ist der tatsächliche Parameter oder das tatsächliche Argument.
// Declaration
public static void method(type1 name1, ..., typeN nameN) {
statement;
}
// Call
method (value1, value2, ..., valueN);
Es gibt zwei Arten von Parametern:
Bei der Übergabe von Parametern werden erst die aktuellen Parameter berrechnet, anschliessend werden die Werte dieser Ausdrücke den formalen Parameter zugewiesen. Es gelten die Regeln der Zuweisung.
Es ist auch möglich einen Array als Parameter an eine Methode zu übergeben. Umgekehrt kann eine Methode auch einen Array zurückgeben.
// Array as parameter
public static type methodName(type[] name) {...}
// return an Array
public static type[] myMethod (...) {
...
return array;
}
// initiliaze an array
int[] arr = new int[5];
arr[0] = 24;
Ein Array ist jedoch kein Basistyp. Hier wird er genauers erläutert.
Wenn Parameter eines primitiven Typs (zB int
, double
, boolean
) an eine Methode übergeben werden, so erstellt die Methode lokale Kopien dieses Wertes. Primitive Typen werden wertmässig übergeben.
Veränderung der Parameter Variable (des formalen Parameters) in der aufgerufenen Methode haben keine Auswirkung auf die aufrufende Methode.
Parameter ermöglichen es, Werte an die aufgerufene Methode zu übergeben. Ein Rückgabewert erlaubt es der aufgerufenen Methode einen Wert zurückzugeben. Methoden die einen Wert zurückliefern heissen Funktionen. Dies geschieht mit dem Schlüsselwort return
. Der Typ des Rückgabewerts muss in der Methodensignatur, also vor der Methode, definiert werden.
Eine Funktion muss immer mit einer return
-Anweisung beendet werden. Wenn eine if/else Anweisung verwendet wird, und innerhalb dieser if/else Anweisung der Wert zurückgegeben wird, so muss es in jedem möglichen Ast ein return
-Statement vorhanden sein.
public static type method(type1 name1, ..., typeN nameN) {
statement;
return expression;
}
Wenn eine Methode hingegen keinen Wert zurückliefert, nennt man sie Prozeduren. Die Signatur für eine Prozedur ist void
(bedeutet „kein Ergebnistyp“). Die return
-Anweisung kann eine Prozedur vorzeitig beeenden. Da eine Prozedur jedoch keinen Rückgabewert liefert, wird einfach return
geschrieben.
public static void method(type1 name1, ..., typeN nameN) {
statement;
if (statment == true) return; //premature abort
statement;
}
Die Daten von einem Programm werden in Variablen gespeichert. In der Mathematik steht das Wort Variable für Werte, in der Informatik jedoch ist eine Variable eine Speicherzelle für einen Wert. Der Inhalt einer Variable ist also veränderlich. Variablen verfügen jedoch nicht nur über einen Namen, sondern auch über einen Datentyp.
Eine Variable muss vor ihrer ersten Verwendung deklariert werden.
int x;
Hierbei wird eine Variable x als ganze Zahl int
deklariert. Es wird bei der Ausführung des Programms eine entsprechend grosse Speicherzelle reserviert. Es können auch mehrere Variablen vom gleichen Typ gleichzeitig deklariert werden:
int x, y;
Bei der Initalisierung kann man auch bereits einen Anfangswert angegeben werden:
int x = 100;
Eine Variable kann jedoch nur Werte von ihrem Typ speichern:
int x = 4.75 // ERROR: incompatible types
Der Scope ist der Teil eines Programms, in dem eine Variable sichtbar ist. Eine Variable die beispielsweise in deiner Methode definiert wurde, existiert nur in der Methode. Das selbe gilt etwa für die for
-Schleife.
Namen bezeichnen Dinge (zum Beispiel Variablen, Konstanten, Typen oder Methoden) in einem Programm. Sie bestehen aus Buchstaben, Zahlen sowie den Zeichen _
und $
. Gross- und Kleinbuchstaben werden unterschieden.
Schlüsselwörter sind spezielle Namen wie return
, while
oder if
. Sie werden eingesetzt um Programmteile einzuleiten, hervorzuheben oder zu beenden. Sie sind reserviert – man darf sie also nicht für Namen verwenden. Einen Überblick über die Schlüsselwörter von Java findet man in der Doku.
Datentypen in Java
Typen beschreiben die Eigennschaften von Daten. Es wird durch die Programmiersprache definiert, wie ein Typ implementiert wird.
Primitive types sind in Java integrierte einfache Typen. Dazu gehören Boolean, Character, Integter und Floting-point. Diese Werte können etwa nicht null
sein.
Primitive Datentypen in Java
Arithmetische Operatoren verknüpfen mehrere Werte/Ausdrücke.
+: Addition,
-: Subtraktion,
*: Multiplikation,
/: Division; Ergebnis ganzzahlig: 13/2 = 6, (-4)/3 = -1
%: Modulo (d. h. Divisionsrest)
Bei der Division oder auch dem Anwenden von Modulo einer int
-Zahl entspricht das Ergebnis ebenfalls einer int
-Zahl.
Die Umwandlung von Typen geschieht in Java automatisch. Bei der Division eines int
-Wert mit einem double
-Wert ist das Ergebnis ein double
-Wert.
+: Identitaetsoperator; +x enstpricht x
-: Vorzeichenumkehr
Unäre Operatoren beziehen sich nur auf einen einzigen Operanden. Unäre Operanden binden stärker als binäre Operatoren.
4 + -3 = 1
-4 + 3 = -1
Die Reihenfolge in der die Operatoren ausgeführt werden nennt man Precedence. Wenn zwei Operatoren die selbe Assoziativität und Rangordnung haben, so werden diese von links nach rechts ausgewertet. Die Presedence in Java sieht wie folgt aus:
()
/
, %
+
, ``Um der Wert einer Variable zu erhöhen/ zu vermindern schreibt man:
x++; x--;
++x; --x;
&&; logisches und
|| ||; logisches oder
!; logische Negation
Für die Anpassung von Werten einer Variable gibt es in Java eine kürzere Schreibweise. Das Programm wird allerdings durch die Schreibweise nicht schnelller.
if
-Anweisungen Eine if
-Anweisung prüft eine Bedingung, die wahr oder falsch sein kann. Je nach dem führt das Programm dann eine andere Anweisung aus.
if (condition) {
// statments
}
else {
// statments
}
Der else
-Zweig kann auch fehlen. Es existiert ebenfalls eine Kurzform von if
-Anweisungen:
if (x > y) max = x; else max = y;
else
Dangling else
beschreibt die Problematik von mehreren verschachtelten if
Anweisungen in der Kurzform:
if (a > b)
if (a > 0) max = a;
else
max = b;
Hierbei baumelt das else
in der Luft. Aus diesem grund empfiehlt es sich, den if
– sowie den else
-Zweig immer in geschweifte Klammern einzuschliessen.
In zusammengesetzten Vergleichen giltet folgende Regel: !
bindet stärker als &&,
was wiederum stärker als ||
bindet.
?
-Operator Der ?
Operator erlaubt die Auswahl zwischen zwei Alternativen:
boolean_expression ? expression1 : expression2
Wenn die boolean_expression
true
ergibt, wird die expression1
ausgeführt, sonst die expression2
.
switch
Anweisung Mit Hilfe der switch
-Anweisung kann man den Wert einer Variable des Typs int
oder String überprüfen. Der Wert der Variable wird mit verschiedenen Cases verglichen und bei einer Übereinstimmung wird der dazugehörige Programmcode ausegeführt.
switch (variable) {
case Wert1:
// Code-Block für den Fall, dass variable = Wert1
break;
case Wert2:
// Code-Block für den Fall, dass variable = Wert2
break;
// ...
default:
// Code-Block für alle anderen Fälle
}
switch(value)
wird einen Wert übergeben. Dieser Wert wird dann mit dem Wert nach case
verglichen.
Loops erlauben das wiederholte Ausführung von Anweisungen.
for
-Loop Die Syntax eines for
-Loops sieht so aus:
for (initialization; condition; update;) {
statements;
statements;
}
//Example
for (i = 0; i<10;i++) {
System.out.println(i);
};
for (type name: collection) {
statements;
}
Reihenfolge:
HashSet
- nicht definiertLinkedHashSet
- wie eigefügtArrayList
- wie im Array gespeichertLinkedList
- wie in der Liste gespeichertTreeSet
- aufsteigend (nach compareTo)while
– Schleife while
-Schleifen führen den Loop solange aus, wie der boolsche Wert true ergibt.
while (tests) {
statements;
}
do
–while
-Schleife Die do
–while
-Schleife unterscheidet sich von der while
-Schleife insofern, dass der Test erst nach dem ersten Ausführen des Body’s ausgewertet wird.
do {
statement;
...
} while (test);
Manchmal tritt in einer Schleife einen Zustand ein, der eine weitere Ausführung der Schleife unnötig macht. In diesem Falle kann man die Schleife vorzeitig mit break
beenden.
Mit einer break
-Anweisung kann man nicht nur eine unmittelbbar umgebende Schleife verlassen, sondern auch eine äussere. Erst muss dann aber die äussere Schlaufe mit einem Label name:
markiert werden. Genau dieses Label muss dann bei break
referenziert werden.
label_out:
for( int i = 0; i < 10; i++) {
for(int j = 0; j < 5; j ++ )
{
break label_out;
}
}
Dieses Kapitel deckt einige wichtige und nützliche Standartlibraries ab.
String
ist ein Typ von der Java Library String. Strings können mit dem +
Operatoren kombinieriert werden
String firstName = "Thomas";
String lastName = "Gross";
String fullName = firstName + " " + lastName; //Thomas Gross
Eine String
Variable ist ein String
-Objekt. Man kann in der Library definierte Methoden anwenwenden, wenn man die Referenz mit dem Operator .
dereferenziert.
firstName.indexOf
.indexOf(string);
gibt den Index an, wo der angegebene.charAt(index);
Gibt den char
-Wert am angegebenen Index zurück..length();
Gibt die Länge vom String zurück.toLowerCase();
Konvertiert alle Zeichen in diesem String in Kleinbuchstaben.toUpperCase);
Konvertiert alle Zeichen in diesem String in Grossbuchstaben.substring(
int beginIndex, int endIndex);
Gibt einen neuen String zurück, der ein Substring vom String ist.Zwei Strings können nicht mit normalen Vergleichsoperatoren verglichen werden. Wenn man sie dennoch vergleichen möchte, muss man die Methoden der String-Library verwenden.
.equals(str);
Vergleicht den String zum angegebenen Objekt.equalsIgnoreCase(str);
Vergleicht diesen String mit dem anderen String, wobei die Gross- und Kleinschreibung nicht berücksichtigt wird.startsWith(str);
Prüft, ob der String mit der angegebenen Zeichenfolge beginnt.endsWith(str);
Prüft, ob der String mit der angegebenen Zeichenfolge endet.contains(str);
Prüft exakt, ob die angegebene Zeichenfolge im String enthalten ist.split(str)
Mit split lässt sich ein String in ein Array mit mehreren Strings teilen. Die Grenze wird mit str definiert. Mit dem regex OR Operator |
lassen sich mehrere Seperatoren definieren.Weitere Infos können der Java Dokumentation entnommen werden.
Die Klasse math
enthählt alle speziellen mathematischen Funktionen, die man zum Berechnen/Simulieren braucht.
Math.abs(value);
Gibt den absoluten Wert von value zurückMath.ceil(value);
rundet aufMath.floor(value);
rundet abMath.log10(value);
Gibt den Logarithmus zur Basis 10 eines Double-Wertes zurück.Math.max (value1, value2);
Gibt den grösseren von zwei Werten zurück.Math.min (value1, value2);
Gibt den kleineren von zwei Werten zurück.Math.pow (base, exp);
Gibt den Wert base hochgezählt in der Potenz von exp zurück.Math.random();
gibt einen zufälligen double-Wert zwischen 0 und 1 zurückMath.round(value);
rundet zur nächsten ganzen ZahlMath.sqrt(value);
Gibt die korrekt gerundete positive Quadratwurzel von value zurück.Math.sin(value); Math.cos(value); Math.tan(value);
Sinus, Kosinus und Tangens von einem Winkels im BogenmassMath.toDegrees(value); Math.toRadians(value);
konvertiert Grad in Bogenmass oder umgekehrtMath.E;
Die Konstante 2.7182818…Math.PI;
Die Konstante 3.1415926…
wichtige Methoden der Math Library, übernommen von Jan Stauffer
weitere Methoden können in der Java Dokumentation nachgeschlagen werdenwichtige Methoden der Math Library, übernommen von Jan Stauffer weitere Methoden können in der Java Dokumentation nachgeschlagen werden
Manchmal möchte man etwas ausgeben, was nicht nur einer einfachen Konsole Ausgabe mit System.out.println("output")
entspricht. PrintStream
erlaubt es Daten, zum Beispiel als File, auszugeben.
Print Stream output = new PrintStream(new File("filename"));
output.println("your output data here");
Wenn die Datei noch nicht existiert, dann wird sie erstellt. Ansonsten wird die bereits vorhandene Datei einfach überschrieben.
Die Klasse File erlaubt es, Operationen mit Dateien durchzuführen.
import java.io.*;
File file = new File("name.txt");
Der Ausdruck new File("name.txt")
erstellt ein Objekt (Handler), welches für eine Datei mit diesem Namen steht. Diese Datei kann mit diesem Handler manipuliert werden. Es wird aber kein Datei erstellt.
Bevor man einen Scanner
für einen Datei Handler erstellt, muss man mittels exists()
sicherstellen, dass die Datei auch wirklich existiert. Sonst wäre ein Fehler FileNotFoundException das Resultat.
Ein interaktives Programm liest Benutzereingaben. Diese Eingabe kann in Form einer Datei, einer Konsole oder eines interaktiven Fensters übermittelt werden. Es müssen somit auch alle Arten von ungeeigneten Eingaben verarbeitet werden. Eine Möglichkeit, Eingaben in Java zu verarbeiten, ist die Klasse Scanner
. Der Scanner kann für eine beliebige Eingabequelle erstellt werden, sei es für die Konsole (via System.in
) oder eine Datei.
nextInt();
Scannt das nächste Objekt als int
nextDouble();
Scannt das nächste Objekt als double
next();
Scannt eine durch Zeichen abgetrennte Ein-Wort-ZeichenkettenextLine();
Scannt einen String mit der Länge einer Zeile einWenn wir den nächsten Wert z. B. eine integer
einlesen möchten, aber es keinen nächsten Wert gibt, dann löst der Scanner
einen Fehler aus. Daher gibt es folgende Methoden, um zu prüfen, ob ein Wert wie dieser existiert:
hasNextInt();
gibt true
zurück, wenn es einen int
-Wert zum lesen gibthasNextDouble();
gibt true
zurück, wenn es einen double
-Wert zum lesen gibthasNext();
gibt true
zurück, wenn es etwas zum lesen gibthasNextLine();
gibt true
zurück, wenn es eine Zeile zum lesen gibt/*Beispiel für eine Eingabe von einer Datei */
Scanner scan = new Scanner (new File("filename"));
while (scan.hasNextLine()){
Scanner line = new Scanner(scan.nextLine());
}
/*Beispiel für eine Eingabe von der Konsole */
Scanner scanner = new Scanner(System.in);
System.out.print("Geben Sie eine natürliche Zahl ein: ");
int n = scanner.nextInt();
Es gibt zwei Fehlertypen, die uns begegnen können, wenn wir nicht sorgfältig mit dem Scanner arbeiten:
Ein Array ist eine Liste/ Reihe von einer gegebenen Anzahl Werten des selben Typs. Der Typ sowie die Länge length
, die bei der Deklaration festgelegt werden, können nicht mehr geändert werden. Das Element beschreibt einen Eintrag im Array und Index der Ort des Arrays (Der Index beginnt mit 0). Der Typ des Elements ist int
, der Typ des Arrays ist int[]
. Im Gegensatz zu den einfachen Typen (int
, double
, float
…) nennt man zB int[]
strukturierte Typen.
Die Festlegung der einzelnen Werte heisst Initialsierung.
type[] nameArray = new type[length];
// for example
int [] a = new int[4]
// 2DArray
int [] [] dp = new int [3] [2];
Eine Arrayvariable enthält einen Zeiger auf ein Array. Das erzeugte Array hat vier Elemente, die Elemente werden mit a[0]
bis a[3]
angesprochen
Bei der Initialisierung des Arrays, enthält jedes Element den Wert null
für den entsprechenden Typ:
Der Zugriff auf Elemente funktioniert so:
nameArray[i] = value;
System.out.println(nameArray[i];
Wenn man auf einen Index zugreifft, der sich ausserhalb der Spannweite des Arrays befindet, dann erhält man einen out-ouf-bounds Fehler.
int a, b;
a = new int[4];
a = b;
Mit der Zuweisung a = b;
zeigen nun das Array a
sowie das Array b
auf die selben Speicherzellen.
Das bedeutet die Speicherzellen lassen sich nun zB mit a[0]
aber auch mit b[0]
ansprechen. Eine Zuweisung von a[0]
betrifft automatisch auch b[0]
.
a = new int[3];
Wenn man nun dem Array a
ein neues Array zuweist, so beziehen sich a
und b
auf andere Speicherzellen und somit verschiedene Arrays.
Mit der Zuweisung b = null;
wird erwirkt, dass die Variable b
auf kein Array zeigt. Es ergibt sich folgendes Bild:
Für Arrays funktioniert die .equals
-Methode sowie der ==
Operator nicht. Aus diesem Grund werden folgende Methoden von der java.util
Library mitgeliefert:
Während bei der Übergabe oder einer Zuweisung von einem Basistyp der Wert der entsprechenden Variable übergeben wird, greift man beim Array direkt auf die gespeicherten Daten (also auf die Speicherzelle) zu. Ein Array ist somit eine Referenzvariable (engl. reference type variable). Eine Referenzvariable bezieht sich auf ein Objekt (zB ein Array, einen String oder Scanner).
Wenn ein Array einer Methode als Parameter übergeben wird, muss im Unterschied zu den Parametern, welche einem Basistyp entsprechen, keine Kopie vom Array angelegt werden. Das ist so, weil ein Array eine Refernzvariable ist und somit keine Daten speichert. Es verweist jeglich auf den Speicherort. An diesem Speicherort können jedoch Aktionen vorgenommen werden, die bleibenden Charakter haben
Es gibt folglich zwei Arten wie Parameter übergeben werden können: value semantics (oben erwähnt, int, double etc.) und reference semantics (String, Array…).
Darstellung im Source Code von Java | Symbol |
---|---|
\t | Tabulatorenzeichen |
\n | neue Zeile |
\" | Anführungszeichen |
\\ | Backslash |
Typ | Beschreibung | Wertebereich | Grösse | Beispiel |
---|---|---|---|---|
boolean | Logische Werte | true/false | 1 Bit | Boolean one = false |
char | 0 bis 65535 | 2 Bytes (16 Bit) | char letterA = ‘A’ | |
byte | -27 bis 27-1 | 1 Byte (8 Bit) | byte a = 10, | |
byte b = -20 | ||||
short | -215 bis 215-1 | 2 Bytes (16 Bit) | short s = 10000, | |
short r = -5000 | ||||
int | ganze Zahlen | -231 bis 231-1 | 4 Bytes (32 Bit) | int a = 100000, |
int b = -200000 | ||||
long | grosse ganze Zahlen | -263 bis 263-1 | 8 Bytes (64 Bit) | long a = 100000L, |
long b = -200000L | ||||
float | +/- 1,4*10-45 bis +/- 3,438 | 4 Bytes (32 Bit) | float f1 = 234.5f | |
double | Reelle Zahlen | +/-4,9^-324 bis +/-1,7308 | 8 Bytes (64 Bit) | double d1 = 12.3 |
Kurzform | Langform | |
---|---|---|
+= | x += y; | x = x + y; |
-= | x -= y; | x = x - y; |
*= | x *= y; | x = x * y; |
= | x /= y; | x = x / y; |
%= | x %= y; | x = x % y; |
Operator | Bedeutung |
---|---|
== | gleich |
!= | ungleich |
> | grösser |
< | kleiner |
>= | grösser oder gleich |
<= | kleiner oder gleich |
Name der Methode | Beschreibung |
---|---|
File.exists( ); | gibt ture zurück, falls diese Datei existiert, sonst false |
File.canRead( ); | gibt ture zurück, falls diese Datei gelesen werden kann |
File.getName( ); | gibt den Namen der Datei zurück |
File.length( ); | gibt die dateigrösse, in Bytes, zurück |
File.delete( ); | löscht die Datei |
File.renameTo(file); | bennennt die Datei um |
null Wert | Typ |
---|---|
0 | int |
0.0 | double |
false | boolean |
null | non-primitive types |
Name der Methode | Beschreibung |
---|---|
binarySearch(array, value); | gibt den Index von einem Wert in einem sortierten Array wieder. Wenn der Wert nicht gefunden wurde, wird ein Index < 0 ausgegeben. |
copyOf(array, length); | Gibt eine neue Kopie des Arrays zurück |
equals(array1, array2); | Gibt true zurück, wenn zwei Arrrays die selben Elemente in der selben Reihenfolge enthalten. |
fill(array, value); | Der Wert wird jedem Element des Arrays zugewiesen. |
sort(array); | sortiert die Elemente des Arrays. |
toString(array); | Wandelt das Array in einen string um. |
Eine Klasse wird mit dem Keyword class
deklariert, wobei die enthaltene Methode main
immer automatisch aufgerufen wird, sobald die Klasse verwendet wird. Klassen erlauben es, mehrere Elemente zu einem neuen Objekt zusammenzufassen und mit dem gleichen Namen anzusprechen. Klassen können im Gegensatz zu Arrays Elementen verschiedener Typen enthalten.
Ein Objekt (object) beschreibt alle Datenwerte, welche durch eine Klasse beschrieben werden (zB Scanner
, Zufallsgenerator, String
). Sie verbinden den Zustand (state) mit dem Verhalten (behavior). Objekte sind Exemplare (instances) einer Klasse. Ein Objekt muss mit new
instanziert werden. Für Strings und Arrays sowie primitive Datentypen benötigen wir kein new
.
Scanner console;
console = new Scanner(System.in);
String name = "Thomas";
Random rand = new Random();
Eine Klasse ermöglicht es, einen Algorithmus zu implementieren. Dabei bietet die Klasse jeweils einen Dienst an den Client an. Dabei existieren zwei Arten von Diensten:
dot
Notation nötig – zB Arrays.toString(...)
.In den meisten Fällen beschreiben Klassen einen Typ, sie beschreiben also Eigenschaften von Daten. Zusätzlich werden in den Klassen Operationen auf diese Datenwerten beschreiben. Sie können alles beschreiben und sind nicht an die primitiven Typen gebunden.
Objekte einer Klasse werden automatisch durch den Garbage Collector freigegeben. Sobald ein Objekt nicht mehr durch einen Zeiger referenziert wird, gibt der Garbage Coller den Speicherplatz wieder frei.
Objekte können wie die primitiven Datentypen einer Methode als Parameter übergeben werden. Ebenso können sie von der Methode mittels return zurückgegeben werden:
//object as a parameter
public static void name(classname name) {};
public static void getArray(int[] arr) {};
// return an object
public static type name() {};
public static int[] name() {
...
return int[] name_of_int_array;
}
OPP ist ein Programmiermodel, das ein Programm als eine Menge von aufeinander einwirkende Objekte organisiert. Die Objekte interagieren, also ein Objekt ruft Methoden (Services) eines anderen Objektes auf, welches dann Werte zurückgibt. Sie (die Objekte) stellen Methoden (Services) zur Verfügung, die ihren Zustand verändern sowie Werte zurückgeben können. Die Klassen, bestehend aus Daten und MEthoden, sind folglich die Grundbausteine der objekttorientierten Programmierung.
Die Abstraktion ist ein wichtiges Grundprinzip von OPP. Ein Dienst kann genutzt werden, ohne das man den Inhalt von eben diesem kennt. Dies macht es möglich, dass man die Interna eines Dienstes ändern kann, ohne dass man seine Clients anpassen muss.
Eine Klasse wird in Java wie folgt definiert:
class Community {
int zipcode;
String name;
int founding_year;
}
Die Variablen in einer Klasse nennt man Felder (fields)/ Attribute.
Klassen werden auf der äussersten Ebene einer Datei definiert, also wie folgt:
class Community {
...
};
public class main {
public static void main(String[] args) {
instruction;
instruction;
...
}
};
Eine Refernezvariable bezieht sich auf ein Objekt (zB ein Array, einen String oder einen Snanner). Man braucht folglich eine Refernzvariable um mit einem Objekt arbeiten zu können. Das Objekt entscheidet jeweils, welche Operationen und Methoden erlaubt sind.
Klassen können wie jeder andere Datentyp verwendet werden, um Variablen zu deklarieren.
Community lyss, zermatt;
Mit dieser Anweisung werden jedoch nur zwei Objektvariablen anglegt, die noch nirgens hinzeigen und somit den Wert null
innehaben. Nun muss also erst noch ein Objekt erzeugt/ konstruiert werden:
Community lyss = new Community();
Konstuktoren sind für die Erschaffung von Objekten zuständig und haben dazu die wichtige Aufgabe, ein Objekt nach seiner „Geburt“ in einen gültigen Zustand zu setzten. Sobald Java den new
Befehl erkennt, wird Speicherplatz für das Objekt zur Verfügung gestellt. Java sucht nun auch nach einem Konstruktor, der dann aufgerufen wird.
public class Community_2 {
private int zipcode;
private String name;
private String founding_year;
//Constructor
public Community_2(int zipcode, String name, int founding_year){
this.zipcode = zipcode;
this.name = name;
this.founding_year = founding_year;
}
}
Innerhalb vom Konstruktor werden nun Parametern den Instanzvariablen zugewiesen. Der Aufruf des Konstruktors sieht nun wie folgt aus:
public class Country {
public static void main(String[] args){
Community_2 a = new Community_2(8001, "Zürich", 1291);
}
}
Beim Aufruf des Konstruktors muss exakt die Parameter-Liste aus der Konstruktoren-Definition eingehalten werden (Anzahl der Parameter, Datentypen, Reihenfolge). Ansonsten gibt es einen Complier-Fehler.
Wenn man keinen Konstruktor definiert, wie wir bei der Community Klasse, beliefert ihn Java beim Complilieren mit dem Standardkonstruktor (Default Konstruktor). Er ist immer parameterlos und hat keinen Inhalt.
lyss = new Community();
Nun zeigt die Variable lyss
auf ein Community Objekt, dessen Felder mit den null
-Werten passend zum jeweiligen Typ (0
, 0.0
, null
) vom Standardkonstruktor initalisiert wurde.
Man kann auf die Attribute zugreifen, indem man sie durch einen Punkt vom Namen der Variable trennt. Man qualifiziert so die Feldnamen mit dem Variablennamen.
lyss.zipcode = 3250;
lyss.name = "Lyss";
lyss.founding_year = 1009;
Auch Objektvariablen können einander zugewiesen, insofern sie den gleichen Typ haben:
lyss = zermatt;
Dabei verhält sich die Zuweisung gleich wie bei Arrays. Das heisst ab jetz referenziert lyss.zipcode
sowie zermatt.zipcode
auf die gleiche Speicherzellen.
In Java gilt die Namensäquivalenz. Das heisst, das Zuweisungen zwischen Objektvariablen nur erlaubt sind, wenn die Variablen denselben Typ haben, also ihre Typen den den gleichen Namen tragen.
In anderen Sprachen gibt es die Strukturäquivalenz: Zwei Typen sind dann gleich, wenn sie dieselbe Struktur, also ihre Felder aus den selben Typen aufgebaut sind.
Bei Vergleichen mit Operatoren (<, ==, <=) finden nur Zeigervergleiche und keine Wertvergleiche statt. Es wird also geprüft ob die Variablen auf das gleiche Objekt zeigen. Wenn man die Werte vergleichen möchte, muss man eine Vergleichsfunktion selber schreiben:
static boolean isEqual (Community a, Community b) {
return a.zipcode == b.zipcode && a.name.equals(b.name) && a.founding_year == b.founding_year;
}
...
if (isEqual(a, b)) ...
Da das Attribute name
einen String ist, verwenden wir dort die .equals
-Methode.
Grafische Darstellung von Klassen: Die Angabe der Parameter bei den Methoden ist freiwillig. Mittels Pfeilen und Linien können die Beziehungen zwischen den Klassen ausgedrückt werden. Klassendiagramme sind ein Teil der Unified Modeling Language (UML).
Module sind Klassen, die über keine main
-Methode verfügen. So kann man wiederverwendbare Software in Klassen ablegen. Math
, Array
, System
sind Beispiele für Module. Da Module über keine main
-Methode verfügen, können die Klassen auch nicht direkt ausgeführt werden.
Das Verhalten der Objekte wird durch Methoden definiert.
get
beginnen zu lassen. In anderen Programmiersprachen beinhaltet bereits der Funktionsaufruf ein get
.set
beginnen zu lassen..toString
-Methode: Mit der .toString
Methode kann ein beliebiges Objekt als Zeichenkette dargestellt werden. In der Regel gibt sie eine Art des Speicherplatz aus. Die Methode wird aufgerufen, wenn ein Objekt mit einer Zeichenkette verknüpft wird.Standardmässig kann immer auf alle Attribute einer Insantz einer Instanz zugegriffen werden.
Klassen können zur Abkapselung (Encapsulation) benutzt werden. Normalerweise wird eine Variable ausserhalb einer Methode definiert. Mittels der Dot-Notation kann dann die Variable, auch welche die mit public
definiert wurde, abgerufen werden.
Möchte man allerdings verhindern, dass von ausserhalb der Klasse nicht auf ein Attribut zugegriffen wird, so kann man das mit dem Keyword private
deklarieren.
public class Name {
private type name;
}
Der Zugriff auf Attribute in Methoden der eigenen Klassen bleibt so möglich.
Mit private
lassen sich nicht nur Variablen abkapseln sondern auch Methoden:
private type name(parameters) {
...;
}
Zusätzlich existiert zudem das Keyword protected
. Grundsätzlich lässt sich protected
mit private
vergleichen mit folgendem Unterschied: Konstruktoren, Methoden und Variablen, welche mit protected
definiert wurden, stehen auch in von der deklarierenden Klasse abgeleiteten Klassen von anderen Packages zur Verfügung.
Oben ist eine grafische Übersicht über die Möglichkeit, Methoden, Attribute und Klassen zu schützen.
Shadowing beschreibt den Zustand, wenn zwei Variablen mit dem selben Namen sichtbar sind. Normalerweise ist das in Java illegal, mit einer Ausnahme: Wenn eine der Variablen ein Attribut einer Klasse ist.
Das Keyword this
verweist auf das Objekt, mit welchem die Methode aufgerufen wurde.
public class Shadowing_ex {
int a;
methods_names(int a) {
this.a = a; // sets attribute a to parameter a
}
// Call the constructor
public static void main(String[] args) {
Main myObj = new Shadowing_ex(5);
System.out.println("Value of x = " + myObj.x); //Value in toggle
}
}
//by Jan Stauffer
Lösung
Value of x = 5
Wenn das this
nicht wäre, wäre der Value 0.
static
Methoden und Variablen static
beschreibt Variablen sowie Methoden. Diese sind Teil einer Klasse, aber nicht Teil von einem Objekt. Eine static
-Variable ist unique, sie existieren nur einmal und Methoden aller Exemplare der Klasse können darauf zugreifen. Für gewöhnlich sind static
-Variablen private
oder final
. Das Keyword final
beschreib den Zustand einer Variable, wenn ihr Wert nicht mehr geändert werden kann.
Eine static
Methode kann ohne Referenz auf ein Objekt (ohne Dot Notation) aufgerufen werden.
Klassen können innerhalb einer anderen Klasse definiert werden. Es ist möglich sowohl statische (static
) sowie nicht statische Klassen innerhalb einer anderen Klasse zu definieren. Innere Klassen sind für andere Klassen nicht sichtbar, sie sind gekapselt. Instanzen einer inneren Klasse können nur innerhalb der Instanz der äusseren Klasse existieren.
private
ist, so ist sie nur in der äusseren Klasse sichtbar.static
-Schlüsselwort bedeutet, dass die innere Klasse zur äusseren Klasse gehört. Die Instanz der äusseren Klasse benötigt es jedoch nicht, um eine Instanz der inneren Klasse zu erzeugen.Methoden können in Java einen einzigen Wert mittels return
zurückgeben. Wenn man mehrere Werte zurückgeben möchte, so muss man diese in einer Klasse kombinieren. Die Methode gibt dann ein Objekt dieser Klasse zurück.
Das Konzept des Ausnahme Mechanismus (Exceptions) basiert auf drei Prinzipien:
Exception handler werden in Java mittels try
-Anweisungen umgesetzt.
try {
... // protected block
} catch (Exception e) { // exception handler
...
}
Es gibt zwei Arten von Ausnahmen Systemausnahmen (runtime exceptions) und Benutzerausnahmen (checked exceptions).
Die Systemausnahmen können überall auftreten und können behandelt werden. Die Ursache ist ein Bug im Programm
null
hat.Benutzerausnahmen signalisieren durch throw
-Anweisungen einen Fehler. Ihnen zugrunde liegt ein unwahrscheinliches, aber prinizipiell mögliches Ereignis. Sie müssen deklariert und sie müssen auch behandelt werden. Dabei wird wird ein Objekt der Ausnahmeklasse eimen passenden Ausnahmebehandler zugeworfen/zugewiesen.
throw exceptionObject;
Anschliessend werden in allen try
-Anweisungen der gerade laufenden Methode nach einem catch
gesucht, die zum Typ der auslösenden Ausnahme passt. Die laufende Anweisungsfolge wird abgebrochen und das Ausnahmeobjekt wird der passenden catch
-Klausel übergeben. Nach dem Abarbeiten setzt das Programm hinter dem try
-Befehl fort, welches zur Klausel gehört.
Manchmal möchte man Daten verknüpfen, weiss aber nicht schon im voraus, wie gross der Input ist. Da man bei Arrays die Anzahl der Elemente bei der Erstellung deklarieren muss, fällt diese Datenstruktur weg. Eine Möglichkeit ist es, wenn man Objekte in Java miteinander verlinkt.
Die LinkedList ist die einfachste verkettete Datenstruktur. Sie besteht aus Knoten und Zeigern. Die Daten hinterlegen wir in Knoten. Der Zeiger verweist auf den direkten Nachfolger des Elements oder eben des Knoten.
Das bedeutet jedoch auch, dass ein Knoten nur Kentniss über seinen Nachfolger hat, der Nachfolger hat jedoch keine Kentniss über seinen Vorgänger. Der Durchlauf funktioniert entsprechend nur in eine Richtung. Die Liste wird durch ihren ersten Knoten definiert.
public class ListNode {
int data;
ListNode next;
public ListNode (int data) {
this.data = data;
this.next = null;
}
public ListNode (int data, ListNode next) {
this.data = data;
this.next = next;
}
}
Die Suche in einer LinkedList hat die Laufzeit [latex]O(n)[/latex]. Dies, da man jedes einzelne Element nacheinander durchlaufen muss, bis das gesuchte Objekt tatsächlich gefunden wird.
for (ListNode current = first; current != null; current=current.next) {
statements;
}
Es gibt einige Methoden um mit einer LinkedList zu arbeiten:
.addFirst( );
fügt ein Element zu Beginn der Liste hinzu.removeFirst( );
entfernt das erste Element der Liste.addLast( );
fügt ein Element am Schluss der Liste hinzu.removeLast( );
entfernt das letzte Element der List.length( );
gibt die Länge der Liste zurück.get( );
gibt das Element des gegebenen Index zurückDer Stack (Stapel) ist eine Datenstruktur der das LIFO-Prinzip (last-in-first-out) zugrunde liegt. Ein Element wird am Anfang der Liste hinzugefügt und von dort auch wieder entnommen. Das Elment, welches zuletzt hinzugefügt wurde, wird auch als erstes wieder entnommen. Der Stack kann mittels einer LinkedList implementiert werden.
Folgende Methoden sollte ein Stack unterstützen:
stack.push(value);
deponiert ein Element auf dem Stapelstack.pop(value);
entfernt das Element, welches sich zuoberst befindet, vom Stapelstack.isEmpty();
prüft, ob es Werte gibtmain
-MethodeEin Paket (Package) ist eine Sammlung von Klassen, welche zusammengehören. Es besteht die Möglichkeit Subpackages zu erstellen.
Packages sind einen Behälter von mehreren Klassen, hier dargestellt in der UML-Notation.
Namen können mittels protected
so deklariert werden, dass sie nur im Package sichtbar sind. So werden Namenskonflikte vermieden und die Zgriffsrechte auf Namen lassen sich besser kontrollieren.
Die Java Bibliothek selber besteht aus Paketen. Einige häufig benötigte Pakete sind:
java.lang
: Das Paket besteht aus Standardklassen, die der Java-Compiler kennt und eigentlich Teil von Java sind. So finden sich hier die Klassen String
und StringBuilder
sowie Thread
(für parallele Prozesse), Exception
(für die Ausnahmebehandlungen) und Math
(für mathematische Funktionen).java.io
: Das Paket enthält Klassen zum Lesen und Schreiben von Datenströmen. Es sind zum Beispiel die Klassen InputStream
, OutputStream
, Reader
sowie Writer
enthalten.java.util
: Das Paket enthält Hilfsklassen wie Date
, Calendar
sowie die Collection-Klassen LinkedList
, ArrayList
, HashSet
oder HashMap
.Dateien (Klassen) welche keine Package-Deklarationen enthalten, werden dem namenslosen default Package zugeweisen. Diese können nicht zugewiesen werden.
Einzelne Klassen kann man bei ihrer Deklaration einem Paket zuordnen, indem man am Anfang der Quelldatei folgende Zeile schreibt:
package packageName;
Alle in dieser Datei deklarierten Klassen gehören nun dem angegebenen Paket an. Dem Package können problemlos noch weitere Dateien zugewiesen werden.
Ein Paket bildet auch eine Sichbarkeitsgrenze. Alles was in einem Paket zugewiesen wird, ist erstmal lokal in diesem Paket und in anderen Paketen unsichtbar. Man jedoch auch einzelne Klassen exportieren und sie anderen Paketen zu Verfügung stellen. Dadurch lassen sich gezielt Schnittstellen zwiwschen den Subsystem einrichten.
Das GUI ist die grafische Benutzeroberfläche/-schnittstelle. Gegenüber der Console bietet das GUI den Vorteil
Einfaches Fenster ohne Steuerelement/ anpassbares Layout
import gui.Window;
public class Empty {
public static void main(String[] args) {
Window window = new Window("Empty", 500, 300);
window.open();
window.waitUntilClosed();
}
}
new Window(…)
setColor(…),fill…()
open()
is…Pressed()
fill…()
refresh()
oder refreshAndClear()
open();
Fenster wird geöffnetclose();
Fenster wird geschlossenwaitUntilClosed();
Programm wartet, bis der User das Fenster schliesstisOpen();
Gibt true
zurück, falls Fenster gerade offen ist, sonst false
fillRect(double x, double y, double width, double height)
Zeichnet ein (ausgefülltes) Rechteck der Grösse width × height, welches die linke obere Ecke beim Punkt (x, y) hatfillCircle(double x, double y, double radius)
Zeichnet einen (ausgefüllten) Kreis mit Radius radius, welches den Mittelpunkt beim Punkt (x, y) hatfillOval(double x, double y, double width, double height)
drawRect(x, y, width, height)
Zeichnet die Kontur eines RechtecksdrawCircle(x, y, radius)
drawLine(x1, y1, x2, y2)
drawString(string, x, y)
Zeichnet den gegebenen String so, dass sich die Grundlinie des ersten Zeichens bei (x, y) befindetdrawImage(path, x, y)
Zeichnet das Bild, das sich in der Datei bei path
befindet, mit der linken oberen Ecke bei (x, y)drawImageCentered(path, x, y)
setColor(red, green, blue)
Setzt die Farbe für die folgenden Zeichenbefehle auf den RGB-Wert gegeben durch red, green, bluesetStrokeWidth(width)
Setzt die Strichdicke für die folgenden draw
-ZeichenbefehlesetFontSize(fontSize)
Setzt die Schriftgrösse für die folgenden drawString
-Befehlerefresh();
Zeigt die aktuelle Zeichnung im Fenster anrefresh(int waitTime);
Zeigt die aktuelle Zeichnung im Fenster an. Um ein konstantes Zeitintervall zwischen mehreren Refreshs zu erreichen, zeigt diese Methode die Änderungen erst waitTime
Millisekunden nach dem letzten Aufruf von refresh()
an.refreshAndClear(int waitTime)
Zeigt wie refresh(waitTime)
die aktuelle Zeichnung an, aber leert die Zeichnungsfläche danach für ein neues Bild.isKeyPressed(String keyName);
Gibt true zurück, falls gegebene Taste im Moment gerade gedrückt wird, false andernfallsisLeftMouseButtonPressed()
/ isRightMouseButtonPressed()
Gibt true
zurück, falls die linke/rechte Maustaste im Moment gerade gedrückt wird, false
andernfallswasKeyTyped(String keyName)
/ was...MouseButtonClicked()
Gibt true
zurück, falls die gegebene Taste gerade “getippt”, “geklickt” wurde, false andernfallsgetMouseX()
/ getMouseY()
Gibt die aktuelle X-/Y-Koordinate des Mauszeigers (Cursors) zurückDie ArrayList
Klasse ist ein Array, von welchem die Grösse verändert werden kann. Es ist in java.util
enthalten.
import java.util.ArrayList; // import the ArrayList class
ArrayList<String> cars = new ArrayList<String>(); // Create an ArrayList object
cars.add(”Volvo”);
fügt Volvo zur ArrayListe hinzucars.get(0);
gibt das erste Element zurückcars.set(0, “Opel”);
ändert das Element 0 zu Opelcars.remove(0);
entfernt das Objekt 0cars.clear();
entfernt alle Elemente aus der Listecars.size();
gibt die Länge der ArrayList zurück zurückArrayList<String> list = new ArrayList<String>(Arrays.asList(array)); // convert the Array to an Array List
String[] array = cars.toArray(new String[cars.size()]); // convert the Array List to an Array
Priority Queues werden in der Regel mit einem Heap implementiert. Die Priority Queue ist nicht threadsicher, und schliesst auch nicht aus.
Die Vererbung [Inheritence] erlaubt es uns Klassen in einer Hierarchie anzuordnen. Die Grundlage der Vererbung ist die “ist ein”-Beziehung. Man kann so eine neue Klasse auf der Basis einer existierenden Klasse erstellen. Der Zustand sowie das Verhalten der bestehenden Klasse werden übernommen. Die bereits existierende Klasse, welche die Grundlage bildet, nennt man Superclass. Die “neue”, daraus resultierende Klasse nennt man Subclass. Die Subclass enthält eine Kopie von jedem Attribut und jeder Methode der Superclass.
public class SuperClass {
...
}
public class SubClass extends SuperClass {
...
}
In der Subclass können neue Methoden definiert sowie Methoden der Superclass überschrieben und ersetzt werden. Bei der Überschreibung einer Methode ist es sinnvoll diese mit @Override
zu markieren. Der Compiler erkennt so den “Override” und teilt uns mit, falls die Methode nicht überschrieben wurde.
public class SuperClass {
public int yeyMethod() {
return 1;
}
}
public class SubClass extends SuperClass {
@Override
public int yeyMethod() {
return 2;
}
}
Wenn eine Methode überschrieben wurde, kann sie dennoch mittels der super
Referenz (super.yesMethod()
) abgerufen werden.
In Java werden Konstruktoren nicht vererbt, daher müssen in einer abgeleiteten Klasse alle erforderlichen Konstruktoren erneut definiert werden. Dabei kann mit Hilfe des Schlüsselworts super auf die Konstruktoren der Vaterklasse (Superklasse) zugegriffen und dadurch bereits vorhandener Quellcode wiederverwendet werden.
Aufgrund der Kompatibilität zwischen der Ober- und Unterklasse ist es erlaub, ein Objekt der Unterklasse einer Oberklassen Variable zuzuweisen.
SuperClass x = new SubClass();
Die Variable x
zeigt nun auf ein SubClass
-Objekt. Man kann über x
allerdings nur jene Felder ansprechen, die in der SuperClass
definiert wurden.
Mit if(x instanceof SubClass)
können wir überprüfen ob x auf ein Objekt der SubClass zeigt. In diesem Falle können wir den Typ von x nach SubClass umwandeln/ casten:
SubClass y = (SubClass) x;
Man sagt, dass Variablen vin objektorientierten Sprachen polymorph sind, also auf Objekte von verschiedenem Typ zeigen können.
Das Casten in Java bedeutet das Konvertieren eines Objekts eines Typs in ein Objekt eines anderen Typs. Es kann durchgeführt werden, indem man den Typ, in den man konvertieren möchte, in Klammern vor dem Objekt setzt. Allerdings kann es zu einer ClassCastException führen, wenn die Typen nicht kompatibel sind.
Wenn Sie in Java ein Child-Objekt einem Parent-Objekt zuweisen, wird das Child-Objekt automatisch in ein Parent-Objekt konvertiert. Dies liegt daran, dass ein Child-Objekt eine Teilmenge der Attribute und Methoden eines Parent-Objekts ist. Hier ist ein Beispiel:
class Parent { ... }
class Child extends Parent { ... }
Child child = new Child();
Parent parent = child;
In diesem Beispiel wird das Child-Objekt child
automatisch zu einem Parent-Objekt parent
konvertiert. Dies ist bekannt als Upcasting. Beachten Sie, dass Sie nicht jedes Mal, wenn Sie ein Parent-Objekt haben, auf die Methoden und Attribute eines Child-Objekts zugreifen können. Um dies zu tun, müssen Sie es erst zurück in ein Child-Objekt casten, was bekannt ist als Downcasting.
Wenn Sie versuchen, einem Child-Objekt ein Parent-Objekt zuzuweisen, müssen Sie das Parent-Objekt explicit (manuell) casten, um eine ClassCastException zu vermeiden.
Ein Interface stellt wie die Vererbbarkteit eine ist-ein Beziehung dar. Es lässt jedoch keinen gemeinsamen Code zu. Das Interface stellt vielmehr eine Art Schnittstelle dar: Als eine Art Zertifikat, das angibt, welche Funktionalität die implementierende Klasse bieten muss. Die Sichtbarkeit von Methoden in einer Schnittstelle ist immer öffentlich, die Schnittstelle selbst kann jedoch eine Standard- oder öffentliche Sichtbarkeit haben.
public interface name {
public type name(parameters); // abstract method
...
}
Eine Schnittstelle enthält so genannte abstrakte Methoden. Sie spezifizieren den Kopf, haben aber keinen Körper, d.h. keine Implementierung. Da sie keinen Code enthalten, muss die Methode von einer Klasse mittels implements
implementiert werden. Es ist möglich mehrere Interfaces zu implementieren
public class name implements interface, interface2 {
....
}
Wenn eine Klasse eine Schnittstelle implementiert, aber nicht alle in der Schnittstelle angegebenen Methoden implementiert, wird der Compiler den Code nicht übersetzen. Dies kann durch Hinzufügen des Schlüsselworts abstract
umgangen werden. Es
bedeutet, dass die Klasse noch nicht vollständig ist, d.h. dass einige Methoden noch nicht implementiert wurden.
Wenn eine Unterklasse und eine Oberklasse beide ein Attribut mit demselben Namen definieren, überschattet das Attribut der Unterklasse das Attribut der Oberklasse. Das Attribut der Oberklasse ist für jede Instanz der Unterklasse noch vorhanden, wird aber durch das Attribut der Unterklasse verdeckt. Wenn wir auf das Attribut der Oberklasse zugreifen wollen, müssen wir die Instanz auf die Superklasse Klasse.
class A {
int x;
}
class B extends A {
double x;
}
B b = new B(); // x = 0.0
((Super)b).x // x=0s
Overloading ist die Terminologie für zwei oder mehr Methoden mit dem gleichen Namen, aber unterschiedlichen Parameterlisten. Auch der Rückgabewert kann von unterschiedlichem Typ sein. Binding wählt die Methode die den gegebenen Parametern am ehesten entspricht. Ein gängiges Beispiel ist die print-Methode für System.out. Sie ist mehrfach überladen, um Instanzen beliebigen Typs ausgeben zu können.
class HelloWorld<Type>{
public Type foo(Type parameter) {
return parameter;
}
}
generisches programmieren mit extends
instanceof: Keyword, wird bei runtime ausgeführt, nicht so effizient, gibt bolean aus, der ausdrückt ob die Instanz auf
Expeptions & Errors
Exceptions (nur während der Runtime)
Errors (während Compliation/Runtime)
mit try/catch kann man Exceptions fangen
mehrere Exceptions fangen
Blackbox Testing
Aufgabe 4
dynamic binding
Short Circut
Iteratoren