Einführung in die Programmierung

Posted on July 14, 2023 (Last modified on December 7, 2023) • 39 min read • 8,307 words
Share via

einige Notizen meiner EProg Vorlesung an der ETH Zürich

Einführung in die Programmierung

Angaben

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:

EBNF

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:

  • LHS (Linke Seite): Name, zwischen < … >
  • : Symbol für „ist definiert als“
  • RHS (rechte Seite): Enthählt eine präzise Beschreibung – kann andere EBNF Regeln, Buchstaben oder Kombinationen der „control forms“ enthalten

EBNF- Beschreibungen bestehen aus 4 Elementen, die „control forms“ genannt werden:

  • Aufreihung („sequence“)
  • Entscheidung („decision“)
  • Wiederholung („repetition“)
  • Rekursion („recursion“)
  • (Auswahl)

https://etter.work/wp-content/uploads/2022/01/IMG_DCF31950B745-1-1024x284.jpeg

Grafische Darstellung von EBNF Regeln

Aufreihung („sequence“)

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.

Auswahl

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.

Optionen/ Entscheidungen („decision“)

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.

Wiederholungen („repetitions“)

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.

Rekursion („recursion“)

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

Überprüfung von EBNF Regeln

zB Definition ganzer Zahlen:

<digit>  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<integer>  [ + |  ] digit { digit }

Überprüfung der LHS:

  • Buchstaben im Symbol
  • Es darf kein Bustabe& Element im Symbol übrig bleiben

Ableitungsbäume

https://etter.work/wp-content/uploads/2022/01/IMG_BCBDE11D1C17-1-1024x403.jpeg

zum Schluss

  • Äquivalente EBNF Regeln erkennen die selben legalen Symbole.
  • EBNF beschreibt „nur“ die Syntax
  • Sonderzeichen ( {, }, [], (, ), |, <, >, ) die geschrieben werden müssen, werden in einen Rahmen platziert.

Invariante, Weakest Precondition

https://de.wikipedia.org/wiki/Wp-Kalkül

Eclipse - Entwicklungsumgebung

  • Ctrl + Shift + F: Auto Format
  • Ctrl + Shift + O : Clean up imports and imports needed libaries

Code Expert - Entwicklungsumgebung

  • F1: alle Commands

Theorie Exam

  • Vererbung von Klassen

    Fehler:

    • Compiler ckeckt Syntax, Semantik (Variable & Methode in der Klasse vorhanden?), Typfehler (sind Datentypen kompatibel?)
    • Laufzeit: Länge des Arry, Divison durch 0
    • The reference type decides which functions are/ can be called and if there exist multiple versions of that function (in sub-classes possibly) then the type of the actual object decides which one is called.
    • That is because inheritance handles attributes differently from methods. Specifically, for methods always the method is ‘searched’ from the instance and then continues up the inheritance hierarchy. However, for attributes, always the attribute of the class from which the method is chosen will be used – if available and unless not otherwise specified with super. And here, as ‘fct3()’ is not in ‘Gamma’ but in ‘Epsilon’, also the attribute from ‘Epsilon’ is used.

Einfache Java Programme

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:

  • Single-Line Comments starten mit einem //. Jeglicher Text zwischen // und dem Ende der Linie wird von Java ignoriert.
  • Multi-Line Comments starten mit /* 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.

In welcher Reihenfolge Java Opertationen auswertet

  1. Klammern (innerste zuerst)
  2. Exponentiation (von rechts nach links)
  3. Multiplikation und Division (von links nach rechts)
  4. Addition und Subtraktion (von links nach rechts)

Sonderzeichen in Java

Sonderzeichen können von Java mittels print dank dem Backslash \\ ausgegeben werden.

Methoden

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.

Deklaration von Methoden

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;
}

Namenskonventionen für Methoden

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.

Methoden mit Parametern

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:

  • formale Parameter: Das sind jene Parameter, die bei der Deklaration der Methode angegeben werden. Die Handhabung in der Methode von Parametern entspricht der der Variablen. Es sind also benannte Speicherzellen. Formale Parameter sind nur in dieser Methode bekannt und können somit gleiche Namen haben, wie eine ausserhalb der Methode deklarierte Variable. Die Speicherzelle existiert auch nur solange, wie die Methode läuft, anschliessend wird die Speicherzelle wieder freigegeben.
  • aktuelle Parameter: Das sind die jeweiligen Werten, die beim Aufruf der Methode übergeben werden.

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.

Übergabe von Werten („value semantics“) - Passed by Value

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.

Rückgabe von Werten („return values“)

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;
}

Typen und Variablen

Variablen

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

Scope

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.

Namensgebung

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örte („Keywords“)

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.

(Daten)Typen („types“)

https://etter.work/wp-content/uploads/2022/01/IMG_F51C131B2084-1-1024x502.jpeg

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

  • Boolean
  • Character
  • Integer
    • byte
    • short
    • int: Die Größe eines int-Wertes liegt bei 32 Bit. Er umfasst einen Wertebereich von dezimal -2147483648 (-2^31) bis 2147483647 (2^31 -1). Der größte vorzeichenbehaftete Ganzzahl-Datentyp nennt sich in Java long. (https://www.java-tutorial.org/datentypenundvariablen.html)
    • long
  • Floating Point
    • float
    • double

Arithmetische Operatoren

Arithmetische Operatoren verknüpfen mehrere Werte/Ausdrücke.

Binäre Operatoren

+: 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.

Unäre Operatoren

+: 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

Precedence

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:

  1. ()
  2. ``, /, %
  3. +, ``

Inkrement- und Dekrement Operatoren

Um der Wert einer Variable zu erhöhen/ zu vermindern schreibt man:

x++; x--;
++x; --x;

Boolean Operatoren

&&; logisches und
|| ||; logisches oder
!; logische Negation

Zuweisungsoperatoren

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.

Funktionen

Verzweigungen

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;

Dangling 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.

Vergleichsoperatoren

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

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 each-Schleife

for (type name: collection) {
	statements;
}

Reihenfolge:

  • HashSet - nicht definiert
  • LinkedHashSet - wie eigefügt
  • ArrayList - wie im Array gespeichert
  • LinkedList - wie in der Liste gespeichert
  • TreeSet - aufsteigend (nach compareTo)

while– Schleife

while-Schleifen führen den Loop solange aus, wie der boolsche Wert true ergibt.

while (tests) {
  statements;
}

dowhile-Schleife

Die dowhile-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);

Abbruch von Schleifen

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;
  }
}

Librariers

Dieses Kapitel deckt einige wichtige und nützliche Standartlibraries ab.

Strings

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.

Math

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ück
  • Math.ceil(value); rundet auf
  • Math.floor(value); rundet ab
  • Math.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ück
  • Math.round(value); rundet zur nächsten ganzen Zahl
  • Math.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 Bogenmass
  • Math.toDegrees(value); Math.toRadians(value); konvertiert Grad in Bogenmass oder umgekehrt
  • Math.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 werden

wichtige Methoden der Math Library, übernommen von Jan Stauffer weitere Methoden können in der Java Dokumentation nachgeschlagen werden

Output – PrintStream

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.

File input

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.

Input – Scanner

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-Zeichenkette
  • nextLine(); Scannt einen String mit der Länge einer Zeile ein

Wenn 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 gibt
  • hasNextDouble(); gibt true zurück, wenn es einen double-Wert zum lesen gibt
  • hasNext(); gibt true zurück, wenn es etwas zum lesen gibt
  • hasNextLine(); 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:

  • NoSuchElementException: Wenn man über das Ende der Eingabe hinausliest
  • InputMismatchException: Wenn man versucht einen falschen Typ zu lesen

Arrays

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];

https://etter.work/wp-content/uploads/2022/02/array-300x81.jpeg

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.

Verhalten bei Zuweisungen

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.

https://etter.work/wp-content/uploads/2022/02/Zuweisungen-Array-300x175.jpeg

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.

https://etter.work/wp-content/uploads/2022/02/2arrays-300x169.jpg

Mit der Zuweisung b = null; wird erwirkt, dass die Variable b auf kein Array zeigt. Es ergibt sich folgendes Bild:

https://etter.work/wp-content/uploads/2022/02/array-null-300x172.jpg

Vergleiche

Für Arrays funktioniert die .equals-Methode sowie der == Operator nicht. Aus diesem Grund werden folgende Methoden von der java.util Library mitgeliefert:

Referenzvariable

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).

Pass by Reference

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

Reference Semantics

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 JavaSymbol
\tTabulatorenzeichen
\nneue Zeile
\"Anführungszeichen
\\Backslash
TypBeschreibungWertebereichGrösseBeispiel
booleanLogische Wertetrue/false1 BitBoolean one = false
char0 bis 655352 Bytes (16 Bit)char letterA = ‘A’
byte-27 bis 27-11 Byte (8 Bit)byte a = 10,
byte b = -20
short-215 bis 215-12 Bytes (16 Bit)short s = 10000,
short r = -5000
intganze Zahlen-231 bis 231-14 Bytes (32 Bit)int a = 100000,
int b = -200000
longgrosse ganze Zahlen-263 bis 263-18 Bytes (64 Bit)long a = 100000L,
long b = -200000L
float+/- 1,4*10-45 bis +/- 3,4384 Bytes (32 Bit)float f1 = 234.5f
doubleReelle Zahlen+/-4,9^-324 bis +/-1,73088 Bytes (64 Bit)double d1 = 12.3
KurzformLangform
+=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;
OperatorBedeutung
==gleich
!=ungleich
>grösser
<kleiner
>=grösser oder gleich
<=kleiner oder gleich
Name der MethodeBeschreibung
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 WertTyp
0int
0.0double
falseboolean
nullnon-primitive types
Name der MethodeBeschreibung
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.

Klassen und Objekte in Java

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:

  • Namenloser Dienst: Die Klasse wird geladen, und anschliessend wird automatisch die main Methode ausgeführt. Der Klassenname entspiercht hierbei dem Namen des Dienstes. Dieser Dienst ist sinnvoll für einen Aufruf im Betriebssystem, der IDE oder Shell.
  • mit Namen ausgewählter Dienst (in der Bibliothek): Der Dienst hat einen Damen. Um ihn aufzurufen ist die 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;
}

Objektorientiertes Programmieren (OOP)

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.

Deklaration von Klassen

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;
    ...
    }
};

Verwendung von Klassen

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;

https://etter.work/wp-content/uploads/2022/02/var-obj-neu-300x175.jpg

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();

Konstruktor

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.

Attribute

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;

https://etter.work/wp-content/uploads/2022/02/Zuweisung-Klasse-300x177.jpg

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.

Vergleiche

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.

https://etter.work/wp-content/uploads/2022/02/IMG_E704E8BB4E51-1-300x243.jpeg

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

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.

Verhalten von Objekten

Das Verhalten der Objekte wird durch Methoden definiert.

  • Clients: Programme, die in der Lage sind, Instanzen und Klassen zu erstellen sowie darauf zuzugreifen.
  • Methoden: Methoden einer Klasse definieren das Verhalten. Das Verhalten umfasst Zustandsänderungen
  • getter und setter: Zugriffsmethoden, die einzlene Eigenschaften (Attribute) eines Objekts abfragen und ändern können. Sie sind Teil der öffentlichen Schnittstelle eines Objekts und verstecken die Implementierungsdetails von diesem Objekt.
    • getter: Abfragemethoden – in einigen Programmiersprachen ist es Konvention, diese Methoden mit get beginnen zu lassen. In anderen Programmiersprachen beinhaltet bereits der Funktionsaufruf ein get.
    • setter: Änderungsmethode – kann vor der Änderung den Wert auf Gültigkeit prüfen und die Änderung Beobachtern mitteilen. In einigen Programmiersprachen ist es Konvention, diese Methoden mit 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.

Sichtbarkeit von Attributen

Standardmässig kann immer auf alle Attribute einer Insantz einer Instanz zugegriffen werden.

Encapsulation

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.

https://etter.work/wp-content/uploads/2022/02/IMG_7564-1024x637.jpg

Oben ist eine grafische Übersicht über die Möglichkeit, Methoden, Attribute und Klassen zu schützen.

Shadowing

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.

Verschachtelung von Klassen

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.

  • Wenn die innere Klasse private ist, so ist sie nur in der äusseren Klasse sichtbar.
  • Das 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.

Methode mit mehreren Rückgabewerten

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.

Exceptions und Errors

Exception

Das Konzept des Ausnahme Mechanismus (Exceptions) basiert auf drei Prinzipien:

  • geschützer Block: Der Block wird gegen das Auftreten von Fehlern geschützt. Wenn in einer vom Block aufgerufenen Methode ein Fehler auftritt, so wird sichergestellt, dass dieser behandelt wrid.
  • Ausnahmebehandler (Exception handler): Ein geschützter Block verfügt über einen/ oder mehrere Exception handler, welche(r) mögliche Fehler abfangen und behandeln. Ein Exception handler ist eine Anweisungsfolge , in der auf den Fehler reagiert wird.
  • Ausnahme (Exception): Ein Fehler wird durch das Auslösen einer Ausnahme kommuniziert. Sobald ein Exception auftritt, wird automatisch ein passender Exception handler gesucht und angesprungen. Im Anschluss wird das Programm vom geschützten Block fortgesetzt.

https://etter.work/wp-content/uploads/2022/02/IMG_577FD95818B2-1-1024x385.jpeg

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

  • ArithmeticException: eine ganze Zahl durch 0 divideren
  • NullPointerException: Es wird auf ein Objekt über einen Zeiger zugegriffen, der den Wert null hat.
  • ArrayIndexOutOfBoundsException: Es wird auf einen Index, der ausserhalb der Spannweite des Arrays liegt, zugegriffen.

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.

Verkettete Datenstrukturen

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.

Verkettete Liste (Linked List)

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.

Implementation einer LinkedList

https://etter.work/wp-content/uploads/2022/02/IMG_FF3D53BF0A80-1-1024x329.jpeg

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;
  }
}

Suche in einer LinkedList

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ück

Stack

Der 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 Stapel
  • stack.pop(value); entfernt das Element, welches sich zuoberst befindet, vom Stapel
  • stack.isEmpty(); prüft, ob es Werte gibt

Regeln, Hinweise und Konvektionen für verständliche Programme

  • Der Code sollte möglichst kompakt sein: keine Wiederholungen.
  • Die Regeln sollten einfach und klar formuliert sein.
  • Eine Zeile sollte nicht mehr als 80 bis 100 Zeichen beinhalten.
  • Eine Java-Datei baut sich in dieser Reihenfolge auf:
  1. Import
  2. (Haupt-) Klasse
  3. main-Methode
  4. andere Methoden
  • Namen bestehen nur aus Buchstaben und Ziffern
  • Klassennamen enthalten GrossBuchstaben und beginnen auch mit solchen.
  • Methodennamen bestehen, wenn möglich aus einem Verb und beginnen mit einem Kleinbuchstaben.
  • Variablennamen beschreiben ihre Variable und enthalten keine Typ oder Metainformationen. Masseinheiten sollten, sofern diese relevant sind, auch im Namen erwähnt werden.
  • Kurze Variablennamen sind reserviert für Loopcounter (zB i, j oder t).

Aufgaben durch Rekursion lösen

Packages in Java

Ein Paket (Package) ist eine Sammlung von Klassen, welche zusammengehören. Es besteht die Möglichkeit Subpackages zu erstellen.

https://etter.work/wp-content/uploads/2022/02/IMG_EC9625FA48E8-1-300x196.jpeg

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.

https://etter.work/wp-content/uploads/2022/02/IMG_7564-1024x637.jpg

Java Bibliothek

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.

Default Package

Dateien (Klassen) welche keine Package-Deklarationen enthalten, werden dem namenslosen default Package zugeweisen. Diese können nicht zugewiesen werden.

Erstellung von eigenen Packages

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.

GUI (Graphical User Interface)

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();
	}
}
  1. Erstellen eines Fensters: new Window(…)
  2. Inhalt ausgeben, d.h. zeichnen: setColor(…),fill…()
  3. Fenster sichtbar machen: open()
  4. Animieren/Interagieren
    1. Benutereingabe abfragen: is…Pressed()
    2. Zeichnen: fill…()
    3. Aktuelle Zeichnung veröffentlichen: refresh() oder refreshAndClear()

Window-Methoden

  • open(); Fenster wird geöffnet
  • close(); Fenster wird geschlossen
  • waitUntilClosed(); Programm wartet, bis der User das Fenster schliesst
  • isOpen(); Gibt true zurück, falls Fenster gerade offen ist, sonst false

Einfache Zeichen-Methoden

  • 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) hat
  • fillCircle(double x, double y, double radius) Zeichnet einen (ausgefüllten) Kreis mit Radius radius, welches den Mittelpunkt beim Punkt (x, y) hat
  • fillOval(double x, double y, double width, double height)
  • drawRect(x, y, width, height) Zeichnet die Kontur eines Rechtecks
  • drawCircle(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) befindet
  • drawImage(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, blue
  • setStrokeWidth(width) Setzt die Strichdicke für die folgenden draw-Zeichenbefehle
  • setFontSize(fontSize) Setzt die Schriftgrösse für die folgenden drawString-Befehle

Veröffentlichen von Änderungen

  • refresh(); Zeigt die aktuelle Zeichnung im Fenster an
  • refresh(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.

Methoden für Eingabe

  • isKeyPressed(String keyName); Gibt true zurück, falls gegebene Taste im Moment gerade gedrückt wird, false andernfalls
  • isLeftMouseButtonPressed() / isRightMouseButtonPressed() Gibt true zurück, falls die linke/rechte Maustaste im Moment gerade gedrückt wird, false andernfalls
  • wasKeyTyped(String keyName) / was...MouseButtonClicked()Gibt true zurück, falls die gegebene Taste gerade “getippt”, “geklickt” wurde, false andernfalls
  • getMouseX() / getMouseY() Gibt die aktuelle X-/Y-Koordinate des Mauszeigers (Cursors) zurück

ArrayList

Die 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 hinzu
  • cars.get(0); gibt das erste Element zurück
  • cars.set(0, “Opel”); ändert das Element 0 zu Opel
  • cars.remove(0); entfernt das Objekt 0
  • cars.clear(); entfernt alle Elemente aus der Liste
  • cars.size(); gibt die Länge der ArrayList zurück zurück
ArrayList<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

HashSet

  • HashSet: Lookup in konstanter Zeit
    • add(value)
    • contains(value)
    • remove
    • clear
    • isEmpty
    • toString
  • LinkedHashSet: benutzt als Backend ein “HashSet”, behält allerdings die Reihenfolge bei
  • HashMap
  • TreeMap
  • TreeSet
  • gibt keine Duplikate
  • Comparable Interface: vergleiche mich zu einem anderen
  • Comparator: vergleiche zwei Weerte

Abbildung (”Map”)

  • Abbildung

Priority Queue

Priority Queues werden in der Regel mit einem Heap implementiert. Die Priority Queue ist nicht threadsicher, und schliesst auch nicht aus.

Vererbbarkeit [Inheritence]

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 {
	...
}

Vererbbarkeit von Methoden

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.

Vererbarkeiten von Konstruktoren

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.

Zuweisung zwischen Objekten

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.

Casten

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.

Upcasten/Downcasten

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.

Interfaces

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.

Polymorphismus

Sichtbarkeit

  • public: überall sichtbar
  • private: nur sichtbar in der Klasse
  • protected: sichtbar im Package, der Klasse sowie deren Subclassen (auch der Subclassen in anderen Packages)
  • default(no keyword): nur im selben Package

Shadowing

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

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.

  • generisches programmieren: klassen die für einen beliebigen Objekttyp funktioniert
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)

    • ArithmeticException
    • NullPointerException
    • IndexOutOfBoundsException
    • FileNotFoundException
  • Errors (während Compliation/Runtime)

  • mit try/catch kann man Exceptions fangen

  • mehrere Exceptions fangen

  • Blackbox Testing

    • null übergeben
    • Max./ Min.value
  • Aufgabe 4

  • dynamic binding

  • Short Circut

Iteratoren

  • hasNext()
  • next()
  • remove()
On this page
I am pleased to hear from you.

You can reach me via social networks or by mail.