Java: Unterschied zwischen den Versionen

Aus m-wiki
Zur Navigation springen Zur Suche springen
 
(10 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 448: Zeile 448:
 
System.out.println( s.replaceAll( " +", " " ) );  // -> Ersetzt mehrmaliges Vorkommen von Leerzeichen durch ein einmaliges an allen Fundorten.
 
System.out.println( s.replaceAll( " +", " " ) );  // -> Ersetzt mehrmaliges Vorkommen von Leerzeichen durch ein einmaliges an allen Fundorten.
 
System.out.println( s.replaceFirst( " +", " " ) ); // -> Ersetzt mehrmaliges Vorkommen von Leerzeichen durch ein einmaliges, doch bricht die Suche nach dem ersten Finden ab.
 
System.out.println( s.replaceFirst( " +", " " ) ); // -> Ersetzt mehrmaliges Vorkommen von Leerzeichen durch ein einmaliges, doch bricht die Suche nach dem ersten Finden ab.
 +
</pre>
 +
 +
==== split() String aufteilen ====
 +
Der übergebene String wird in ein Array abgefüllt. Als Trennzeichen werden reguläre Ausdrücke übergeben.
 +
<pre>
 +
String test = "Das ist ein Test.";
 +
String[] segs = test.split("\s"); // Das Trennzeichen sind Leerräume
 +
System.out.println( Arrays.toString(segs) ); // Das, ist, ein, Test. -> Wichtig ist hier, dass Satzzeichen verbleiben, da sie ja keine Leerräume beinhalten
 +
</pre>
 +
 +
<pre>
 +
String string = "Hört es euch an, denn das ist mein Gedudel!";
 +
int nrOfWords = string.split( "(\\s|\\p{Punct})+" ).length; // Hier wird als Option Leerraum oder Satzzeichen übergeben.
 +
System.out.println( nrOfWords );      // 9
 
</pre>
 
</pre>
  
Zeile 884: Zeile 898:
 
! Modifikator !! Sichtbarkeit !! Lebensdauer !! Bemerkungen
 
! Modifikator !! Sichtbarkeit !! Lebensdauer !! Bemerkungen
 
|-
 
|-
| public || Von überall her (auch externe Klassen). Wird eine Klasse privat markiert, so kann man auch nicht mehr auf interne öffentliche Variablen zugreifen -> wirklich? || - || -
+
| public ([https://en.wikipedia.org/wiki/Unified_Modeling_Language UML]:&nbsp;+) || Von überall her (auch externe Klassen).<br />Wird eine Klasse als privat markiert, so kann man auch nicht mehr auf interne öffentliche Variablen zugreifen. || - ||
 +
<pre>
 +
private class x { public int i; } // end x
 +
private class y {
 +
  x x = new x();
 +
  x.i = 5; // Kompilierfehler
 +
} // end y
 +
</pre>
 +
Aus Gründen der Kapselung sollten möglichst keine Variablen public sein und nur über Methoden angesprochen werden.
 
|-
 
|-
| protected || eigene Klasse und davon abgeleitete Klassen (Alle Klassen des Pakets) || - || -
+
| protected (UML:&nbsp;#) || eigene Klasse und davon abgeleitete Klassen (Alle Klassen des Pakets) || - || -
 
|-
 
|-
| private || nur in der eigenen Klasse || - || -
+
| private (UML:&nbsp;-) || nur in der eigenen Klasse || - || Möglichst explizit für alle Variablen und interne Methoden verwenden, denn aus Grund der Datenkapselung sollte möglichst viel als <code>private</code> deklariert werden.
 
|-
 
|-
| package scoped  || Innerhalb des Pakets sichtbar || - || Ist theoretisch -> wird intern angewendet, wenn man keinen Modifikator verwendet.
+
| package scoped (UML:&nbsp;~) || Innerhalb des Pakets sichtbar || - || Ist theoretisch -> wird intern angewendet, wenn man keinen Modifikator verwendet.
 
|-
 
|-
 
| static || - || Solange ein Objekt der Klasse existiert. || Das Objekt (Variable / Methode) wird über die Klasse (klasse.methode) und nicht über die Instanz (instanz.methode) angesprochen. Dies bedeutet, dass solche Objekte auch nicht abgeleitet werden können, da sie klassenbezogen sind. Dies bedeutet, dass man entsprechend nicht zuerst ein Objekt der Klasse erzeugen muss, um auf seine Methoden zugreifen zu können. Die main-Methode ist etwa so definiert.
 
| static || - || Solange ein Objekt der Klasse existiert. || Das Objekt (Variable / Methode) wird über die Klasse (klasse.methode) und nicht über die Instanz (instanz.methode) angesprochen. Dies bedeutet, dass solche Objekte auch nicht abgeleitet werden können, da sie klassenbezogen sind. Dies bedeutet, dass man entsprechend nicht zuerst ein Objekt der Klasse erzeugen muss, um auf seine Methoden zugreifen zu können. Die main-Methode ist etwa so definiert.
Zeile 971: Zeile 993:
 
</pre>
 
</pre>
 
Die Zeile mit dem this.i verweist auf das äussere i, welches sonst vom internen i unsichtbar gemacht worden wäre.
 
Die Zeile mit dem this.i verweist auf das äussere i, welches sonst vom internen i unsichtbar gemacht worden wäre.
 +
 +
==== getter (get()) und setter (set(x)) ====
 +
In Java sollte man [[Java#Variablen|Variablen]] nicht direkt modifizieren, sondern dies immer definiert über [[Java#Methoden (Funktionen)|Methoden]] erledigen. Methoden, welche nur den Wert einer Variablen zurückliefern oder diese nur setzen, werden entsprechend getter und setter genannt. Bei get() bekommt man als Rückgabe einfach den Inhalt der Variable zurück, während bei set kein Rückgabewert verwendet wird.<br />
 +
Ich selber würde aber auch bei einem set(x) min. einen Boolean zurückgeben, damit man weiss, dass die Zuweisung erfolgreich war. Falls nicht (Wert nicht im erlaubten Bereich), könnte man dann entsprechend reagieren. Sonst muss man nicht nur die Fehlerbehandlung sondern auch die Ausgabe in der Methode erledigen. Sonst kann man einfach den Fehlerstatus setzen und den Fehlercode in einer speziellen Fehlervariable des Objektes ablegen, welche man anschliessend zur Ausgabe an eine andere Methode übergeben kann.
 +
<pre>
 +
public String getName() { return name; } // end getName()
 +
 +
public void setName(String name) { // Standard
 +
  if ( name != null && !name.trim().isEmpty()) { this.name = name; } // end if
 +
} // end setName()
 +
 +
public boolean setName(String name) { // Mit Statusrückgabe
 +
  if ( name != null && !name.trim().isEmpty()) {
 +
    this.name = name;
 +
    return true;
 +
  } // end if
 +
  this.errorText = "setName Aufruf schlug fehl"; // der Fehler sollte natürlich genau eingegrenzt werden (null, leer, zu lang usw.)
 +
  this.errorCode = 94; // die Fehlernummer sollte intern natürlich aufgelöst werden können
 +
  return false;
 +
} // end setName()
 +
</pre>
 +
Interne Variablen sind natürlich über die entsprechenden [[Java#Modifikatoren/Modifizierer (public, static usw.)|Modifikatoren]] zu kapseln.
  
 
=== Methoden (Funktionen) ===
 
=== Methoden (Funktionen) ===
Zeile 1.055: Zeile 1.099:
 
* <code>print();</code> -> Ausgabe des enthaltenen Arguments ohne Zeilenumbruch.
 
* <code>print();</code> -> Ausgabe des enthaltenen Arguments ohne Zeilenumbruch.
 
* <code>println();</code> -> Ausgabe des enthaltenen Arguments mit anschliessendem Zeilenumbruch.
 
* <code>println();</code> -> Ausgabe des enthaltenen Arguments mit anschliessendem Zeilenumbruch.
* <code>printf();</code> -> Ausgabe des enthaltenen Arguments mit Formatierungen.<br />%n ergibt einen Zeilenumbruch, %d steht für eine ganze Zahl, %f ist für eine Fliesskommazahl und %s steht für eine Zeichenkette<br /><code>printf(%d Prozent ist nicht ganz %s.%n", 99, "Hundert" );</code>
+
* <code>printf();</code> -> Ausgabe des enthaltenen Arguments mit Formatierungen.<br /><code>%n</code> ergibt einen Zeilenumbruch, <code>%d</code> steht für eine ganze Zahl, <code>%f</code> ist für eine Fliesskommazahl und <code>%s</code> steht für eine Zeichenkette.<code>%b</code> steht für Boolean, <code>%t</code> für Datum und Zeit, während <code>%e</code> für die wissenschaftliche Schreibweise einer Zahl steht. Dazu wird mit <code>%%</code> ein Prozentzeichen ausgegeben, während man mit <code>%x</code> hexadezimale Zahlen ausgibt und <code>%c</code> explizit Unicode-Zeichen verarbeitet.<br />
 +
<code>printf(%d Prozent ist nicht ganz %s.%n", 99, "Hundert" );</code>
 
* Der Fehlerkanal wird mit <code>System.err.printXXX()</code> angesprochen. Dies ist ein anderes Objekt und ermöglicht es, die Fehlerausgabe umzuleiten (etwa in eine Logdatei).
 
* Der Fehlerkanal wird mit <code>System.err.printXXX()</code> angesprochen. Dies ist ein anderes Objekt und ermöglicht es, die Fehlerausgabe umzuleiten (etwa in eine Logdatei).
 
* Bei diesen Methoden kann man Strings und "einfache Datentypen" kombinieren mit dem + Zeichen: <code>print("Ausgabe: " + wert);</code>
 
* Bei diesen Methoden kann man Strings und "einfache Datentypen" kombinieren mit dem + Zeichen: <code>print("Ausgabe: " + wert);</code>
 +
Für die Formatierung kann man die Stellenanzahl (auch mit führenden Nullen <code>%05d</code>) und bei Fliesskommazahlen auch die Nachkommastellen angeben <code>%010.2f</code>.<br />
 +
Beim Parameter <code>%t</code> muss entsprechend angegeben werden, welche Werte ausgegeben werden sollen. Die mögliche Liste ist sehr lang (<code>%tA</code> -> Wochentag oder <code>%tc</code> komplettes Datum mit Zeit in der Form %ta %tb %td %tT %tZ %tY).
  
 
===== random() Zufallszahl generieren =====
 
===== random() Zufallszahl generieren =====
Zeile 1.065: Zeile 1.112:
  
 
===== Scanner() Werte (von der Konsole) einlesen =====
 
===== Scanner() Werte (von der Konsole) einlesen =====
Der Methode aus der Klasse <code>java.util</code> muss noch gesagt werden, von wo man Daten einlesen will und auch der zu erwartende Datentyp muss mitgegeben werden, da Java keine impliziten [[Java#Datentypen umwandeln|casts]] vornimmt.
+
Der Methode aus der Klasse <code>java.util</code> muss noch gesagt werden, von wo man Daten einlesen will und auch der zu erwartende Datentyp muss mitgegeben werden, da Java keine impliziten [[Java#Datentypen umwandeln|Casts]] vornimmt. Entsprechend kann bei fehlerhafter Eingabe ein <code>InputMismatchException</code> geworfen werden, den man entsprechend abfangen muss.<br />
 +
Man kann für die Eingabe auch die lokale Spracheinstellung explizit setzen, auch wenn Java sonst einfach standardmässig die vom Betriebssystem nimmt <code>useLocale(Locale.German(Switzerland));</code>.
 +
====== Konsole ======
 
* String: <code>String name = new java.util.Scanner(System.in).nextLine();</code>
 
* String: <code>String name = new java.util.Scanner(System.in).nextLine();</code>
 
* Int: <code>int age = new java.util.Scanner(System.in).nextInt();</code>
 
* Int: <code>int age = new java.util.Scanner(System.in).nextInt();</code>
 
* Double: <code>double value = new java.util.Scanner(System.in).nextDouble();</code>
 
* Double: <code>double value = new java.util.Scanner(System.in).nextDouble();</code>
 +
====== Datei ======
 +
<pre>
 +
import java.io.*;
 +
import java.util.Scanner;
 +
public class ReadAllLines {
 +
  public static void main( String[] args ) throws FileNotFoundException { // Da es einen Fehler geben kann, sollte entsprechend das catch implementiert werden.
 +
    Scanner scanner = new Scanner(new File("Datei.txt"));
 +
    while(scanner.hasNextLine()) {
 +
      System.out.println(scanner.nextLine());
 +
    } // end while
 +
    scanner.close(); // Speicher freigeben
 +
  } // end main
 +
} // end class
 +
</pre>
  
 
===== toString() In einen String umwandeln =====
 
===== toString() In einen String umwandeln =====
Zeile 1.191: Zeile 1.254:
  
 
== Reguläre Ausdrücke ==
 
== Reguläre Ausdrücke ==
 +
Siehe dazu den Artikel [[Suche mit regulären Ausdrücken (regex)|Reguläre Ausdrücke]]. Hier werden nur Java spezifische Sachen behandelt. So ist etwa das Ganze im Paket <code>java.util.regex</code> zu finden.<br />
 +
Neben den "normalen" Aufzählungen wie <code>.*?</code> gibt es noch die Aufzählung <code>X(?!Y)</code> welche bedeutet, dass Y nicht auf X folgen darf, damit der Ausdruck true ist.<br />
 +
Im Gegensatz zu [[PHP]] verwendet Java die [[Suche mit regulären Ausdrücken (regex)#Modifikatoren|Modifikatoren]] als Flags in Klammern. So bedeutet <code>(?i)</code>, dass ab diesem Ausdruck insensitive verglichen wird:<code>System.out.println("wauWau".matches("(?i)wauwau"));  // true</code>
 +
 +
=== Vergleich ===
 +
Für einen Vergleich verwendet man entweder die Methode <code>java.util.regex.Pattern.matches()</code> oder bei Strings die Objektmethode <code>matches()</code>.
 +
<pre>
 +
Pattern.matches( "'.*'", "'Hallo Welt'" ) // true -> Hochkomma + beliebiger Inhalt + Hochkomma
 +
</pre>
 +
 +
=== Suche ===
 +
<pre>
 +
String s = "'Demnach, welcher verheiratet, der tut wohl; welcher aber nicht verheiratet, der tut besser.' 1. Korinther 7, 38";
 +
Matcher matcher = Pattern.compile( "\\d+" ).matcher( s ); // sucht Zahlen
 +
while ( matcher.find() ) // true, wenn etwas gefunden wird, das dem Suchkriterium entspricht
 +
  System.out.printf( "%s an Position [%d,%d]%n",
 +
                    matcher.group(), // Gibt das Suchresultat zuück (in diesem Fall die Zahlen 1, 7, 38)
 +
                    matcher.start(), matcher.end() ); // Start- und Endposition im String (Die Endposition ist immer eins höher. Bei der Position von der 7 wird 107,108 zurückgegeben.)
 +
</pre>
  
 
== Links ==
 
== Links ==

Aktuelle Version vom 2. August 2020, 19:28 Uhr

Inhaltsverzeichnis

Einführung

Tipps zu Büchern unten bei den Links

Kommentare

  • // Kommentar -> Einzeiliger Kommentar
  • /* Kommentar */ -> Mehrzeiliger Kommentar
  • /** Kommentar */ -> Mehrzeiliger Kommentar für Dokumentation

Dateiaufbau und Bibliotheken/Packages

  • Nur eine Klasse darf pro Datei öffentlich sein und diese muss den Namen der Datei haben: public class application {} -> application.java (Klassennamen werden normalerweise klein geschrieben, doch dies ist nur eine Konvention.)
  • Nach der Kompilierung mit javac wird der Dateiname ohne class übergeben -> java application
  • Der Einstiegspunkt in jedes Programm ist immer: public static void main(String[] args) (Der Parameter args kann frei gewählt werden, hat sich aber als Standard durchgesetzt).
  • Die Java-Bibliotheken enthalten Packages mit Klassen, Schnittstellen (Interfaces) und Ausnahmebehandlungen (Exceptions)
  • Externe Bibliotheken werden über die Pfadangabe eingebunden: java.util.Date d = new java.util.Date();
    • Um alle Klassen eines Paketes / einer Bibliothek einzufügen (und damit Schreibarbeit zu sparen) wird der import-Befehl verwendet: import java.util.*;
    • Will man nur eine einzelne Klasse eines Paketes importieren (Overhead beim kompilieren reduzieren): import java.util.Date;
    • Enthalten 2 Pakete den gleichen Klassennamen java.util.Date java.sql.Date, so muss trotz des Imports der volle Paketnamen angegeben werden, damit der Compiler weiss, welche Klasse gemeint ist.
  • Packages kann man selber erstellen auch wieder mit der Angabe des Pfades ab dem Java-Verzeichnis: package Paketname;
    • Die Angabe package muss immer zuoberst in der Datei stehen (noch vor dem Import), sonst gibt es einen Fehler
    • Wie bei normalen Klassen darf nur eine Klasse öffentlich sein, welche dann den Dateinamen gibt: \Tool\Network\MyClass.java

Syntax

Sonderzeichen

Zeichen Bedeutung
\b Backspace
\t horizontaler Tabulator
\n Zeilenumbruch
\f Seitenumbruch
\r Wagenrücklauf
" doppeltes Anführungszeichen
' einfaches Anführungszeichen
\ Backslash
\nnn oktaler Code
\uxxxx hexadezimaler Code

System.out.print("ProgrammersBase.NET \\u0021");

Datentypen

Typ Byte Wertebereich Standardwert
boolean 1 true, false false
byte 1 - 27 bis 27 0
short 2 - 215 bis 215 0
int 4 - 231 bis 231 0
long 8 - 263 bis 263 0
float 4 - 3.40282347*1038 bis 3.40282347*1038 0.0
double 8 - 1.79769313486231570*10308 bis 1.79769313486231570*10308 0.0
char 2 alle Unicode - Zeichen (Werden in einfache Hochkommas gesetzt: char x = 'd';) \n0000
java.math.BigDecimal ? grosse Zahlen ?

Diese Datentypen können mit sogenannten Wrapper-Klassen bei Bedarf auch als Objekte verwendet werden, damit man nur noch Objekte hat, wie etwa in Smalltalk, doch da sich dadurch nur die Geschwindigkeit negativ ändert, ist mir der Sinn schleierhaft und daher wird das Ganze hier auch nicht weiter behandelt.

Die Datentypen Byte, Integer, Long, Short, Character, Double und Float besitzen die Konstanten MAX_VALUE und MIN_VALUE, die den grössten und kleinsten zulässigen Wert des Datentyps bezeichnen. So kann man etwa Tests für Konvertierungen durchführen um zu schauen, ob es zu Werteverlusten kommt.
if(x > Float.MAX_VALUE ) { print("Konvertierungsfehler"); }

Um grosse Zahlen oder Zahlenblöcke besser lesbar zu machen, darf man ab Java 7 als Hilsmittel den Unterstrich einsetzen: 86_400_000 oder 0b01101001_01001101_11100101_01011110

Datentypen umwandeln

Kleine DT werden (bei Bedarf) automatisch umgewandelt, während man beim umgekehrten Weg einen expliziten Cast anwenden muss, da dabei Werte verloren gehen können. Die Zuweisung geschieht mit: (gewünschter Datentyp) Variable

int i = 65;
char c = (char) i; // -> Nun Buchstabe A

Wichtig: Boolean kann nicht direkt umgewandelt werden in einen Integerwert. Hier muss man eine Abfrage treffen und dann den gewünschten Wert direkt an eine Variable vergeben:

boolean a = true;
int b = 0;
if(a) { b = 1; }

Da gewisse kleine Datentypen wie Byte und Short automatisch in einen Integer gewandelt werden, muss man einen expliziten Cast durchführen, wenn man Short haben möchte:

short a = 1;
short b = 2;
short c = (short) (a + b) // ohne expliziten cast gibt es einen Compiler-Fehler

Allerdings ist dies nur auf Embedded-Systemen sinnvoll, da die Platzersparnis auf "normalen" Rechnern den Aufwand nicht lohnt.
Bei der Konvertierung von Zahlen in einen String kann man die Methode toString() verwenden:

int a = 2;
System.out.println(a.toString());

Variablen

Da der Typ fix ist, muss man bei Java im Gegensatz zu c(++) nichts weiter beachten. Bei der Deklaration kann/sollte man der Variablen auch einen Anfangswert mitgeben, doch dies ist prinzipiell nicht notwendig. Der Name kann theoretisch aus allen Unicode-Zeichen bestehen, doch ich empfehle, beim ASCII-Zeichensatz zu bleiben. Bei zusammengesetzten Namen sollte zur besseren Lesbarkeit jeder weitere Wortteil gross geschrieben werden (isBigHuman).
Die Definition geschieht daher nach folgendem Schema: Typ Name (= Wert);.
Die Zuweisungen kann man zusammenfassen, doch ich empfehle, dies nicht zu tun, denn dann kann man mit einem kleine Kommentar hinter der Variable deren Zweck notieren.

int alter;
int autos = 2; // Anzahl Autos pro Haushalt
boolean isHuman = true; // Handelt es sich um einen Menschen?
float einkommen, ausgaben;
double steuern = 0.0, zinsen = 1.5, anzahlPersonen;

lokale Variablen

Nur innerhalb des definierten Blocks (Schleife) oder der Methode gültig. Wenn nicht über den Rückgabewert zurückgegeben, wird ihr Inhalt beim Verlassen verworfen.

konstante Variablen (Konstanten)

Kann den Wert nach der Definition nicht mehr ändern. Etwa für Pi oder ähnliches verwendet, doch kein Muss.

final Typ Name = Wert;
final float pi = 3.14;

Will man die Variable nur einmal pro Klasse brauchen, kann man sie auch auslagern und statisch verwenden.

statische Variablen

Gleicher Gültigkeitsbereich wie lokale Variablen, doch sie behalten ihren Wert auch über das Ende der Methode hinaus und können beim nochmaligen betreten weiter verwendet werden (Aufrufe zählen (Instanzen usw.)). Das heisst, die Variable bleibt bis zum Programmende oder dem expliziten "zerstören" aller Klasseninstanzen persistent im Speicher.

class Test {  
  static public int i = 2; 
} // end class Test

public class MyClass {
  public static void main(String[] args) {
    System.out.print(Test.i);      
  }
} // end class MyClass

Wie man sieht, muss die statische Variable in eine externe Klasse ausgelagert werden, damit das Ganze auch funktionieren kann. Damit man die Variable auch sieht, muss sie öffentlich (public) definiert sein. Sonst kann man eine Methode erstellen und den Wert entsprechend zurückgeben lassen.

Klassenvariablen

Alle benutzerdefinierten Variablen, welche jedoch nicht direkt erzeugt werden oder an eine Instanz gebunden sind. -> Genauer beschreiben

Instanzvariablen

Alle von einer Klasse instantiierten und an ein Objekt gebundenen Variablen -> Noch etwas genauer schreiben

Arrays

Unterscheiden sich optisch durch die Klammern, doch es sind andere Typen, da Arrays aus Klassen gebaut werden.

Arrays definieren und Werte zuweisen

int[][] array2 = new int[10][20];
array2[3][17] = 127;
int[] prim  = {2, 3, 5, 7, 11};

Man könnte die Klammern bei der Definition auch hinten beim Namen schreiben, doch da die Übergabe eines Arrays an eine Methode auch so geschieht, machen wir es einheitlich. Wie man sieht, sind auch mehrdimensionale Arrays möglich. Man kann auch eigene Arrays definieren (es sind ja Klassen) und diese dann jeweils als Elemente behandeln und darauf zugreifen.

Zugriff auf Elemente im Array

wert = array[0][3];

Man sieht, dass die Zählung bei Null beginnt. Hier hat man beim zweidimensionalen Array das 4. Element der ersten "Reihe" gewählt.

arraycopy() Arrays kopieren

Da Arrays Klassen sind, kann man diese nicht einfach mit array2 = array1; kopieren. Sonst zeigen die Zeiger nämlich weiterhin auf array1. Man verwendet daher folgende Methode:

public class MyClass {
  public static void main(String[] args) {
    int[] array1 = {0,1,2,3,4};
    int[]  copy = {0,0,0,0,0};

    System.arraycopy(array1,2,copy,1,3); // -> Beginne im array array1 an der Position 3 (Index beginnt bei 0) und kopiere dann ins array copy. 
                                         //    Beginne dort an Position 2 und schreibe 3 kopierte Werte rein.

    for(int i=0; i<copy.length; i++) {
      System.out.println(copy[i]);
    } // end for
  } // end main
} // end MyClass

Es gibt auch die Methode clone(). Dies ist aber mit Vorsicht zu geniessen. Hier wird zwar bei 1-dimensionalen Arrays eine Kopie erstellt, doch bei mehrdimensionalen Feldern zeigen die Unterfelder immer noch auf die Original-Objekte. Dies ist in den meisten Fällen nicht wünschenswert und daher sollte dies nicht verwendet werden.

binarySearch() Wert in Arrays suchen

Die binäre Suche ist nur dann korrekt, wenn das Array vorher sortiert wurde. Sonst kann es fehlerhafte Resultate geben.

import java.util.*;

public class MyClass {
  public static void main(String[] args) {
    int[] array = {1,3,5,8};

    System.out.println(Arrays.binarySearch(array,5));
    System.out.println(Arrays.binarySearch(array,6));
  } // end main
} // end MyClass

Wird nach einem nicht vorhandenen Eintrag gesucht, so wird als Ergebnis der mögliche Einfügungsindex zurückgegeben und nicht die gefundene Indexposition.

equals() Arrays vergleichen

import java.util.*;

public class MyClass {
  public static void main(String[] args) {
    int[] array1 = {1,2,3,4};
    int[] array2 = {1,2,3,4};
    int[] array3 = {4,3,2,1};
    int[] array4 = {1,2    };

    System.out.println(Arrays.equals(array1,array1));
    System.out.println(Arrays.equals(array1,array2)); 
    System.out.println(Arrays.equals(array2,array3));
    System.out.println(Arrays.equals(array1,array4));
  } // end main
} // end MyClass

Hier wird zuerst zwei mal true geliefert und dann zwei mal false, da die Arrays auch mit den Elementen übereinstimmen müssen und nicht nur in der Anzahl. Dies bedeutet, dass Arrays vor dem Vergleichen sortiert sein müssen, falls dies gewünscht ist.

fill() Array mit bestimmtem Wert füllen

Hier gibt es zwei Möglichkeiten. Alle Elemente:

Arrays.fill(array,2); // füllt das Array mit dem Wert 2

Bestimmter Bereich:

Arrays.fill(array,2,4,1); // füllt das Array von Index 2 bis Index 4 mit dem Wert 1

length() Länge des Arrays ermitteln

x = array.length;

Wird vor allem in Schleifen zum befüllen verwendet:

for(int i=0;i<array.length;i++) {
  System.out.print(array[i]);
} // end for

Damit die Berechnung nicht jedes Mal durchgeführt wird, sollte die Anweisung vor der Schlaufe an eine Hilfsvariable übergeben werden, damit man in der Schlaufe nur noch darauf prüfen kann.

sort() Array sortieren

Hier gibt es wieder zwei Möglichkeiten. Alle Elemente:

Arrays.sort(array); // sortiert das komplette Array

Bestimmter Bereich:

Arrays.sort(array,2,4); // sortiert das Array von Index 2 bis Index 4 aufsteigend

Arrays an eine Methode übergeben

public static void print(int[] a) {
    for(int i=0;i<a.length;i++) {
      System.out.print(a[i]);
    } // end for
} // end print

Wie man sieht, wird über den Typ angezeigt, dass es ein Array ist.
Da Arrays als Referenz übergeben werden, modifiziert man hier im Gegensatz zu Variablen automatisch das Original.

Array zurückgeben

Hier wird einfach der Rückgabewert void angepasst auf das Array und schon ist es bereit:

public static int[] init() {
  int[] tmp = new int[10];
  for(int i=0;i<tmp.length;i++) {
    tmp[i] = i;
  } // end for
  return tmp;
} // end init

Array durchlaufen und Werte zurückgeben

Ist eine Variante von for. In php als for each umgesetzt.

static double avg( double[] array ) {
  if ( array == null || array.length == 0 ) { throw new IllegalArgumentException( "Array null oder leer" ); }
  double sum = 0;
  for ( double n : array ) { sum += n; } // Die Werte werden der Hilfsvariable n übergeben
  return sum / array.length;
} // end avg()

Strings

Definition

  • Wurden als Klasse implementiert.
  • Wichtigste Einschränkung: Aus Performancegründen gilt die Klasse als konstant und erzeugte Strings können daher nicht mehr angepasst werden.
  • Strings sind Unicode und brauchen daher keine Zeichensätze.

Die Erzeugung kann wie eine Variable oder über den Konstruktor der Klasse erfolgen. Man kann aber auch ein Array oder einen vorhandenen String übergeben (Kopie).

public class MyClass {
  public static void main(String[] args) {
    char[] array = {'S','t','r','i','n','g',' ','3'};

    String string1 = "String 1";
    String string2 = new String("String 2");  
    String string3 = new String(array,0,7); 
    String string4 = new String(string1);
  } // end main
} // end MyClass

+ oder concat() Strings verketten

Durch das verknüpfen mit + kann man Strings verknüpfen. Man kann dabei auch Zahlen mitgeben:

public class MyClass {
  public static void main(String[] args) {
    String string1 = "String 1";
    String string2 = new String("String 2");   
    String string3 = string1 + string2 + "Test " + "5";
  } // end main
} // end MyClass

Hier muss man aufpassen, dass man nicht ungewollt falsch zusammenzählt, da reine Zahlen je nach Reihenfolge oder Klammernangabe ausgerchnet werden, bevor sie mit dem Rest verkettet werden. Daher möglichst keine Berechnungen in der Verkettung ("x" + (3 + 4) + "y" -> x7y) und Buchstaben/Zahlen nur in doppelten Hochkommas, da einfache Hochkommas als Char gewertet werden und dann diese miteinander verrechnet werden und so keine Verkettung geschieht('A'(65) + 'a'(97) -> ó(162)).

Auf Codebasis schneller und effizienter wäre aber das verwenden von concat:

    String string1 = "String 1";
    String string2 = new String("String 2");   
    String string3 = string1.concat(string2);

charAt() Zeichen an einer gewissen Position ermitteln

Das Zeichen an der gewünschten Position wird ausgegeben. Das erste Zeichen ist wie beim Array an Position 0.

public class MyClass {
  public static void main(String[] args) {
    String string1 = "String 1";
    System.out.println(string1.charAt(3)); // ergibt 'i'
  } // end main
} // end MyClass

compareTo() contains, equals(), startsWith() Strings auf Inhalte prüfen

Je nach Bedarf kann man folgende Vergleiche machen:

Befehl Bedeutung Beispiel
compareTo Führt einen lexikalischen Vergleich durch. Dabei werden die Zeichen verglichen sowie die Stringlänge. Endet ein String oder ist ein Zeichen kleiner beziehungsweise größer, so wird ein numerischer Ergebniswert geliefert. Bei Gleichheit gibt es 0.
Die Methode ist wichtig bei einer alphabetischen Sortierung. Dabei muss man bedenken, dass landesspezifische Sortierungen nicht berücksichtigt werden, wie etwa deutsche Umlaute. Dazu muss man Collator-Klassen nutzen, welche dafür ausgelegt sind.
String string1 =  "CPP.ProgrammersBase.NET";
String string2 = "JAVA.ProgrammersBase.NET";
    
int i = string1.compareTo(string2);
 
if(i<0) { 
  System.out.println("Der 1. String ist lexikalisch kleiner"); // kleiner oder Zeichen früher im Alphabet 
} // end if
else if(i>0) { 
  System.out.println("Der 1. String ist lexikalisch grösser"); // grösser oder Zeichen später im Alphabet 
} // end else if
else { 
  System.out.println("Die Strings sind lexikalisch gleich"); // identisch 
} // end else
compareToIgnoreCase Wie oben, aber es wird nur die Länge/Buchstaben verglichen, da die Gross-/Kleinschreibung ignoriert wird.
contains Testst, ob der angegebene Teilstring im übergebenen String enthalten ist (true oder false)
Es wird entsprechend auf Gross- Kleinschreibung geachtet.
System.out.println("Hallo zusammen, ich besitze Viagra!".contains("Viagra") ); // true
endsWith Der String muss auf die mitgegebene Sequenz enden
String string = "JAVA.ProgrammersBase.NET";
String search = "NET";

System.out.println(string.endsWith(search)); // -> true
equals Exakter Zeichen- und Längenvergleich.
Wichtig ist dabei, dass es zu einer NullPointerException kommen kann, wenn der vordere der beiden Strings null ist. Wenn einer der beiden Strings definiert ist, so sollte dieser in den vorderen Teil genommen werden, da in diesem Fall keine Ausnahme generiert wird, wenn der zweite Teil null ist.
String string1 = "TEST";
String string2 = "Test";
String string3;

System.out.println(string1.equals(string2)); // -> false
System.out.println(string1.equalsIgnoreCase(string2)); // true

System.out.println(string1.equals(string3)); // false ohne Ausnahmefehler
System.out.println(string3.equals(string1)); // false mit Abbruch durch Ausnahmefehler
equalsIgnoreCase Gross- Kleinschreibung wird ignoriert
regionMatches Prüft, ob zwei Sequenzen im String übereinstimmen. Bei Bedarf auch mit Gross- Kleinschreibung. Dies, wenn man als ersten Parameter ein true mitgibt.
String string1 = "JAVA.ProgrammersBase.NET";
String search =  "CPP.ProgrammersBase.NET";
   
System.out.println(string1.regionMatches(16, search, 15, 4));
System.out.println(string1.regionMatches(true, 16, search, 15, 4));

Sucht im string1 ab Position 16 und schaut, ob im übergebenen String (search) an Position 15 die nächsten 4 Zeichen übereinstimmen

startsWith Der String muss ab der gewünschten Stelle mit der mitgegebenen Sequenz übereinstimmen
String string = "JAVA.ProgrammersBase.NET";
String search = "NET";

System.out.println(string.startsWith(search)); // -> false
System.out.println(string.startsWith(search, 21)); // -> true (Suche beginnt wie immer mit 0)

indexOf() Position eines Zeichens in String finden

Diese Funktionen liefern die Position des ersten oder letzten Vorkommens des gesuchten Zeichens.

String string = "Such mich";
String search = "mi";

System.out.println(string.indexOf(search)); // liefert 5

Man kann auch eine definierte Startposition mitgeben und so mittels einer Schleife alle Vorkommnisse zählen, indem man die gefundene Position als neue Startposition mitgibt.
Will man das letzte Vorkommen finden, verwendet man lastIndexOf.
Wird der Wert nicht gefunden, so liefert die Funktion -1 zurück.

length() String Länge ermitteln

Wird wie bei Arrays gemacht:

public class MyClass {
  public static void main(String[] args) {
    String string1 = "String 1";
    System.out.println(string1.length());
  } // end main
} // end MyClass

parseXXX() String in Zahl konvertieren

Ist das Gegenteil von valueOf und steht für jeden Typ gesondert zur Verfügung, so dass es parseInt(), parseDouble(), usw. gibt.
Da man bei Benutzereingaben mit Fehlern rechnen muss, muss immer mit NumberFormatException Fehlern rechnen und diese entsprechend abfangen. Dazu gehören bei int auch Leerzeichen, da diese im Gegensatz zu etwa double nicht von der Bibliothek abgefangen werden (ja das ist inkonsequent). Entsprechend muss die Abfrage als try-catch formuliert werden.

String s = " 1234    "; // Hier müsste natürlich ein .trim() angehängt werden, doch der String könnte auch andere Zeichen enthalten
try {
  int i = Integer.parseInt(s);
} // end try
catch(NumberFormatException error) {
  System.out.printf("String konnte nicht in einen Integer konvertiert werden! %s", error.toString());
} // end catch

replace() Zeichen ersetzen

Da Strings konstant sind, wird der entsprechende String kopiert und das angepasste Resultat zurückgeliefert. Es gibt verschiedene Varianten, je nachdem, ob man nur einzelne Zeichen oder Zeichenbereiche ersetzen will und auch, ob man mit einem regulären Ausdruck arbeiten möchte.

String string = "Hallo";
System.out.println(string.replace('a','e')); // -> Hello

String s = "Schnecken  erschrecken, wenn  Schnecken an Schnecken schlecken,    weil zum Schrecken  vieler Schnecken Schnecken   nicht schmecken.";
System.out.println( s.replace("Schnecke", "Katze") );

System.out.println( s.replaceAll( " +", " " ) );   // -> Ersetzt mehrmaliges Vorkommen von Leerzeichen durch ein einmaliges an allen Fundorten.
System.out.println( s.replaceFirst( " +", " " ) ); // -> Ersetzt mehrmaliges Vorkommen von Leerzeichen durch ein einmaliges, doch bricht die Suche nach dem ersten Finden ab.

split() String aufteilen

Der übergebene String wird in ein Array abgefüllt. Als Trennzeichen werden reguläre Ausdrücke übergeben.

String test = "Das ist ein Test.";
String[] segs = test.split("\s"); // Das Trennzeichen sind Leerräume
System.out.println( Arrays.toString(segs) ); // Das, ist, ein, Test. -> Wichtig ist hier, dass Satzzeichen verbleiben, da sie ja keine Leerräume beinhalten
String string = "Hört es euch an, denn das ist mein Gedudel!";
int nrOfWords = string.split( "(\\s|\\p{Punct})+" ).length; // Hier wird als Option Leerraum oder Satzzeichen übergeben.
System.out.println( nrOfWords );       // 9

substring() Teilstring extrahieren

Will man mehrere Zeichen extrahieren, verwendet man substring. Die Parameter werden immer vom Anfang aus gerechnet, wobei das Zeichen an der Endeposition nicht mehr mit ausgegeben wird.

public class MyClass {
  public static void main(String[] args) {
    String string1 = "String";
    System.out.println(string1.substring(1,3)); // Teil in der Mitte ausschneiden ergibt 'tr'
    System.out.println(string1.substring(2)); // Ende ausschneiden ergibt 'ring'. Da bis zum Ende übernommen wird, muss nur die Startposition angegeben werden.
  } // end main
} // end MyClass

toLowerCase(), toUpperCase() Umwandlung in Gross- oder Kleinbuchstaben

Mit toLowerCase oder toUpperCase wird der komplette String angepasst.

String string = "klein";
System.out.println(string.toUpperCase()); // Liefert KLEIN

Je nach lokalen Begebenheiten kann dies zu einer inkorrekten Verarbeitung führen. Hier sollte dann die locale berücksichtigt werden:
System.out.println("TITANIK".toLowerCase(new Locale("tr"))); // tıtanık -> im türkischen ohne Punkte auf dem i
Ohne Angabe wird einfach die Einstellung des Betriebssystems übernommen.

trim() Leerzeichen eliminieren

Wenn man nicht sicher ist, ob der String noch (unsichtbare) Leerzeichen (vorher oder nachher) enthält, kann man diese vor einer weitern Verarbeitung automatisch entfernen:
s.trim();

Will man nachher noch testen, ob der String nun leer ist, kann dies folgendermassen kombiniert werden:
boolean isBlank = s.trim().isEmpty();

valueOf() Wert in String konvertieren

Auch wenn man primitive Datentypen explizit mit String s1 = String.valueOf(10); erzeugen kann, findet die Typumwandlung auch ohne explizite Typumwandlung statt, so dass man dies bei der Initialisierung auch weglassen kann.String s1 = "10";
Muss man allerdings im Code eine Variable in einen Sting konvertieren und nicht einfach nur als Parameter bei print mitgeben, dann braucht man die Angabe, da ein + den Wert einfach hinten an den String anhängt.

String s = "10";
int a = 10;
s.valueOf(a);
System.out.println(s);

Allerdings kann diese Konvertierung auch bei beliebigen Objekten gemacht werden. String r = String.valueOf( new java.awt.Point() ); // java.awt.Point[x=0,y=0]. Intern wird dabei einfach die Methode toString() aufgerufen.

Stringbuffer (variable Strings)

Strings sind ja per Definition konstant. Mit einem Stringbuffer kann man aber variable Strings erzeugen. Das bedeutet, dass man diese danach manipulieren kann. Das Ganze geht auch über die vorhandenen Methoden mit Strings, doch die Stringbuffer sind komfortabler. Um das Ganze zu realisieren, wird eine bestimmte Grösse verwendet, welche mit der capacity Methode abgefragt werden kann. Diese Grösse ist aber nur auf Embedded-Systemen mit wenig Speicher relevant. Man kann die neue Buffergrösse manuell mit ensureCapacity(Grösse) setzen.
Es gibt auch noch die StringBuilder Klasse, welche ein wenig schneller ist, dafür bei Threads nicht synchron. Aus diesem Grund lohnt sich deren Einsatz nur in Spezialfällen. Die Verwendung ist aber äquivalent zu Stringbuffer.

Befehl Bedeutung Beispiel
append Zeichen am Ende anfügen
StringBuffer string = new StringBuffer("Test");

string.append("zusatz");
System.out.println(string); // -> Testzusatz
contentEquals Buffer mit String vergleichen
StringBuffer string = new StringBuffer("Test");
String s = "Test";

System.out.println(s.contentEquals(string) ); // true
delete Löscht eine Buchstabensequenz
StringBuffer string = new StringBuffer("Teststring");
    
string.delete(4, 6); // An Position 4 beginnen und 6 Zeichen entfernen
System.out.println(string); // -> Test
deleteCharAt entfernt das Zeichen an der mitgegebenen Position
StringBuffer string = new StringBuffer("Testi");
    
string.deleteCharAt(4);
System.out.println(string); // -> Test
getChars Kopiert einen Bereich in ein Array.
char[] a = new char[20];
StringBuffer b = new StringBuffer("Teststring");
b.getChars(4, 9, a, 0); // Kopiert von Position 4 bis Position 9 ins Array a ab Position 0.
    
System.out.println(a); // -> string
insert Hier kann die Einfügeposition selber bestimmt werden
StringBuffer string =  new StringBuffer("Der String");
    
string.insert(3, "neue ");
System.out.println(string); // -> Der neue String
replace Ersetzt eine bestimmte Sequenz mit der Angegebenen.
StringBuffer string = new StringBuffer("Test");
    
string.replace(3, 1, "aurus"); // Entfernt 1 Zeichen an Position 3 und fügt die mitgegebene Sequenz ein
System.out.println(string); // -> Tesaurus
reverse Dreht den String um
StringBuffer string = new StringBuffer("Test");
    
string.reverse();
System.out.println(string); // -> tseT
setCharAt Ersetzt ein Zeichen an der gegebenen Position
StringBuffer string = new StringBuffer("Tast");
    
string.setCharAt(1, "e");
System.out.println(string); // -> Test
substring Kopiert eine angegebene Sequenz
StringBuffer string = new StringBuffer("Teststring");
 
System.out.println(string.substring(5)); // -> string -> Ohne zweiten Parameter, wird bis zum Ende kopiert, sonst nur bis zur zweiten Position

Bedingungen und Schlaufen

if then else

if(Bedingung 1) {
  // Anweisungen
} // end if
else if (Bedingung 2) {
  // Anweisungen
} // end else if
else {
  // Anweisungen
} // end else

Die else if Anweisung kann beliebig oft erfolgen. Hier ist aber ein switch meist besser. Das schlussendliche else wird nur aufgerufen, wenn keine Vorherbedingung erfüllt wurde.

switch

switch(Parameter) {
  case Wert1:  // Anweisungen
  break;
  case Wert2:  // Anweisungen
  break;
  default:     // Anweisungen
} // end switch

Der Parameter muss konstant sein. Ab Java 7 kann man neben Integer auch Aufzählungen (enum) und Strings/Char verwenden.
Es sind keine Wertebereiche (7-15) möglich. Alle Anwesiungen nach dem case werden ausgeführt bis zum break. Auf diese Weise kann man "primitiv" Bereiche festlegen: case 1: case 2: case 3: print"1-3"; break;
Default wird nur ausgeführt, wenn kein anderer Wert zutraf.

while

while(Bedingung) {
  // Anweisungen
} // end while
  • Solange die Bedingung stimmt, wird durchlaufen
  • Stimmt die Bedingung schon am Anfang nicht, wird die Schleife übersprungen -> Will man dies nicht, muss man do-while verwenden
  • Hat man kein oder ein falsches Abbruchkriterium, ergibt sich eine Endlosschleife

for

for(Typ Variable = Wert; Bedingung; Update) {
  // Anweisungen
} // end for
  • for(int i=0; i<10; i++)

Kann auch durch eine while-Schleife gebaut werden, ist aber einfacher zu lesen.

Break und Continue und Sprungmarken

  • break; Abbruch der Schleife
  • continue; Wiederholung der Schleife

continue springt sofort nach der Anweisung wieder zum Kopf und beginnt mit einem neuen Durchlauf. So kann man bei ungültigen Werten gleich den nächsten Wert einlesen.
Beim break kann man eine undefinierte Schleife beim Ende der Daten definiert verlassen.
Marken werden verwendet, wenn die obigen Befehle nicht eindeutig sind. Etwa bei geschachtelten Schleifen. In diesem Fall wird die Sprungmarke an der Stelle gesetzt, an die man beim Befehl springen möchte.

test1:
while (true) {
  test2:
  while (true) {
    if (a < b) { break test1; }
    else { break test2; }
  } // end while
} // end while

Operatoren und Operationen

Bei mehreren Operationen in einer Anweisung, werden die Anweisungen entsprechend ihrer Priorität abgearbeitet. Diese Reihenfolge entspricht den mathematischen Regeln. Bei Unsicherheit sollten Klammern eingesetzt werden, da diese dafür sorgen, dass die Reihenfolge entsprechend eingehalten wird.

Mathematik

  • + Führt eine Addition durch.
  • - Führt eine Subtraktion durch.
  • * Führt eine Multiplikation durch.
  • / Führt eine Division durch.
  •  % Führt eine Division durch, wobei nur der Restwert geliefert wird.
  • == Gleichheit (Achtung, Klassen müssen entsprechend ihrer Methoden verglichen werden und dazu gehören auch Arrays)
  •  != Ungleichheit (Siehe Hinweis bei Gleichheit)

inc und dec

  • ++ Erhöht den Wert um 1
  • -- Erniedrigt um 1

Je nach Stellung wird zuerst erhöht und dann berechnet oder umgekehrt. Am einfachsten ist der Prefix, doch je nach Verwendung ist auch der Postfix notwendig:

  • i = j++; // Postfix -> zuerst bekommt i den Wert von j und danach wird j um 1 erhöht.
  • i = ++j; // Prefix -> zuerst wird j um 1 erhöht und danach i zugewiesen.

Logische Vergleiche

  •  ! NICHT // Liefert immer das Gegenteil
  • && UND mit "Short Circuit Evaluation"
  • & UND ohne "Short Circuit Evaluation"
  • || ODER mit "Short Circuit Evaluation"
  • | ODER ohne "Short Circuit Evaluation"
  • ^ ODER (exklusiv) // Liefert nur dann TRUE, wenn die Werte unterschiedlich sind (TRUE+FALSE oder FALSE+TRUE)
Short Circuit Evaluation

Verwendet eine Operation dieses Verfahren, so wird der weiter rechts stehende Ausdruck nur dann ausgewertet, wenn er für das Gesamtergebnis noch von Bedeutung ist. Dadurch kann man viele Vergleiche abkürzen.

Bitvergleiche

Auswertung wie oben. Die Vergleiche werden auf die jeweiligen Bits angewendet und nicht auf den gesamten Ausdruck

  • ~ Komplement // Binäres Komplement bilden: 110 -> 001
  • & UND // 110 + 101 -> 100
  • | ODER // 110 + 101 -> 111
  • ^ ODER (exklusiv) 110 + 101 -> 011

Bits schieben

Die Bits der übergebenen Variable werden um die gewünschte Anzahl verschoben und die füllenden Positionen werden mit 0 gefüllt. Je nach Wahl kann das Vorzeichen belassen werden (pos- / neg.).

  • << \ Linksverschiebung
  • >> \ Rechtsverschiebung mit Vorzeichen
  • >>> \ Rechtsverschiebung ohne Vorzeichen

Spezialabfragen

  •  ?: wird als Kurzform eines if then else verwendet. Variable?Truewert:Falsewert; // Lieber sauber mit if then else arbeiten.
  • instanceof ermittelt, ob der übergebene Wert eine Instanz der angeforderten Klasse oder einer Subklasse davon ist
  • . (Punkt) wird verwendet, um auf Subklassen und Methoden zuzugreifen (Siehe Kapitel Klassen)
  • + kann auch Strings miteinander verknüpfen

Objektorientierung

Theorie

Das Ziel ist, alle Implementierungsdetails zu kapseln und nur über definierte Schnittstellen (Methoden) mit dem Objekt (Klasse) zu kommunizieren. Wird eine Klasse von einer anderen abgeleitet, so nennt man dies entweder Superklasse -> Subklasse oder Elternklasse -> Kindklasse. Je weiter nach "unten", umso spezifischer wird die Implementierung, während nach oben generalisiert (verallgemeinert) wird. Eine Klasse stellt den Bauplan dar, während schliesslich ein Objekt durch eine Instantiierung aus diesem Bauplan erstellt wird.
Bevor man sich eine neue Klasse erstellt, sollte man sich die folgenden Fragen stellen (und beantworten), da sich daraus automatisch die Referenz der zu bildenden Klasse ergibt:

  • Welche Fähigkeiten hat das Objekt? -> Methoden
  • Welche Eigenschaften hat das Objekt? -> Variablen
  • Welche Initialisierungen müssen vorgenommen werden? -> Konstruktor(en)
  • Welche Daten sollen dem Benutzer zugänglich sein? -> Variablen und Methoden intern oder extern zugänglich definieren
  • (Ist das Objekt Teil einer Vererbungshierarchie?) -> Nur notwendig, wenn mehrere Klassen mit Subklassen geplant werden (Aufteilung)
  • (Ist das Objekt von anderen Klassen abhängig?) -> Bibliotheken einbinden

Schlagwörter

  • Von Aggregation spricht man, wenn ein Objekt andere aufnehmen kann (Behälter), ohne von ihnen abhängig zu sein. -> is a (a ist b zugeordnet)
  • Bei der Komposition stellt man das Objekt aus verschiedenen Objekten zusammen und ist somit von den Subobjekten abhängig. -> part of (a ist Teil von b)

Vererbung

In Java kann immer nur von einer Klasse abgeleitet werden, wobei man selber bestimmen kann, welche Eigenschaften man übernimmt und welche man anpasst. Möchte man hingegen Eigenschaften und Methoden von anderen Klassen integrieren, so verwendet man eine Schnittstelle (Interface). Das sind (leere) Methoden einer anderen Klasse, welche dann benutzerspezifisch implementiert werden. Alle Klassen werden in Java von Object abgeleitet.

Klassen

Klasse definieren

[public] class Test1 {
  // Anweisungen
} // end Test1

Es darf pro Datei nur eine öffentliche Klasse geben und diese muss dem Namen der Datei entsprechen. Hier wäre dies Test1.class. Alle anderen Klassen in der Datei sind automatisch intern, falls noch weitere Klassen definiert werden. Will man eine weitere öffentliche Klasse, so hat man diese in einer eigenen Datei zu speichern.

Klasse verwenden

Die oberhalb definierte Klasse wird folgendermassen eingebunden:

Klasse Anwendung1;
Anwendung1 = new Test1();

oder in Kurzform

Klasse Object = new Klasseninstanz();
Test1 object = new Test1();

In einem konkreten Beispiel:

class MyClass {
  // Anweisungen
} // end MyClass
      
public class MyTest {
  public static void main(String[] args) {
    MyClass object = new MyClass(); // Diesen Befehl könnte man wie oben vorgestellt auch aufteilen ins Objekt erstellen und Objekt instantiieren.
  } // end main
} // end MyTest

innere und anonyme Klassen

Werden verwendet, um eingebundene äussere Klassen kurzfristig für eigene Zwecke zu überschreiben. Sind nur innerhalb der Definition sicht- und verwendbar.

Superklassen und Subklassen (Klassen ableiten)

Die Superklasse ist die Elternklasse oder Basisklasse, von der abgeleitet wird. Die neue Klasse ist die Kind- oder Subklasse:

class MySuperClass{
}

class MySubClass extends MySuperClass{
}

Die Subklasse erbt alle Variablen und Methoden der Superklasse mit Ausnahme der Objekte, welche als final gekennzeichnet wurden. Diese können in der neuen Klasse nicht mehr verändert werden. Zusätzlich werden Konstruktoren und Destruktoren nicht vererbt, da diese klassenspezifisch sind. Genauso statische Methoden, da diese an die jeweilige Klasse gebunden sind und private Variablen, da die der Klasse "gehören". Im Gegensatz zu c++ kann man nur immer von einer Klasse ableiten. Alles andere wird über die Schnittstellen "importiert".

Konstruktoren in abgeleiteten Klassen

Im Konstruktor muss der jeweilige Elternkonstruktor aufgerufen werden (super.Konstruktor();), falls man im Konstruktor Variablen "oberhalb" definieren will (hier die Variable i):

class MySuperClass {
  public MySuperClass(int i) {
    this.i = i;
  } // end Konstruktor

  public int i;
} // end MySuperClass

class MySubClass extends MySuperClass {
  public MySubClass(int i,String s) {
    super(i);
    this.s = s;
  } // end Konstruktor

  public String s;
} // end MySubClass

public class MyClass {
  public static void main(String[] args) {
    MySubClass object = new MySubClass(0,"ProgrammersBase.NET");

    System.out.println(object.i);
    System.out.println(object.s);
  } // end main
} // end MyClass
abgeleitete Methoden

Damit abgeleitete Methoden das Verhalten der Basisklasse überschreiben, muss die Originalmethode komplett überschrieben werden (gleiche Parameter und Anzahl). Wählt man nun eine Methode, so wird immer die "tiefste" Stufe genommen, in der eine übereinstimmende Methode gefunden wird. Die direkte Auswahl mittels Punkt ist nur bei der direkten Elternklasse erlaubt und zwar mittels super.methode(). Ohne wird einfach hierarchisch nach oben weiter gesucht, bis es eine entsprechende Methode gibt. Falls keine gefunden wird, kommt es zu einem Fehler.

instanceof (Instanz einer Klasse)

Diese spezielle Methode wird verwendet, um zu prüfen, ob ein Objekt einer bestimmten (abgeleiteten) Klasse oder Schnittstelle entspricht.

System.out.print(object instanceof MyClass);

Damit kann man sicherstellen, dass das gewählte Objekt bestimmten Voraussetzungen entspricht, die man im anschliessenden Code voraussetzt (Methoden, Variablen usw.).

Schnittstellen (Interfaces)

Die Methoden und Variablen müssen öffentlich sein und auch initialisiert. Sie gelten als konstant.

interface MyInterface {
  public int i = 0;
  public void print();
} // end MyInterface

Hier ein Beispiel:

interface MyInterface {
  public String s = "ProgrammersBase.NET";
  public void print();
} // end MyInterface

class MyClass implements MyInterface {
  public void print() {
    System.out.print(s);
  } // end print
} // end MyClass

public class MyTest {
  public static void main(String[] args) {
    MyClass object = new MyClass();
    object.print();
  } // end main
} // end MyTest

Einmal implementierte Schnittstellen werden auch an Subklassen weitergegeben. Das heisst, Subklassen können diese verwenden, ohne diese noch einmal implementieren zu müssen. Bei Bedarf darf man diese aber einfach überschreiben und somit neu definieren. Man kann auch mehrere Schnittstellen implementieren. Diese werden jeweils mit Komma getrennt angegeben. Bis jetzt habe ich den Sinn dieser Schnittstellen noch nicht verstanden, da die Methoden ja abstrakt sind und somit jeweils programmiert werden müssen.

Konstruktoren

Konstruktoren werden erst aufgerufen, nachdem die internen Variablen initialisiert sind. Dies ermöglicht es, die Variablendefinition im Code erst anschliessend zu machen, ohne dass es zu einem Fehler kommt. Je nach Vererbungsstatus müssen im Konstruktor noch Variablen der vererbenden Superklassen initialisiert werden.

Aufbau

Dies sind spezielle Methoden, welche beim erstellen eines Objekts aufgerufen werden, um Werte zu initialisieren. Der Konstruktor ist optional und trägt immer den Namen der Klasse:

class MyClass {
  public MyClass() {
    i = 10;
  } // end Konstruktor
  private int i;
} // end Klasse

Parameterübergabe

Da der Konstruktor eine normale Methode ist, können an ihn entsprechende Parameter übergeben werden:

class MyClass {
  public MyClass(int a, int b) {
    i = a * b;
  } // end Konstruktor
  private int i;
} // end MyClass

public class MyTest {
  public static void main(String[] args) {
    MyClass object = new MyClass(2, 5);
  } // end main
} // end MyTest

Der Konstruktor kann auch überladen werden, wobei sich entweder die Parameteranzahl oder die Datentypen unterscheiden müssen.

Statische Konstruktoren

Dienen zur Initialisierung der Klassenvariablen. Können keine Parameter bekommen, da der Aufruf zur Laufzeit erfolgt.

class MyClass {
  public static   int i;
  public static short s;
  static {
    i = 10;
    s = 20;
  } // end Konstruktor
} // end MyClass

Modifikatoren/Modifizierer (public, static usw.)

Java kennt folgende Modifikatoren für Klassen, Methoden, Variablen usw. Diese regeln die Sichtbarkeit und die Lebensdauer.

Modifikator Sichtbarkeit Lebensdauer Bemerkungen
public (UML: +) Von überall her (auch externe Klassen).
Wird eine Klasse als privat markiert, so kann man auch nicht mehr auf interne öffentliche Variablen zugreifen.
-
private class x { public int i; } // end x
private class y { 
  x x = new x();
  x.i = 5; // Kompilierfehler
} // end y

Aus Gründen der Kapselung sollten möglichst keine Variablen public sein und nur über Methoden angesprochen werden.

protected (UML: #) eigene Klasse und davon abgeleitete Klassen (Alle Klassen des Pakets) - -
private (UML: -) nur in der eigenen Klasse - Möglichst explizit für alle Variablen und interne Methoden verwenden, denn aus Grund der Datenkapselung sollte möglichst viel als private deklariert werden.
package scoped (UML: ~) Innerhalb des Pakets sichtbar - Ist theoretisch -> wird intern angewendet, wenn man keinen Modifikator verwendet.
static - Solange ein Objekt der Klasse existiert. Das Objekt (Variable / Methode) wird über die Klasse (klasse.methode) und nicht über die Instanz (instanz.methode) angesprochen. Dies bedeutet, dass solche Objekte auch nicht abgeleitet werden können, da sie klassenbezogen sind. Dies bedeutet, dass man entsprechend nicht zuerst ein Objekt der Klasse erzeugen muss, um auf seine Methoden zugreifen zu können. Die main-Methode ist etwa so definiert.
final - - Fixiert den Wert. Variablen können nicht mehr verändert werden und Klassen/Methoden nicht abgeleitet. Damit erreicht man, dass etwa Konstanten nicht fälschlicherweise einen neuen Wert erhalten oder dass definierte Methoden sich plötzlich anders verhalten.
abstract - - Die Methode / Klasse muss in einer abgeleiteten Klasse konkret implementiert werden. In der als abstract definierten Methode oder Klasse werden die Methoden nicht auscodiert. Es werden nur Anweisungen geschrieben, mit welchen Parametern usw. die Methoden schlussendlich arbeiten sollen.
Dies wird etwa bei Schnittstellen verwendet, da diese Methoden dann in anderen Klassen verwendet werden können.
native - - Sind in einer anderen Sprache wie etwa c++ geschrieben.
synchronized - - Wird fürs Multithreading verwendet.
transient - - Diese Objekte werden bei Serialisierung und Deserialisierung ignoriert -> weiter erklären. Normalerweise nicht notwendig.
volatile - - Stellt Datenintegrität bei Multithreading sicher. Sonst nicht verwendet.

Generics

Mit Generics kann man Typen genauer definieren. Wie etwa bei einem Array dem man auch sagt welcher Art die Einträge sind. Bei einer Liste List kann man daher mit einem Generic angeben, welcher Art diese Liste ist. Der Generic wird in spitzen Klammern nach der Definition gesetzt:
List <Point> liste

Annotationen

Ähnlich wie Generics werden Annotationen eingesetzt, um etwas genauer zu spezifizieren. Sie wirken aber nicht auf Objekte, sondern auf Klassen oder Methoden und sind wie static oder public Modifikatoren. Java kennt ab V7 vier Stück (es gibt noch mehr im Paket javax.annotation, doch die sind meist nicht von Relevanz). Die Annotationen können je nach Typ auch Werte besitzen, welche die Hinweise spezifizieren. Ob und welche Werte sie besitzen wird in der Spezifikation festgelegt. Der Wert wird, falls verwendet, hintenan gestellt in Klammern: @Annotation (Wert)

Annotationstyp Wirkung Bemerkung
@Override Die annotierte Methode überschreibt eine Methode aus der Oberklasse oder implementiert eine Methode einer Schnittstelle. Keine Wertangabe erlaubt.
@Deprecated Das markierte Element ist veraltet und sollte nicht mehr verwendet werden. Keine Wertangabe erlaubt.
@SuppressWarnings Unterdrückt bestimmte Compiler-Warnungen. Somit kann man Warnungen bei älterem Code verhindern oder wenn man bei einer Definition keine Generics einsetzt. Am umfassendsten ist ("all") vor der Klassendefinition. Will man bestimmte Werte kombinieren, so verwendet man geschweifte Klammern: ({"rawtypes", "unchecked"})
@SafeVarargs Besondere Markierung für Methoden mit variabler Argumentzahl und generischem Argumenttyp -

In der Praxis verwendet man normalerweise nur @Override häufig. @Deprecated und @SuppressWarnings sollte man vermeiden, denn sie weisen entsprechend auf alten oder fehlerhaften Code hin.

Zugriff auf Variablen

öffentliche Variablen

Der Zugriff erfolgt wie bei Methoden über den Punktoperator: Klasse.Variable

class MyClass {
  public int i = 10;
} // end MyClass

public class MyTest {
  public static void main(String[] args) {
    MyClass object = new MyClass();
    System.out.print(object.i);
  } // end main
} // end MyTest

Man sieht, dass die Variable über die Instanz der Klasse angesprochen wird und somit Teil der Instanz ist. Würde man eine zweite Instanz erstellen und den Wert der einen Instanz ändern, so wäre die Variable der zweiten Instanz nicht betroffen.

statische Variablen

class MyClass {
  public static int i = 10;
} // end MyClass

public class MyTest {
  public static void main(String[] args) {
    System.out.print(MyClass.i);
  } // end main
} // end MyTest

Hier wird keine Instanz gewählt, da man schliesslich unabhängig der Instanzen ist, sondern man greift auf die Klasse selber zu. Das heisst, wenn man diese Variable anpasst, ist diese für alle Instanzen gültig, da es auf die Klasse wirkt (Instanzenzähler). Siehe statische Variablen.

this

Hierbei handelt es sich um einen Verweis auf die eigene Instanz. Möchte man nämlich eine interne Variable ansprechen, so muss das Objekt wissen, dass jetzt die eigene Instanz gemeint ist. Will man etwa aus Unterblöcken auf gleichnamige äussere Variablen der Klasse zugreifen, so kann man dies mit this:

class MyClass {
  public MyClass(int i,String s) {
    this.i = i;
    this.s = s;
  } // end Konstruktor

  private    int i;
  private String s;
} // end MyClass

Die Zeile mit dem this.i verweist auf das äussere i, welches sonst vom internen i unsichtbar gemacht worden wäre.

getter (get()) und setter (set(x))

In Java sollte man Variablen nicht direkt modifizieren, sondern dies immer definiert über Methoden erledigen. Methoden, welche nur den Wert einer Variablen zurückliefern oder diese nur setzen, werden entsprechend getter und setter genannt. Bei get() bekommt man als Rückgabe einfach den Inhalt der Variable zurück, während bei set kein Rückgabewert verwendet wird.
Ich selber würde aber auch bei einem set(x) min. einen Boolean zurückgeben, damit man weiss, dass die Zuweisung erfolgreich war. Falls nicht (Wert nicht im erlaubten Bereich), könnte man dann entsprechend reagieren. Sonst muss man nicht nur die Fehlerbehandlung sondern auch die Ausgabe in der Methode erledigen. Sonst kann man einfach den Fehlerstatus setzen und den Fehlercode in einer speziellen Fehlervariable des Objektes ablegen, welche man anschliessend zur Ausgabe an eine andere Methode übergeben kann.

public String getName() { return name; } // end getName()

public void setName(String name) { // Standard
  if ( name != null && !name.trim().isEmpty()) { this.name = name; } // end if
} // end setName()

public boolean setName(String name) { // Mit Statusrückgabe
  if ( name != null && !name.trim().isEmpty()) { 
    this.name = name; 
    return true;
  } // end if
  this.errorText = "setName Aufruf schlug fehl"; // der Fehler sollte natürlich genau eingegrenzt werden (null, leer, zu lang usw.)
  this.errorCode = 94; // die Fehlernummer sollte intern natürlich aufgelöst werden können
  return false;
} // end setName()

Interne Variablen sind natürlich über die entsprechenden Modifikatoren zu kapseln.

Methoden (Funktionen)

In Java werden Subroutinen als Methoden bezeichnet im Gegensatz zu c, wo von Funktionen gesprochen wird.
Wie beim Konstruktor gesehen, kann man einer Methode Werte übergeben und auch wieder daraus auslesen. Auf Methoden wird wie auf Variablen mit dem Punkt-Operator zugegriffen Klasse.Methode()

class MyClass {
  public int MyMethod(int a) {
    System.out.print("Intern: "+a);
    a++;
    return a;
  } // end MyMethod
} // end MyClass

public class MyTest {
  public static void main(String[] args) {
    int b = 5;
    MyClass object = new MyClass();
    b = object.MyMethod(b);
    System.out.print("Neu:"+b);
  } // end main
} // end MyTest
  • Damit man Werte übergeben kann, muss der Übergabeparameter übereinstimmen -> sonst evtl. Cast notwendig
  • Ist der Rückgabewert void, so gibt die Methode nichts zurück. Sonst zeigt man an, was zurückgegeben wird
  • Die Übergabe geschieht immer als Wert. Ausnahmen bei Arrays und Klassen, da dann ein Zeiger als Referenz übergeben wird
  • Als Rückgabewert kann auch eine Klasse definiert werden, so dass beliebige Rückgabemengen möglich sind.
  • Methoden können durch unterschiedliche Parameteranzahl oder Datentypenunterschied überladen werden. methode(int) methode(int, int) methode(int, string)

Möchte man Methoden aus der Java-Bibliothek verwenden, so muss die entsprechende Bibliothek vorher mit import eingebunden werden. Manchmal ist nicht ganz klar, in welchem Paket die gewünschte Methode ist, da manche Methoden zwar gleich heissen, doch eine andere Funktion besitzen. In diesem Fall muss man über die API-Dokumentation herausfinden, welches nun die korrekte Methode ist oder halt im Internet nachfragen.

Methoden überladen

Im Gegensatz zu php kann man in Java nicht einfach fixe Werte an nicht übergebene Parameter zuweisen. static double rechner( double eins = 5, double zwei = 10) ist nicht erlaubt. Möchte man dies erreichen, so muss man die Methode von einer anderen Methode aus aufrufen und dort den Fixwert definieren.
Diese mehrfache Definition der gleichen Methode nennt man "überladen".

double rechner (eins, zwei) {
  return eins * zwei;
}

double rechner () {
  int eins = 5;
  int zwei = 10;
  return rechner(eins, zwei);
}

Nun kann man rechner aufrufen und erhält einen Standardwert. Dies geht entsprechend in diversen Varianten, bedingt gegenüber anderen Sprachen halt einfach mehr Code.

Methoden verketten

Muss man auf ein Objekt mehrere (komatible) Methoden nacheinander anwenden, so kann man diese auch mit dem Punkt-Operator verketten.

java.awt.Point  p = new java.awt.Point(2,7);
System.out.println( p.toString().length() );

Variable Anzahl Parameter übergeben

Wenn bei der Definition nicht klar ist, wie viele Parameter man der Methode übergibt, dann gibt es die Möglichkeit, diese als variables Feld (Array) zu definieren. Dies geschieht mit typ... array, wobei man diesen Parameter auch anders nennen könnte, doch da es sich schlussendlich um ein Array handelt, wird es so gleich deutlich. Dies kann nun einfach mit einer for (int a:array) ausgelesen werden.

static int sum(int... array) {
  for (int a:array) {
    a += a;
  } // end for
  return a;
} // end sum

Nützliche Methoden

Die folgenden Methoden sind alphabetisch nach dem Methodennamen sortiert. Es handelt sich um eine Auflistung von nützlichen allgemeinen Methoden.

equals() Objekte vergleichen

Im Gegensatz zu primitiven Typen kann man Objekte nicht einfach mit "=" vergleichen, da damit nur geprüft würde, ob sie auf das gleiche Objekt referenzieren. Hier muss man stattdessen die Methode equals() verwenden, die etwa schon bei den Arrays gezeigt wurde. Wenn man eigene Objekte erstellt, muss man die Methode natürlich selber implementieren, da man dann festlegen muss, welche Variablen für den Vergleich beigezogen werden sollen.
Wenn man dies nicht macht, dann erbt man die Methode von Object, welche einfach nur die Referenz testet.

exit() Wert ans aufrufende Programm zurückgeben

wenn das Programm einfach beendet wird, so gibt Java keinen Rückgabewert zurück. Entsprechend kann nicht geprüft werden, ob die Verarbeitung fehlerfrei abgeschlossen wurde. Aus diesem Grund gibt es die statische MethodeSystem.exit(Statuscode);, welche den Code zurückgibt. Im Erfolgsfall sollte man immer 0 zurückgeben, da dies als "erfolgreich" interpretiert wird.

...length() Länge zurückgeben

Wird meist auf Strings oder Streams angewandt.

String s = "Test";
System.out.println( s.length() );
long size = new java.io.File( "file.txt" ).length(); // Dateilänge in Byte speichern
printXXX() Werte (auf der Konsole) ausgeben

Befindet sich in der Klasse System.out

  • print(); -> Ausgabe des enthaltenen Arguments ohne Zeilenumbruch.
  • println(); -> Ausgabe des enthaltenen Arguments mit anschliessendem Zeilenumbruch.
  • printf(); -> Ausgabe des enthaltenen Arguments mit Formatierungen.
    %n ergibt einen Zeilenumbruch, %d steht für eine ganze Zahl, %f ist für eine Fliesskommazahl und %s steht für eine Zeichenkette.%b steht für Boolean, %t für Datum und Zeit, während %e für die wissenschaftliche Schreibweise einer Zahl steht. Dazu wird mit %% ein Prozentzeichen ausgegeben, während man mit %x hexadezimale Zahlen ausgibt und %c explizit Unicode-Zeichen verarbeitet.

printf(%d Prozent ist nicht ganz %s.%n", 99, "Hundert" );

  • Der Fehlerkanal wird mit System.err.printXXX() angesprochen. Dies ist ein anderes Objekt und ermöglicht es, die Fehlerausgabe umzuleiten (etwa in eine Logdatei).
  • Bei diesen Methoden kann man Strings und "einfache Datentypen" kombinieren mit dem + Zeichen: print("Ausgabe: " + wert);

Für die Formatierung kann man die Stellenanzahl (auch mit führenden Nullen %05d) und bei Fliesskommazahlen auch die Nachkommastellen angeben %010.2f.
Beim Parameter %t muss entsprechend angegeben werden, welche Werte ausgegeben werden sollen. Die mögliche Liste ist sehr lang (%tA -> Wochentag oder %tc komplettes Datum mit Zeit in der Form %ta %tb %td %tT %tZ %tY).

random() Zufallszahl generieren

Die Methode der Klasse Math liefert als Ergebnis einen Zufallswert zwischen 0 und 1. Je nach gewünschtem Wert muss der Bereich entsprechend berechnet werden.
x = Min + (int)(Math.random() * ((Max - Min) + 1))
int r = (int)(Math.random() * 5 + 1); Liefert einen Integer-Wert zwischen 1 und 5

Scanner() Werte (von der Konsole) einlesen

Der Methode aus der Klasse java.util muss noch gesagt werden, von wo man Daten einlesen will und auch der zu erwartende Datentyp muss mitgegeben werden, da Java keine impliziten Casts vornimmt. Entsprechend kann bei fehlerhafter Eingabe ein InputMismatchException geworfen werden, den man entsprechend abfangen muss.
Man kann für die Eingabe auch die lokale Spracheinstellung explizit setzen, auch wenn Java sonst einfach standardmässig die vom Betriebssystem nimmt useLocale(Locale.German(Switzerland));.

Konsole
  • String: String name = new java.util.Scanner(System.in).nextLine();
  • Int: int age = new java.util.Scanner(System.in).nextInt();
  • Double: double value = new java.util.Scanner(System.in).nextDouble();
Datei
import java.io.*;
import java.util.Scanner;
public class ReadAllLines {
  public static void main( String[] args ) throws FileNotFoundException { // Da es einen Fehler geben kann, sollte entsprechend das catch implementiert werden.
    Scanner scanner = new Scanner(new File("Datei.txt"));
    while(scanner.hasNextLine()) {
      System.out.println(scanner.nextLine());
    } // end while
    scanner.close(); // Speicher freigeben
  } // end main
} // end class
toString() In einen String umwandeln

Jedes Objekt kann in einen String umgewandelt werden. Ob die entsprechende Ausgabe dann aber sinnvoll ist, ist nicht unbedingt definiert.

java.awt.Point  p = new java.awt.Point(5,10);
System.out.println( p.toString() );

Fehlerbehandlung (try-catch(-finally))

Definition der Fehlerklassen

Wenn ein Fehler im Programmablauf auftritt, so ruft Java spezielle Klassen zur Fehlerbehandlung auf:

Fehlerart Beschreibung
Error Diese Klasse repräsentiert die schwersten Fehler im System. Auch wenn sie recht selten auftreten, so ist fortan ein störungsfreier Programmverlauf kaum noch zu garantieren. Subklassen dieser Kategorie sollten nicht selber ausgelöst werden. In der Regel wird bei diesen Fehlern das Programm vollständig beendet. Diese Fehlerklasse wird normalerweise vom System aufgerufen und nicht programmiert.
Exception Sie ist die wichtigste Hauptkategorie, auf die man sich als Programmierer konzentrieren sollte. Prinzipiell ist auch diese noch einmal zweigeteilt. Und zwar in alle Ausnahmen die von RuntimeException abgeleitet sind oder nicht. Alle benutzerdefinierten Ausnahmen sollten sich in diese Sparte einordnen.
Runtime Exception Hierbei handelt es sich um Laufzeitfehler, die während des Programmverlaufs auftreten. Die Kategorie vereint alle Fehler, die auf Grund eines Programmierfehlers auftreten können. Dazu gehören unter anderem die folgenden Fälle.
  • fehlerhafte Typkonvertierung
  • Zugriff über die Arraygrenze hinaus
  • Zugriff auf einen leeren Zeiger

Alle nicht von RuntimeException abgeleiteten Klassen repräsentieren Fehler, die sich durch ungünstige Umstände und nicht etwa durch fehlerhaften Code ergeben. Beispiele dafür sind folgende Gegebenheiten.

  • Fehler beim Lesen einer Datei
  • ungenügend Systemressourcen
  • Fehlfunktion externer Dienste

Je nach Umständen kann das Programm bis auf die aufgerufene Funktion je nach Benutzerentscheid normal weiter laufen.

Der Ablauf ist folgendermassen:

  • Aufruf des try-Blocks
  • Fehlerbehandlung, falls notwendig durch catch
    Wird der Fehler nicht abgefangen, wird er schlussendlich durch die generelle Fehlerbehandlung abgefangen, doch dann sieht man meist nur, dass ein Fehler auftrat, doch man kann dann evtl. nicht mehr genau sagen, was passiert ist.
  • Aufruf des finally-Blocks

try-catch

Im try-Block wird versucht, eine bestimmte Anweisung auszuführen. Schlägt diese fehl, so wird der catch Block aufgerufen. Hier zeigt sich, dass der Programmierer selbst herausfinden muss, welche Fehler wann auftreten können und wie man diese am besten behandelt.

public class MyClass {
  public static void main(String[] args) {
    try {
      Thread.sleep(2000);
    } // end try
    catch(InterruptedException e) {
      System.out.print("Fehler aufgetreten!");
    } // end catch
  } // end main
} // end myClass

Der catch-Block erhält als Parameter die Instanz der auslösenden Fehlerklasse. Dies kann bei der Auswertung hilfreich sein. Bei der Bearbeitung muss man wissen, dass die Klasse von throwable abgeleitet wird und somit schon vordefinierte Methoden enthält, mit welchen man arbeiten kann.

catch-Typen

Für die Behandlung muss man natürlich wissen, welche Ausnahme ausgelöst wird. Das heisst, es muss bekannt sein, welcher Art Fehler aufgetreten ist. Können mehrere Fehlerarten auftreten, so müssen diese alle aufgeführt werden. Stimmt der Fehlertyp nicht überein, versucht Java, den Fehler in einer höheren Klasse abzufangen. Gelingt dies nicht, so wird das Programm mit einem Runtime-Error beendet.

try {
  // Anweisungen
} // end try

catch(Typ1 Objekt) {
  // Anweisungen
} // end catch Typ1

catch(Typ2 Objekt) {
  // Anweisungen
} // end catch Typ2

eigene Fehlerklassen

Möchte man auf "eigene" Fehler reagieren können, so kann man auch eigene Fehlerklassen definieren und diese dann verwenden. Dabei sollte man von Exception ableiten:

class MyException extends Exception {
  public MyException() {
  } // end leerer Konstruktor

  public MyException(String s) {
    super(s);
  } // end Konstruktor mit string, welcher den Konstruktor von Exception aufruft
} // end MyException

public class MyClass {
  public static void main(String[] args) {
    try {
      throw new MyException("Fehler aufgetreten..."); // Fehlerbehandlung explizit aufrufen
    } // end try

    catch(MyException e) {
      System.out.print(e.getMessage());
    } // end catch MyException
  } // end main
} // end MyClass

Fehler ankündigen (throws)

Falls eine Methode einen Fehler (Exception) auslösen könnte (Datei nicht schreibbar usw.), muss dies wegen des Sicherheitskonzepts angekündigt werden, indem man beim erstellen der Methode throws mitgibt und dann die Art des Fehlers. Gibt es verschiedene Fehlerarten, so werden diese komplett mit Komma getrennt aufgelistet. Dazu muss die Methode den problematischen Block jeweils mit try umschliessen.

[Modifikator] Typ Methode() throws Exception1, Exception2, ... {
  // ....
  try {
  // problematische Anweisung
  } // end try
} // end Methode

finally

Dieser Block ist optional und wird immer aufgerufen, wenn er vorhanden ist. Er wird genutzt, um wichtige Arbeiten auszuführen, auch wenn ein Fehler auftrat (Verbindungen schliessen usw.).

[Modifikator] Typ Methode() throws Exception1, Exception2, ... {
  // ....
  try {
  // problematische Anweisung
  } // end try
  catch {
  // Fehlerbehandlung
  } // end catch
  finally {
  // Schlussblock, welcher immer aufgerufen wird
  } // end finally
} // end Methode

Reguläre Ausdrücke

Siehe dazu den Artikel Reguläre Ausdrücke. Hier werden nur Java spezifische Sachen behandelt. So ist etwa das Ganze im Paket java.util.regex zu finden.
Neben den "normalen" Aufzählungen wie .*? gibt es noch die Aufzählung X(?!Y) welche bedeutet, dass Y nicht auf X folgen darf, damit der Ausdruck true ist.
Im Gegensatz zu PHP verwendet Java die Modifikatoren als Flags in Klammern. So bedeutet (?i), dass ab diesem Ausdruck insensitive verglichen wird:System.out.println("wauWau".matches("(?i)wauwau")); // true

Vergleich

Für einen Vergleich verwendet man entweder die Methode java.util.regex.Pattern.matches() oder bei Strings die Objektmethode matches().

Pattern.matches( "'.*'", "'Hallo Welt'" ) // true -> Hochkomma + beliebiger Inhalt + Hochkomma

Suche

String s = "'Demnach, welcher verheiratet, der tut wohl; welcher aber nicht verheiratet, der tut besser.' 1. Korinther 7, 38";
Matcher matcher = Pattern.compile( "\\d+" ).matcher( s ); // sucht Zahlen
while ( matcher.find() ) // true, wenn etwas gefunden wird, das dem Suchkriterium entspricht
  System.out.printf( "%s an Position [%d,%d]%n",
                     matcher.group(), // Gibt das Suchresultat zuück (in diesem Fall die Zahlen 1, 7, 38)
                     matcher.start(), matcher.end() ); // Start- und Endposition im String (Die Endposition ist immer eins höher. Bei der Position von der 7 wird 107,108 zurückgegeben.)

Links

Link Beschreibung Niveau
http://java.programmersbase.net/ Einführung in die Java-Programmierung Anfänger
http://openbook.galileocomputing.de/javainsel9/ Buch: Java ist auch eine Insel Anfänger-Fortgeschrittene
https://www.oracle.com/java/technologies/javase-downloads.html Dokumentation Java-API zum Download Anfänger-Fortgeschrittene
https://docs.oracle.com/javase/9/docs/api/index.html?overview-summary.html Dokumentation Java-API zum Online nachlesen Anfänger-Fortgeschrittene