Java - Eine Einführung

Transcription

Java - Eine Einführung
Java - Eine Einführung
Ole Schulz-Trieglaff
7. April 2004
2
Inhaltsverzeichnis
1 Der erste Tag
1.1 Einleitung . . . . . . . . . . . . . . . .
1.2 Jetzt geht es los . . . . . . . . . . . . .
1.2.1 Ausführen des Programms . . .
1.3 Java-Applets . . . . . . . . . . . . . .
1.4 Variablen und Operatoren . . . . . . .
1.4.1 Datentypen . . . . . . . . . . .
1.4.2 Operatoren und Berechnungen
1.5 Typecasts . . . . . . . . . . . . . . . .
2 Der
2.1
2.2
2.3
2.4
2.5
2.6
2.7
zweite Tag
Schleifen und Anweisungen . . . .
if-Anweisung . . . . . . . . . . . .
Die while-Schleife . . . . . . . . . .
do-while-Schleife . . . . . . . . . .
Die for-Schleife . . . . . . . . . . .
Die switch-Anweisung . . . . . . .
Break und continue . . . . . . . . .
2.7.1 Labeled Breaks . . . . . . .
2.7.2 continue . . . . . . . . . . .
2.8 Arrays . . . . . . . . . . . . . . . .
2.8.1 Zugriff auf Array-Elemente
2.8.2 Mehrdimensionale Arrays .
2.9 Lesen von der Kommandozeile . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
5
6
7
7
7
8
8
10
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
11
11
11
12
12
13
13
14
14
14
15
15
15
16
3 Der dritte Tag
3.1 Ojektorientierte Programmierung . .
3.1.1 Klassen und Objekte . . . . .
3.1.2 Methoden . . . . . . . . . . .
3.1.3 Parameter . . . . . . . . . . .
3.1.4 Rückgabewerte . . . . . . . .
3.1.5 Überladen von Methoden . .
3.1.6 Rekursion . . . . . . . . . . .
3.1.7 Konstruktoren . . . . . . . .
3.1.8 Verkettung von Konstuktoren
3.2 Ein- und Ausgabe . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
17
17
17
17
18
19
20
20
20
21
21
3
4
INHALTSVERZEICHNIS
4 Der
4.1
4.2
4.3
4.4
4.5
vierte Tag
Vererbung . . . . . . . . . . . .
Modifier . . . . . . . . . . . . .
static . . . . . . . . . . . . . . .
Abstrakte Klassen . . . . . . .
Interfaces . . . . . . . . . . . .
4.5.1 Mehrfachvererbung . . .
4.6 Graphische Ausgabe mit Swing
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
23
23
24
25
25
26
27
27
Kapitel 1
Der erste Tag
1.1
Einleitung
Java wurde Anfang der 90er Jahre von der kalifornischen Firma SUN Microsystems entwickelt. SUN wollte damals ein System entwickeln mit dem sich die
unterschiedlichsten technischen Geräte ansteuern lassen. Alle bisherigen Programmiersprachen waren zu unflexibel, d.h. es hätte einen sehr großen Aufwand
erfordert, ein Programm, dass z.B. für einen PC entwickelt worden war, an einen
Toaster anzupassen. (Der Fachbegriff dafür ist portieren)
Java wurde im Rahmen dieses Projektes mit dem Ziel entwickelt möglichst einfach, zuverlässig und portabel zu sein. Das Projekt selbst war nicht besonders
erfolgreich, nur Java blieb übrig und wurde weiter entwickelt.
Eine kleine Anekdote: Java hieß ursprünglich Oak (engl. für Eiche), wie die
Bäume, die vor dem Büro eines der Chef-Entwickler von Java, James Gosling,
stehen. Es stellte sich jedoch heraus, das es bereits eine Programmiersprache
mit diesem Namen gab. Um juristische Schwierigkeiten zu vermeiden, wurde
schnell nach einem neuen Namen gesucht. Eine der Kaffesorten des Ladens, in
denen das Team seinen Kaffee kaufte, hieß Java...
Jetzt schnell noch ein Überblick über die wichtigsten Merkmale von Java:
• Java ist objektorientiert. Das ist so ziemlich die wichtigste Eigenschaft.
Sie sagt im Wesentlichen aus, dass man in Java versucht Teile eines Programms in sog. Objekte oder Klassen zu strukturieren. Was dies genau
heisst, werden wir erst später sehen.
• Java unterstützt das Internet. Java ist auf Netzwerkanwendungen optimiert d.h. viele Programme die Daten in Netzwerken übertragen müssen,
lassen sich mit Java sehr leicht realisieren.
• Java ist fexibel und robust. Wie oben schon erwähnt lassen sich in
Java geschriebene Programme ohne große Mühe an verschiedene Rechner
anpassen. Außerdem wurde bei der Entwicklung großer Wert auf Datensicherheit gelegt. Alle Java Programme laufen in einer Art Schutzkäfig
(engl. Sandbox), der sie vor anderen Programmen abschirmt. Dadurch
wird verhindert, dass ein Java-Programm Schaden verursachen kann.
• Java verfügt über viele Standardfunktionen Auch ein gutes Argument für Java: für viele häufig auftretenden Probleme besitzt Java schon
5
6
KAPITEL 1. DER ERSTE TAG
vorgefertigte Lösungen, die kostenlos (wie auch die Programmiersprache
selbst) mitgeliefert werden.
Noch eine kleine Anmerkung: Die erste Version von Java wurde 1995 veröffentlicht und hatte die Versionsnummer 1.0. Ab der Version 1.2 spricht man kurioserweise vom Java 2 SDK ( = Software Development Kit). Die neuste Version
heisst allerdings wieder Java 1.5....
1.2
Jetzt geht es los
Wir wollen uns jetzt ein Java-Programm mal etwas genauer anschauen. Grundsätzlich unterscheidet man (und frau auch) zwischen sog. Applets, die fest in Webseiten eingebaut sind und Java-Programmen, die sich auch alleine ausführen lassen.
/*
Hallo.java
Ein erstes Java-Programm
Ole Schulz-Trieglaff, März 2004
*/
// Hier geht es los
public class Hallo {
public static void main(String[] args) {
System.out.println("Hallo!");
System.out.println("Ich ein Java-Programm!");
}
}
Was bedeutet das alles ?
• Die ersten sieben Zeilen bilden Kommentare.
• public class Hallo {..}
Definiert alles, was zu (Haupt-)Klasse Hallo gehört. Die Inhalt der Klasse
wird von geschweiften Klammern umrahmt: { und }.
• public static void main(String[] args) {..}
Beim Ausführen der Klasse wird dieser Abschnitt zuerst ausgeführt. Alles
was zu diesem main-Abschnitt gehört, wird wieder von Klammern eingerahmt.
•
System.out.println("Hallo!");
Dieser Befehl gibt Hallo! auf dem Bildschirm aus. Alle Befehle müssen in
Java mit einem Semikolon abgeschlossen werden.
1.3. JAVA-APPLETS
1.2.1
7
Ausführen des Programms
Das Ganze läuft immer gleich:
1. Programm schreiben / abtippen mit Editor der Wahl und speichern. Der
Dateiname muss dabei dem Namen der Hauptklasse entsprechen. (Im Beispiel oben Hallo.java ).
2. Für den Computer übersetzen (kompilieren) mit: javac Hallo.java Dieser
Befehl erzeugt eine Datei Hallo.class, die Java-Bytecode enthält.
3. Ausgeführt wird dieser Bytecode dann mit dem Befehl java Hello.
1.3
Java-Applets
Der Vollständigkeit halber möchte ich hier nochmal kurz ein Java-Applet vorstellen. Applets sind kleine Programme, die in eine Internetseite eingebaut sind
und von einem Webbrowser (z.B. dem Internet Explorer) ausgeführt werden.
Ein kurzes Applet kann z.B. so aussehen:
import java.awt.*;
import java.applet.Applet;
public class FirstApplet extends Applet {
public void paint (Graphics g) {
g.drawString("Hallo! Ich bin ein Java-Applet.",50,50);
}
}
1.4
Variablen und Operatoren
Variablen sind Platzhalter oder Container. Sie speichern Werte, mit denen ein
Programm arbeiten soll. Um eine Variable verwenden zu können, muss man sie
zuerst deklarieren und sie dann initialisieren. Beispiel:
\\ Wir erstellen eine Variable vom Typ int mit Namen var
int var;
// Deklaration: Variable var hat Typ int
var = 99;
// Initialisierung: var hat jetzt den Wert 99
Deklaration und Initialisierung sind Anweisungen, deshalb werden sie auch
mit einem Semikolon beendet.
Java unterscheidet zwischen Groß- und Kleinschreibung. Deshalb sind var und
Var unterschiedliche Variablen. Es gibt ebenfalls strenge Regeln, welche Zeichen
Variablennamen (identifier) enthalten dürfen:
1. Die Zeichen des Alphabets (java letter), also a-z and A-Z
2. Der Unterstrich
3. Die Ziffern von 0-9
8
KAPITEL 1. DER ERSTE TAG
4. Ein identifier muss mit einem java letter oder $ beginnen.
Es gilt die Konvention, dass eine Variable immer mit einem kleinen Buchstaben
beginnen muss. Falls der Name aus mehreren Teilen besteht, wird jeder außer
dem ersten Teil mit einem Großbuchstaben begonnen, z.B. : hoeheDerBox
1.4.1
Datentypen
Man unterscheidet in Java (und in den meisten anderen Programmiersprachen
auch) zwischen:
• primitiven Datentypen. Sie sind nicht weiter unterteilbar.
• und zusammengesetzten Datentypen. Sie bestehen aus mehren Teilwerten, die jeweils einzeln manipuliert werden können. Ein Beispiel dafür sind
Arrays, die Ähnlichkeit zu Listen in Haskell besitzen.
Hier eine Übersicht über die wichtigsten primitiven Datentypen:
Name
Grösse
Wertebereich
byte
8
-126 bis +128
short
16
-32768 bis 32767
int
32
−231 bis 231 − 1
long
64
−263 bis 263 − 1
float
32
−3.4 × 1038 bis +3.4 × 1038
double
64
−1.797 × 10308 bis +1.797 × 10308
boolean
1
true oder false
char
16
Druckbare Zeichen: ‘a‘ bis ‘Z‘ auch Steuerzeichen
Der wichtigste zusammengesetzte Datentyp ist String. Er sieht in Java genauso aus wie in Haskell. Er besteht aus einem oder mehreren Zeichen, eingefasst
von doppelten Anführungsstrichen, wie z.B. “Java ist toll !! “. Man kann zwei
Strings aneinanderhängen (konkatenieren) mit dem + Operator (Nicht ++ wie
bei Haskell !!):
string str = "Java" + " ist " + "toll!";
int i = 5;
System.out.println("i hat den Wert: " + i);
1.4.2
Operatoren und Berechnungen
Java kennt folgende Operatoren:
arithmetische Operatoren:
• binäre: +, −, ∗, / und % (Modulooperation, Rest bei Division)
int x = 3 / 2;
float z = 3.0 / 2;
// ergibt 1
// ergibt 1.5
1.4. VARIABLEN UND OPERATOREN
9
• und unäre: ++ und −−. Beide gibt es in einer Präfix- und Postfix-Version.
Sie können nur auf Variablen angewendet werden.
int i = 5;
int k = i++;
int l = ++i;
// k = 5, i = 6
// l = 7, i = 7
Vergleichsoperatoren:
• == und ! = (ungleich) sind auf beliebige Typen anwendbar
• <, <=, >, >= für Zahlen und Buchstaben
Boolesche Operatoren: Auch Java kennt Lazy-Evaluation ! Es wird hier
aber Short-Circuit-Evaluation genannt.
• ! Nicht (not in Haskell)
• && Und, || Oder mit Short-Circuit-Evaluation
• & logisches und, | Oder, ∧ exklusives Oder (XOR)
Bitoperatoren Mit diesen Operatoren kann direkt auf die Binärdarstellung
von numerischen Datentypem zugegriffen werden.
• bitweises Komplement: ∼ inervertiert jedes einzelne Bit
• & bitweises Und, | bitweises Oder, ∧ bitweises XOR , << Linksshift, >>
arithmetischer Rechtsshift, >>> logischer Rechtsshift
// Mit dem Linksshift schnell potenzieren:
byte potenz = 2 << 2; // ergibt 2^3 = 8, denn 00000010 wird zu 00001000
Zuweisungsoperatoren Der Zuweisungsoperator ist in Java einfach das Gleichheitszeichen =. (Wer hätte das gedacht!) Es gibt aber zusätzlich noch die Möglichkeit, Operationen mit dem Gleichheitszeichen zu kombinieren. Ein Beispiel:
Wenn wir den Wert einer Variablen x um 5 erhöhen wollen, dann könnten wir
schreiben:
x = x + 5;
// x hat jetzt den Wert 5
Viel kürzer ist jedoch:
x += 5;
// x hat jetzt auch den Wert 5
Das Gleiche geht auch mit:
+ =, − =, ∗ =, / =, % =, & =, | =, ∧ =, <<=, >>=, >>>=
Was man auch noch wissen sollte: der Zuweisungsoperator liefiert das Ergebnis
zurück. Es sind also auch Verkettungen möglich wie:
float i,j,k;
i = j = 7;
k = 5 + 6*(i += j/2);
Zum Schluss gibt es noch einen Operator, von dem man etwas gehört haben
sollte, und zwar ?. Er wird auch als ternärer Auswahloperator bezeichnet:
max = (i>j) ? i : j;
10
1.5
KAPITEL 1. DER ERSTE TAG
Typecasts
Oft gerät man in Situationen, in denen man verschiedene Datentypen ineinander umwandeln will oder muss. So einen Vorgang nennt man Typecast. Man
unterscheidet zwischen expliziten und impliziten Typecasts:
expliziter Typecast:
float f = 2.5f;
int i = (int) f; // Float wird zu Int, i hat den Wert 2
impliziter Typecast:
5/2.0
// double 2.5
int i = 5;
System.out.println("i hat den Wert: " + i); // Typecast von int nach String
Allgemein gilt, dass erweiternde Typecasts, bei denen keine Genauigkeit verloren
geht, vom Compiler automatisch (implizit) durchgeführt werden. Also z.b. von
short nach float o. ä. In die umgekehrte Richtung ist eine explizite Anweisung
des Programmiers erforderlich.
Kapitel 2
Der zweite Tag
2.1
Schleifen und Anweisungen
Ein Block ist eine Zusammenfassung von Anweisungen, diese werden nacheinander abgearbeitet. Eine Variable ist ab ihrer Deklaration sichtbar und zwar bis
zu dem Ende des Blocks, in dem sie eingeführt wurde.
int i = 0;
{
int j = i; // OK
{
int k = i; // OK
int k = 0; // Fehler: k schon belegt
}
j = k; // Fehler: k nicht bekannt
int k = 0; // OK: k neu belegen
}
++j; // Fehler: j nicht bekannt
++i; // OK
2.2
if-Anweisung
Syntax
if (Bedingung)
Anweisung; // Wenn Bedingung true ist, wird Anweisung ausgeführt
oder
if (Bedingung)
Anweisung1; // Bedingung ist erfüllt
else
Anweisung2; // Bedingung ist nicht erfüllt
Beispiel:
if (Temperatur < -10 )
System.out.println("Brrr!!");
11
12
KAPITEL 2. DER ZWEITE TAG
Es geht auch:
if (Bedingung1)
Anweisung1; // Bedingung1 ist erfüllt
else if (Bedingung2)
Anweisung2; // Bedingung2 ist erfüllt
statt
if (Bedingung1)
Anweisung1; // Bedingung1 ist erfüllt
else
if (Bedingung2)
Anweisung2; // Bedingung2 ist erfüllt
Statt einer einzelnen Anweisung kann auch eine Folge von Anweisungen stehen.
Diese müssen dann allerdings als Block zusammengefasst werden.
Einer der typischen Fallstricke, wie sie in vielen Programmiersprachen auftreten
ist das Problem des dangling else:
if (a)
if (b)
s1;
else
s2;
Diese Codefragment wird nicht so ausgeführt, wie es die Einrückung vermuten
lässt. Der else- Zweig gehört zur innersten Verzweigung mit if(b).... Ein “freies“
else wird immer an das am weitesten innen liegende if angehängt.
2.3
Die while-Schleife
// berechne ganzzahligen Logarithmus von n
// zur Basis 2 fuer n > 0
int log = 0;
while (n > 1) {
++log;
n /= 2;
}
System.out.println("log = "+log);
2.4
do-while-Schleife
// berechne ganzzahligen log von n
// zur Basis 2 fuer n > 0
int log =-1;
do {
++log;
n /= 2;
} while (n > 0);
System.out.println("log = "+log);
2.5. DIE FOR-SCHLEIFE
13
Diese Schleife wird mindestens einmal ausgeführt, selbst wenn die while-Bedingung
nicht erfüllt ist.
2.5
Die for-Schleife
Syntax:
for (init; test; update)
Anweisung;
init und test sind Ausdrücke. Beide können auch aus mehreren Ausdrücke bestehen. Diese müssen dann durch Kommata getrennt werden. An der Stelle von
init dürfen auch Initialisierungen stehen.
Beispiel:
// berechnet 2^n
// also die n-te 2er Potenz für n>0
int potenz = 2;
for (int i=1; i<n; i++) {
potenz *= 2;
}
System.out.println(n + "-te 2er Potenz ist " + potenz);
init, test und update können auch leer sein:
\\ Die forever-Schleife
for (;;) {
System.out.println("Das hört ja nie auf.");
}
2.6
Die switch-Anweisung
switch (Ausdruck) {
case Constant1:
Anweisung1;
case Constant2:
Anweisung2;
....
default:
DefaultAnweisung;
}
Ausdruck wird zuerst ausgewertet. Er muss vom Typ byte, short, char oder int
sein. In Abhängigkeit vom Ergebnis wird dann die case-Marke angesprungen,
deren Konstante mit dem Ergebnis übereinstimmt und die entsprechende Anweisung ausgeführt.
Beispiel:
switch (Wochentag) {
case 1:
System.out.println("Es ist Montag.");
14
KAPITEL 2. DER ZWEITE TAG
break;
case 2:
System.out.println("Es ist Dienstag.");
break; // usw. ...
case 7: System.out.println("Es ist Sonntag.");
break;
default:
System.out.println("Falsche Eingabe!");
}
2.7
Break und continue
Die break -Anweisung kann dazu eingesetzt werden um Schleifen vorzeitig zu
verlassen.
for (int i=0; i<irgendwas; i++) {
// Mach was
if (Schluss)
break; // For-Schleife wird verlassen
// kein Schluss
}
// Hier geht es nach break weiter...
Wichtig: Bei verschachtelten Schleifen wird nur die innerste Schleife verlassen!
2.7.1
Labeled Breaks
Mit Hilfe von einem Label können wir auch mehrere Schleifen auf einmal verlassen:
for (int i=0; i<n; ++i) {
forLabel: // Label für die Break-Anweisung unten
for (int j=0; j<n; ++j) {
for (int k=0; k<n; ++k) {
// ...
if (Spring)
break forLabel;
// ...
}
}
// bis hierher wird gesprungen
}
2.7.2
continue
Die continue-Anweisung arbeitet ähnlich wie break. Ohne ein Label springt continue an das Ende der aktuellen Schleife und beginnt mit dem nächsten Durchlauf. Mit Label springt continue an das Ende der markierten Schleife.
Man sollte mit break und continue sparsam umgehen! Ein Programm kann durch
sie sehr unübersichtlich werden.
2.8. ARRAYS
2.8
15
Arrays
Arrays sind die nächste Entsprechung zu den Listen in Haskell und haben eine
ähnlich große Bedeutung. Sie sind eine Zusammenfassung (genauer: ein Tupel)
von Elementen gleichen Typs. Genau wie primitive Datentypen werden sie zuerst
deklariert
int[] arr1;
double arr2[]; // alternative Schreibweise, Tribut an die Sprache C++
bool[] barr;
und dann initialisiert
arr1 = new int[5]; // arr1 kann 5 Elemente vom Typ Int aufnehmen
arr2 = new double[10]
barr = new boolean[15];
In diesem Beispiel oben werden alle Elemente mit 0 bzw. false initialisiert. Alternativ können die Elemente auch schon bei der Initialisierung angeben werden:
int[] x = {1,2,3,4,5};
boolean[] b = {false,false};
2.8.1
Zugriff auf Array-Elemente
Wie in den Haskell-Listen beginnt die Indizierung bei 0. Der Zugriff auf einzelne
Elemente erfolgt durch einen Index, der in eckigen Klammern hinter dem ArrayNamen angegeben wird.
// Alle Elemente initialisieren
for (int i=0; i<arr.length; i++) {
arr[i] = i*10;
}
// Alle Elemente eines Arrays ausgeben
for (int i=0; i<arr.length; i++) {
System.out.println(arr[i]);
}
2.8.2
Mehrdimensionale Arrays
Mehrdimensionale Arrays werden als Arrays von Arrays angelegt. Sie müssen
nicht rechteckig sein, d.h. ein Array kann Arrays unterschiedlicher Größe enthalten.
Der Zugriff auf einzelne Elemente erfolgt durch Angabe aller Indizes, jeweils in
eckigen Klammern.
float[][] matrix = new float[10][10];
int[][][] matrix3d = new int[100][100][100];
// Ausgabe
for (int i=0; i<matrix.length; i++) {
for (int j=0; j<matrix[i].length; j++)
System.out.println("Der Eintrag an Stelle " + i +" " + j + " ist: " + matrix[i][j]);
}
16
KAPITEL 2. DER ZWEITE TAG
2.9
Lesen von der Kommandozeile
Wenn man ein Programm mit dem Befehl java ausführt kann man hinter dem
Programmnamen noch weitere Parameter angeben. Diese Parameter landen im
dem Array args aus der Main-Methode.
public class Echo {
public static void main(String[] args) {
for (int i=0; i<args.length; ++i)
System.out.print(args[i]+" ");
System.out.println();
}
}
Mit
Integer.parseInt(string)
lässt sich dann ein String-Parameter in einen Int umwandeln und mit
Double.parseDouble(string)
in einen Double.
Beispiel:
// Dieses Programm summiert alle Zahlen auf,
// die als Eingabeparameter übergeben wurden
// Aufruf: java Add 1 2 3
public class Add {
public static void main(String[] args) {
int sum = 0;
for(int i=0; i<args.length;i++) {
sum += Integer.parseInt(args[i]);
}
System.out.println("Summe: " + sum);
}
}
Kapitel 3
Der dritte Tag
3.1
3.1.1
Ojektorientierte Programmierung
Klassen und Objekte
Im Gegensatz zu Haskell sind nicht Funktionen das zentrale Element in Java,
sondern Klassen. Allgemein fasst eine Klasse Daten und Funktionen, die auf
diesen Daten operieren zusammen. Allerdings ist eine Klasse nichts Konkretes.
Am Besten stellt man sie sich als eine Art Bauplan oder Rezept vor, nachdem
der Computer vorgeht. Das Ergebnis einer solchen Konstruktion ist dann eine
Instanz (auch Objekt genannt).
Um bei unserem Beispiel zu bleiben: Wenn man sich eine Klasse als einen Bauplan z.B. für ein Auto vorstellt, dann definiert dieser Bauplan, was ein Auto
für Eigenschaften hat und was man damit machen kann. Ein Auto hat eine
Farbe und eine bestimme Leistung (in PS). Ein Auto kann beschleunigen und
abbremsen. Ein konkretes Auto, z.B. ein Porsche oder Volkswagen wäre dann
eine Instanz der Klasse Auto.
public class Auto {
int ps;
\\ Membervariablen oder Felder
String farbe = "Blau";
}
Wird für ein Feld kein Wert angegeben, wird es automatisch mit 0 bzw. f alse
initialisiert. Eine Instanz dieser Klasse lässt sich mit new erzeugen:
Auto meinAuto = new Auto();
meinAuto.ps = 300;
System.out.println("Mein Auto hat " + meinAuto.ps + " PS");
3.1.2
Methoden
Methoden definieren das Verhalten einer Klasse. Eine Klasse Auto sollte bremsen und beschleunigen können. Eine Methode kann auch verwendet werden, um
die Felder einer Klasse zu ändern oder auszugeben.
17
18
KAPITEL 3. DER DRITTE TAG
class Auto {
int ps
= 200;
String farbe = "Blau";
void tune(int p) {
// ändert die Membervariable PS
ps = p;
// kein Zugriff mit ’.’
}
void zeigeFarbe() { // gibt die Farbe aus
System.out.println("Dieses Auto hat die Farbe: " + Farbe);
}
}
public class AutoTest {
public static void main(String[] argv) {
Auto Golf = new Auto();
Golf.zeigeFarbe();
// "Dieses Auto hat die Farbe: blau "
Golf.tune(300);
// Dieser Golf hat jetzt 300 PS
}
Wie man in diesem Beispiel sieht, können Methoden einer Klasse auf die Felder
ohne den Punkt-Operator ’.’ zugreifen. Wenn man deutlich machen will, dass
man auf die Felder der aktuellen Klasse zugreift, dann kann man this verwenden,
eine Referenz auf die aktuelle Klasse:
void tune(int p) {
this.ps = p;
}
// ändert die Membervariable PS
Die Definition einer Methode hat den folgenden Aufbau (alles in eckigen Klammern ist optional):
[Modifier] Rückgabewert Name( [Parameter] ) {
[Anweisungen;]
}
Über die sogenannten Modifier, von denen wir bisher nur public kennen, werden
erst morgen mehr erfahren.
3.1.3
Parameter
Eine Methode kann mehrere Parameter als Übergabewerte erhalten. Anders als
bei Haskell-Funktionen muss nicht nur der Typ angegeben werden, sondern auch
ein Name für die Variable.
void beschleunigen(int wieOft) {
while (wieOft-- > 0)
System.out.println("Brumm");
}
Wichtig ist, dass in Java Parameter by value übergeben werden, d.h. es werden
nur Kopien übergeben und die Aufrufer der Methode merkt nichts von Änderungen der Variable in der Methode. Dies gilt nicht für Objekte, wie z.B. Arrays
oder eigene Klassen! Wenn man ein Objekt einer Methode übergibt, dann sind
3.1. OJEKTORIENTIERTE PROGRAMMIERUNG
19
Änderungen in der Methode auch für den Aufrufer sichtbar. Das liegt daran,
dass Objekte Referenzen sind, also auf einen Speicherbereich verweisen.
Auto Porsche = new Auto();
int i = 10;
Porsche.beschleunigen(i);
System.out.println(i); // ergibt 10
3.1.4
Rückgabewerte
Statt void (nichts) kann eine Funktion auch Werte wie int oder char zurückgeben. Die Anweisung dafür lautet return. Wenn diese Anweisung ausgeführt
wird, führt dies zum Beenden der Methode. Der Java-Compiler stellt sicher,
dass jeder Weg aus der Methode heraus mit einnem return abgeschlossen wird.
Beispiele:
int wrong() {
if (flag) // flag ist boolesche variable
return 1;
// Compilerfehler: kein return fuer !flag
}
int alsoWrong() {
if (flag)
return 1;
if (!flag)
// Fehler:
return 0; // Compiler nicht schlau genug
}
int ok() {
if (flag)
return 1;
else
return 0;
}
Noch ein Beispiel mit Parametern und Rückgabewert:
class Rechner { // Immer nur eine public Klasse pro Datei !!
int log (int n) {
int log = 0;
while (n > 1) {
++log;
n /= 2;
}
return log;
}
}
public class LogTest {
public static void main(String[] argv) {
20
KAPITEL 3. DER DRITTE TAG
if (argv.length != 1) {
System.out.println("usage: " +"java Factorial <n>");
return;
}
int n = Integer.parseInt(argv[0]);
Rechner re = new Rechner();
int l = re.log(n);
System.out.println("2er Logarithmus von " + n + " ist " + l);
}
}
3.1.5
Überladen von Methoden
Es ist erlaubt innerhalb einer Klasse zwei Methoden mit dem gleichen Namen zu
definieren. Der Compiler unterscheidet die Methoden dann anhand der Anzahl
und des Typs ihrer Parameter. Der Rückgabewert trägt nicht zur Unterscheidung bei. Es ist nicht erlaubt, zwei Methoden mit gleichem Namen und gleichen
Parametern zu definieren.
3.1.6
Rekursion
Rekursion ist in Java längst nicht so wichtig wie in Haskell, aber auch möglich:
int fakultät(int n) {
if (n < 2)
return 1;
return n * fakultät(n-1);
}
3.1.7
Konstruktoren
Unter einem Konstruktor versteht man eine spezielle Methode, die bei der Initialisierung des Objekts aufgerufen wird. Der Konstruktor hat den gleichen Namen
wie die Klasse zu der er gehört und hat keinen Rückgabewert. Er kann beliebig viele Parameter haben und darf überladen werden. Er kann dazu benutzt
werden, die Felder seiner Klasse zu initialisieren:
class Auto {
int ps;
String farbe;
Auto(int p) { // Konstruktor Nr. 1
ps = p;
}
Auto(int p, String f) { // Konstruktor Nr. 2
ps
= p;
farbe = f;
}
3.2. EIN- UND AUSGABE
21
}
public class AutoTest {
public static void main(String[] args) {
Auto BMW = new Auto(200);
// erster Konstruktor
Auto Mercedes = new Auto(210,"Schwarz"); // zweiter Konstruktor
}
}
Wenn man keinen Konstruktor expliziert definiert, dann wird vom Compiler ein
Default- Konstruktor ohne Parameter erstellt. Wenn man wie in dem Beispiel oben nur parametrisierte Konstruktoren angibt, dann wird kein DefaultKonstruktor erstellt.
3.1.8
Verkettung von Konstuktoren
Konstruktoren können sich auch gegenseitig aufrufen. Der aufzurufende Konstruktor wird dabei als normale Methode angesehen. Er wird über den Namen
this aufgerufen. Die beiden Konstruktoren aus dem Beispiel von oben könnten
auch so geschrieben werden:
Auto(int p) {
ps = p;
}
Auto(int p, String f) {
this(p);
farbe = f;
}
// ruft ersten Konstruktor auf
Dieses Beispiel ist vielleicht etwas verwirrend, aber bei größeren Klassen mit
vielen Feldern spart man sich Tipparbeit.
3.2
Ein- und Ausgabe
Die Ein- und Ausgabe von Daten wird in Java über Streams abgewickelt. Je
nach Empfänger und Sender der Daten gibt es verschiedene Streams. Will man
z.B. Eingaben von der Tastatur aus einlesen muss man einen anderen Stream
verwenden, als wenn man in eine Datei schreiben wollte. Streams sind selbst
Klassen. Wir arbeiten mit ihnen, indem wir Instanzen von ihnen anlegen und
ihre Methoden aufrufen. Streams lassen sich auch ineinander verschachteln z.B.
um die Eingabe zu filtern.
Einen wichtigen Stream haben wir schon kennengelernt:
System.out.println("Ausgabe: "); // Ausgabe mit Zeilenumbruch
int i = 10;
System.out.print(i);
// Ausgabe ohne Zeilenumbruch
22
KAPITEL 3. DER DRITTE TAG
Es ist leider etwas komplizierter, Daten von der Tastatur einzulesen. Wir müssen
zuerst eine Instanz der Klasse InputStreamReader anlegen und diese dann
mit einem BufferedReader verknüpfen.
import java.io.*;
public class Eingabe {
public static void main(String[] args) throws IOException {
int a;
InputStreamReader InStream = new InputStreamReader(System.in);
BufferedReader BReader = new BufferedReader(InStream);
System.out.print("Bitte geben sie eine Zahl ein: ");
String str = BReader.readLine();
// eine Zeile lesen
a = Integer.parseInt(str);
// String in int umwandeln
System.out.println("Sie haben eingegeben: " + a);
}
}
Die Klasse Integer ist eine sogenannte Wrapper-Klasse. Sie dient unter anderem dazu, andere Datentypen in Integer zu konvertieren.
Der Vollständigkeit halber noch ein Beispiel zur Ausgabe in eine Datei:
import java.io.*;
public class Ausgabe {
public static void main(String[] args) throws IOException {
int zahl = 40;
File datei = new File("Test.txt");
FileWriter OutStream = new FileWriter(datei);
PrintWriter OutWriter = new PrintWriter(OutStream);
OutWriter.println("Das wird jetzt in die Datei geschrieben");
OutWriter.println(zahl); // Schreibt 40 in die Datei
OutWriter.close();
// schliest dann Stream zur Datei
}
}
Hier wird erst eine Instanz der Klasse File erzeugt. Der Konstruktor erhält
den Namen der Datei als String. Dann wird eine FileWriter- und eine PrintWriter-Instanz erzeugt. Die PrintWriter-Instanz kann jetzt wie System.out
verwendet werden.
Kapitel 4
Der vierte Tag
4.1
Vererbung
Neben der Verwendung von Klassen ist Vererbung ein wichtiges Merkmal objektorientierter Sprachen. Unter Vererbung versteht man die Möglichkeit, Eigenschaften vorhandener Klassen auf andere zu übertragen.
class Mitarbeiter {
String name;
int gehalt;
Mitarbeiter(String n, int g) {
name = n;
gehalt = g;
}
void gehaltErhoehen(int erhoehung) {
gehalt += erhoehung;
}
}
class Chef extends Mitarbeiter { // Diese Klasse erbt von Mitarbeiter
String abteilung;
// Klasse Chef hat zusätzliches Feld abteilung
// Konstruktor der Oberklasse wird aufgerufen
Chef(String n, int g, String abt) {
super(n,g);
// muss als erstes stehen
abteilung = abt;
}
// Diese Methode überschreibt die Methode aus der Oberklasse
void gehaltErhoehen(int erhoehung) {
gehalt += 2*erhoehung;
}
}
23
24
KAPITEL 4. DER VIERTE TAG
• Vererbung ermöglicht die Abbildung von Hierarchien und die Wiedervendung von Code.
• Die Unterklasse erbt Methoden und Felder der Oberklasse. Sie kann aber
auch eigene Member definieren.
• Methoden der Unterklasse, welche die gleiche Signatur wie Methoden der
Oberklasse besitzen, überschreiben diese.
• Es gibt in Java keine Mehrfachvererbung d.h. eine Klasse hat immer nur
eine Oberklasse und nicht mehrere.
• Jede Java-Klasse erbt von der Klasse Objekt auch wenn es nicht explizit
angegeben wird.
public class TestMitarbeiter {
public static void main(String[] args) {
Mitarbeiter m = new Mitarbeiter("Heinz",1000);
Chef c = new Chef("Ole",2000,"Abeilung2");
m.gehaltErhoehen(100);
c.gehaltErhoehen(100);
System.out.println(m.gehalt);
System.out.println(c.gehalt);
// ergibt 1100
// ergibt 2200
}
}
Mit dem Ausdruck super lässt sich nicht nur der Konstruktor der Oberklasse
aufrufen, sondern auch auch überschriebene Methoden. In dem Beispiel von
oben würde super.gehaltErhoehen(100); in der Klasse Chef die Methode der
Klasse Mitarbeiter aufrufen.
4.2
Modifier
Modifier sind eine Möglichkeit die Eigenschaften von Klassen, Methoden und
Variablen zu verändern. Vorallem beeinflussen Modifier die Sichtbarkeit von
Programmelementen. Sie erlauben eine Kontrolle darüber, inwiefern die abgeleiteten Klassen Zugriff auf die Elemente der Oberklasse haben.
• public : Elemente diesen Typs sind überall sichtbar. Sie können in der
eigenen Klasse und von Methoden beliebiger anderer Klassen benutzt werden. Auch Klassen können public sein. Allerdings darf in jeder Quelldatei
nur eine öffentliche Klasse stehen.
• protected : Methoden oder Variablen diesen Typs sind nur in der aktuellen Klasse und in von ihr abgeleiteten Klassen sichtbar. Außerdem sind
sie für Klassen aus dem gleichen Paket sichtbar.
4.3. STATIC
25
• package scoped : Wenn kein Modifier angegeben wird, gilt die Regel,
dass Klassen, Methoden und Variablen nur innerhalb ihres Pakets sichtbar
sind.
• static : Elemente mit diesem Attribut sind nicht an die Existenz einer
Instanz gebunden, d.h. sie existieren vom Laden einer Klasse bis zum
Beenden des Programms.
• final Membervariablen vom Typ final können nicht verändert werden.
Methoden mit diesem Typ dürfen nicht überschrieben werden und Klassen nicht zur Ableitung neuer Klassen benutzt werden. Solche Variablen
kann man als Konstanten betrachten. Es gibt die Konvention konstante
Variablen in Großbuchstaben zu benennen.
• Außerdem gibt es noch die Modifier transient und volatile. Sie sind für
die Serialisierung von Objekten und für die Programmierung von Threads
wichtig.
4.3
static
Wir haben schon verschiedene statische Membervariablen und Methoden kennengelernt z.B. ist out eine statische Membervariable von System. Die Klasse
Math besitzt verschiedene statische Methoden wie abs, sin oder cos.
Eine weitere Anwendung von statischen Klassenvariablen ist ein Instanzenzähler:
public class TestAuto {
public static int objcnt = 0;
public Testauto {
++objcnt;
}
public void finalize() {
--objcnt;
}
public static void main(String[] argv) {
Testauto auto1;
Testauto auto2 = new Testauto();
System.out.println("Anzahl Testauto-Instanzen: " + Testauto.objcnt);
}
}
Auch die main-Methode ist statisch definiert.
4.4
Abstrakte Klassen
Außerdem gibt es noch die Möglichkeit, abstrakte Methoden zu definieren. Eine
solche Methode bestitzt anstelle der geschweiften Klammern mit Anweisungen
lediglich ein Semikolon und das Attribut abstract. Eine Klasse mit abstrakten
Methoden wird insgesamt als abstrakt gekennzeichnet. Sie kann nicht instanziert
26
KAPITEL 4. DER VIERTE TAG
werden. Klassen, die sich von abstrakten Klassen ableiten müssen alle abstrakten
Methoden aus der Oberklasse implementieren oder sind selbst abstrakt.
abstract class Mitarbeiter {
String name;
public abstract double monatsBrutto();
// abstrakte Methode
}
class Arbeiter extends Mitarbeiter {
double stundenlohn;
double anzahlstunden;
public double monatsBrutto() {
return studenlohn * anzahlstunden;
}
// überschreibt monatsBrutto();
}
4.5
Interfaces
Ein Interface ist eine besondere Klasse, die ausschließlich abstrakte Methoden
und Konstanten enthält. Anstelle des Schlüsselwortes class verwendet man interface.
Man kann ein Interface als eine Art Schnittstelle auffassen, die bestimmte Eigenschaften für ihre Unterklassen definiert. Es gelten ähnliche Regeln wie für
abstrakte Klassen:
• Von einem Interface können keine Instanzen gebildet werden.
• Eine von einem Interface abgeleitete Klasse muss alle Methoden des Interface implementieren.
• Wenn die abgeleitete Klasse nicht alle Methoden implementiert, muss sie
als abstract definiert werden.
• Ein Interface kann dazu benutzt werden um Mehrfachvererbung zu
simulieren.
public interface Groesse {
public int laenge();
public int hoehe();
public int breite();
}
public class Auto implements Groesse {
public int laenge;
public int hoehe;
public int breite;
public int leistung;
public int lange() { return this.laenge; }
public int hoehe() { return this.hoehe; }
public int breite() { return this.breite; }
}
4.6. GRAPHISCHE AUSGABE MIT SWING
27
Interfaces sind die nächste Entsprechung zu Klassen in Haskell. Beispielsweise gibt es in Java das Interface Comparable mit der abstrakten Methode
compareTo. Dieses Interface kann von Klassen implementiert werden, die paarweise vergleichbar sind.
4.5.1
Mehrfachvererbung
Eigentlich ist in Java keine Mehrfachvererbung möglich, d.h. eine Klasse kann
immer nur von einer Oberklasse erben. Es ist allerdings möglich, dass eine Klasse
von mehreren Interfaces erbt. Diese muss dann allerdings alle abstrakten Methoden der Interfaces implementieren, oder wird als abstract deklariert.
Die Auto-Klasse von oben könnte z.B. die Interfaces Groesse und Comparable
implementieren.
public class
public int
public int
public int
punlic int
Auto implements Groesse, Comparable {
laenge;
hoehe;
breite;
leistung;
public int lange() { return this.laenge; }
public int hoehe() { return this.hoehe; }
public int breite() { return this.breite; }
public int CompareTo(Object o) {
int ret = 0;
if (leistung < ((Auto)o).leistung) {
ret = -1;
} else if (leistung > ((Auto)o).leistung) {
ret = 1;
}
return ret;
}
}
4.6
Graphische Ausgabe mit Swing
Ein großer Vorteil von Java ist, dass Methoden zur Erzeugung von graphischen
Oberflächen frei Haus mitgeliefert werden. Es gibt zwei Bibliotheken für die Erzeugung von Fenstern, Buttons etc. Die ältere heisst AWT (Abstract Window
Toolkit). Sie ist etwas langsam und hat deshalb einen schlechten Ruf unter Programmiern. Dies war der Grund für die Entwicklung von Swing. Swing ist eine
Sammlung von Klassen, die alles bieten was man zur Gestaltung von graphischen Oberflächen braucht. Leider ist diese Bibliothek sehr umfangreich. Eine
Einführung würde einen eigenen Kurs füllen. Deshalb nur ein kurzes Beispiel
wie man Dialoge und Eingabefelder mit Swing erzeugen kann:
28
KAPITEL 4. DER VIERTE TAG
import javax.swing.JOptionPane;
public class SwingDemo {
public static void main(String[] args) {
String input = JOptionPane.showInputDialog("Bitte geben sie etwas ein: ");
JOptionPane.showMessageDialog(null,"Danke! Sie haben eingegeben: " + input);
}
}