Eine Methode (auch: Funktion, Prozedur, Operation, Routine, Unterprogramm) kann man sich anschaulich vorstellen als ein wiederverwendbares Programmstück. Eine Methode kann beliebigen Code enthalten und von beliebigen Stellen aus beliebig oft aufgerufen werden. Ein Beispiel für eine Methode ist System.out.println(String)
, die Text auf dem Bildschirm ausgibt. Methoden kann man auch selbst erstellen.
Die Methoden sort(int[])
auf den Seiten 33 und 35 in Kapitel 4 des Skript sind beides Implementationen des Selection-Sort–Algorithmus. Es besteht kein nennenswerter Unterschied zwischen den beiden. Beide Methoden sortieren den übergebenen Array aufsteigend. Auf die Funktionsweise dieses und anderer Sortieralgorithmen geht das Kapitel „Sortieren“ der Vorlesung „Algorithmen und Datenstrukturen“ ein.
public static void main (String[] args)
andere Zeilen wie public static int[] einlesen ()
?Bei diesen Zeilen handelt es sich um so genannte „Methodensignaturen,“ also Definitionen des Namens einer Methode sowie deren Parameter, Rückgabewert etc. An sie schließt in der Regel ein Block ({…}
) an, in dem der Algorithmus der Methode in Java beschrieben ist.
In Klassen kann man beliebig viele Methoden schreiben. Die Reihenfolge, in der die Methoden dabei geschrieben werden, ist egal.
Methoden werden gerne eingesetzt, um längere Algorithmen sinnvoll in kleinere Abschnitte zu unterteilen (sog. Top-Down–Verfahren). Vor allem aber dienen Sie der Vermeidung von Redundanzen. In diesem Fall wird die einlesen
-Methode nur einmal aufgerufen, so dass es sich wohl nur um eine sinnvolle Aufteilung des Gesamtproblems in Teilprobleme handelt. Eine Methode zum Sortieren dagegen kann man an vielen Stellen brauchen, denn Sortiert werden muss immer irgend etwas; daher dient die sort
-Methode in den Beispielen im Kapitel 4 durchaus auch der Vermeidung von Redundanzen.
public static void main
und public static void sort
– bzw. wann wende ich was an?Beides sind Teile von Methodendeklarationen (so genannten „Signaturen“). Der Unterschied ist allein der Name. Methodennamen sind (in bestimmten Grenzen) frei wählbar.
Die Methode mit der Signatur public static void main (String[])
ist diejenige, die beim Start der Klasse durch Eingabe von java Klassenname
im Terminal aufgerufen wird. sort
heißt eine Methode aus dem Skript, die den Inhalt eines übergebenen Arrays sortiert.
String[] args
,“ die in den Klammern der main-Methoden stehen?Es handelt sich dabei um eine Folge von Zeichenketten, die beim Programmstart an die main
-Methode übergeben werden. Dieses Array enthält alles, was beim Startbefehl vom Terminal aus hinter dem Programmnamen angehängt wird.
Versuche mal, den gesamten Inhalt des Arrays args
mit einer Schleife auszugeben, und ruf’ dann das Programm mit java Klassenname irgendein beliebiger Text
auf!
In Java haben Blöcke (von geschweiften Klammern { } eingeschlossene Code-Abschnitte) immer einen eigenen Namensraum für die Bezeichner von Variablen. Die in der Methodensignatur definierten Parameternamen gelten nur im Namensraum innerhalb der Methode.
Im Gegensatz dazu können Methoden nicht nur mit Variablen, sondern mit beliebigen Werten aufgerufen werden. Im Hallo-Welt–Programm wird z. B. die println
-Methode mit dem Wert "Hello world."
aufgerufen.
Im Beispiel der println
-Methode kannst Du sehen, dass die Namen der Parameter eigentlich recht egal sind. Die println
-Methode hast nämlich nicht Du geschrieben, sondern jemand anders. Nur weil der den Parameter x
genannt hat, bedeutet nicht, dass Du das auch tun solltest.
Wenn Du aber tatsächlich sowohl die Methode selbst schreibst als auch sie selbst aufrufst und dabei Variablen verwendest, bietet es sich natürlich oft an, den gleichen Namen für beides zu verwenden. Ob unterschiedliche Namen sinnvoll sind, hängt von der jeweiligen Situation ab.
In Methodensignaturen und Methodenaufrufen werden mehrere Argumente (Paramter) immer durch Kommata abgetrennt. Strichpunkt stehen lediglich nach dem Methodenaufruf. Beispiel: Circle.bigger(a, b);
(mit a
und b
vom Typ Circle
)
void
?Für alle Methoden (abgesehen von dem, was Dr. Bürg „Konstruktormethode“ nennt) muss in deren Signatur („Deklaration“) der Typ des Rückgabewerts festgelegt werden. Bei Methoden, die keinen Wert mit return
zurückgeben sollen, wird an dieser Stelle das Schlüsselwort void
(engl. Leere, Nichts) gesetzt. Es ist also quasi der Datentyp eines nicht existierenden Werts.
[]
) hinter int
?Leere eckige Klammern ([]
) hinter Datentypen bilden einen Arraydatentyp, dessen einzelne Elemente von eben diesem Typ (hier: int
) sind. Der Typ des Rückgabewerts bzw. Parameters ist hier int[]
, also ein Array aus lauter Werten vom Typ int
.
[]
in der Zeile public static void main (String[] args)
einmal vor dem args
und einmal nach dem args
?Die Position ist in diesem Fall von der Java Language Specification nicht vorgeschrieben und ist damit egal. Man darf sich also entsprechend des persönlichen Stils aussuchen, wo man’s hinschreibt. Im Allgemeinen möchte ich die Variante vor dem Parameternamen empfehlen, weil die eckigen Klammern streng genommen Teil des Typs sind (ein String-Array, String[]
).
Ja. Weil Arrays eine verschieden große Anzahl von Elementen haben, die alle indiziert (also mit einer laufenden Nummer versehen) sind, werden häufig for-Schleifen verwendet, um Arrays zu durchlaufen (d. h. nacheinander jedes Element des Arrays zu behandeln). Genau das aber geht mit jeder beliebigen Art von Schleife. Im Tutorium hatte ich schon mal erwähnt, dass for-Schleifen und while-Schleifen eigentlich das gleiche sind. Ein Array-Durchlauf ist aber auch mit einer do-while-Schleife kein Problem (wenn auch ein klein wenig umständlicher, weil man den Fall eines leeren Arrays abfangen muss).
Es gibt im Übrigen sogar eine Technik, mit der man Arrays auch ganz ohne Schleifen durchlaufen kann: die Rekursion. Die wird aber (leider) nicht im Skript behandelt.
double…[] = new double[…];
und die selbe Zeile mit anderen Bezeichnungen als double
?Es wird ein neuer Array mit einer bestimmten Länge erzeugt (new double[…]
) und einer neuen Referenz mit einem bestimmten Namen (double…[]
) zugewiesen (=
). Dabei ist double
der Typ des Arrays und der Referenz. Statt dessen können auch alle anderen Datentypen verwendet werden, die dann eben einen Array des betreffenden Typs beschreiben.
l = s
l[i] = s[i]
Vom Wert her gibt es keinen Unterschied; beides sind Zuweisungen (keine Eingabemöglichkeiten!). Bei (a) wird der Wert einer Variablen s einer zweiten Variable l zugewiesen, bei (b) wird der Wert eines Arrayelements si einem zweiten Arrayelement li zugewiesen.
Codebeispiel: int[] a = {1.0};
Es geht schlichtweg nicht, sondern führt zu einem Compile-Zeit–Fehler (possible loss of precision), weil der Java-Compiler versucht, vor dem unbeabsichtigten Verlust der Nachkommastellen zu schützen. Bei normalen Wertzuweisungen (etwa a[0] = 1.0;
kann man durch einen Type-Cast die Zuweisung erzwingen, wobei die Nachkommastellen weggeschmissen werden (also a[0] = (int)1.0;
). Beim Initialisieren eines Arrays ist das aber nicht möglich; da kann man ja auch stattdessen viel einfacher den gewünschten int-Wert direkt angeben.
Eine zweidimensionale Matrix kann in Java z. B. gut mit einem zweidimensionalen Array implementiert werden. Dann ist die Matrix zweidimensional und aus verschiedenen Arrays zusammengesetzt; es besteht also kein Unterschied.
Für alles, bei dem eine unbestimmte Anzahl von Variablen für gleichartige Werte benötigt wird. Beispiele: Zahlenfolgen, Matrizen, Sammlungen von beliebigen Objekten, Menüs in graphischen Benutzeroberflächen (für die Liste der Menüpunkte), Listen von Datensätzen einer Datenbank etc. pp.
Vergleiche auch die Kapitel „Statische Informationsstrukturen“ und „Dynamische Informationsstrukturen“ der Vorlesung „Algorithmen und Datenstrukturen“ im letzten Semester.
Das Sortieren eines Arrays von Hand ist unmöglich; man weiß vorher nicht, welche Zahlen im Array enthalten sind, und kann folglich keinen Algorithmus schreiben, der diese (unbekannten) Zahlen in die (unbekannte) richtige Reihenfolge bringt. Außerdem wäre ohnehin der Aufwand für das Sortieren von Hand unüberschaubar groß, da ja die Größe des Arrays unbekannt und im Prinzip auch unbeschränkt ist; eine Millarde Zahlen etwa kann niemand von Hand sortieren. Für einen guten Sortieralgorithmus ist das aber ein Klacks.
Der Inhalt ist jeweils der gespeicherte (oder zu speichernde) Wert, genau wie bei einer einfachen Variable. Weil in einem Array mehrere Werte gespeichert werden können, benötigt man eine Möglicheit zur Unterscheidung dieser Werte; dies ist der Index.
Ähnlich wie in der Mathematik kann man die verschiedenen Arrayelemente als einzelne, durch eine laufende Nummer unterschiedene Variablen ansehen. Der Index wäre dabei ein Bestandteil der Variablennamen.
Mathematische Schreibweise: ai, Java-Schreibweise: a[i]
Man kann die Arraygrenzen nicht überschreiten, weil man es nicht nicht darf. Java versucht alles, was verboten ist, zu verhindern; es erscheint dann eine Fehlermeldung (hier ArrayIndexOutOfBoundsException).
(Nicht alle Sprachen verhindern Verbotenes. Vor allem C ist dafür berüchtigt, prinzipbedingt überhaupt keine Überprüfung von Arraygrenzen durchzuführen. Das Programm pfuscht dann unter Umständen wild im RAM herum und kann Datenverluste, Abstürze und Schlimmeres verursachen.)
Man darf die Arraygrenzen nicht überschreiten, weil bei der Deklaration des Arrays nur eine begrenzte Menge an Speicherplatz für den Array festgelegt wurde.
(In Java sind Arraygrößen nicht veränderbar. Es gibt aber in Java andere Datenstrukturen, deren Größe beliebig verändert werden kann, etwa die des Collection-Frameworks im Paket „java.util“ (vgl. API-Dokumentation). Vergleiche auch das Kapitel „Dynamische Informationsstrukturen“ der Vorlesung „Algorithmen und Datenstrukturen.“)
Vermutlich meinst Du „überschreiten.“ Mit dem „Überschreiten von Arraygrenzen“ ist gemeint, auf einen Array mit einem ungültigen Index zuzugreifen. Beim Erzeugen eines Arrays wird immer die Anzahl der Elemente festgelegt, und das Zählen der Indizes beginnt immer mit null. Alle Indizes, die kleiner als null oder größer als die Anzahl der Elemente des Arrays sind, sind ungültig. Beispiel: Nach der Deklaration int a = new int[2];
ist der Zugriff auf a
mit a[5]
unerlaubt, da der Array nur zwei Elemente hat (nämlich a[0]
und a[1]
).
Das Überschreiten der Arraygrenzen ist eine so genanne Laufzeitausnahme-Situation, in der automatisch eine „Exception“ geworfen wird (hier: ArrayIndexOutOfBoundsException
). Dies ist nichts Schlimmes und sehr oft sogar erwünscht, denn die geworfenen Exceptions kann man an anderer Stelle schön gezielt abfangen (try
/ catch
) und gehörig behandeln (vgl. Kapitel 7). Daher ist es häufig nicht nötig (und manchmal auch gar nicht sinnvoll), eine Ausnahmesituation in Java zu verhindern.
Möchte man das Werfen der Exception dennoch verhindern, so muss schon das Auftreten der Ausnahmesituation verhindert werden, da Java diese so genannten RuntimeException
s automatisch wirft. Es müsste also der Wert des Arrayindizes mit if (index < grenze) …
geprüft werden, bevor tatsächlich damit auf den Array zugegriffen wird.
Ein String ist intern als nicht veränderlicher Array von Buchstaben (char[]
) implementiert. Es können also so viele Zeichen gespeichert werden, wie der Array lang sein kann (jedenfalls ungefähr, vgl. UTF-16 in Unicode 4.0 Kapitel 3, S. 76 f.).
So weit ich das sehe, ist in der Java Language Specification die Länge eines Arrays nicht definiert. Damit dürfte die maximale Länge eines Strings also nur durch den zur Verfügung stehenden Speicher abhängen. Sehr lange Strings sind jedoch vermutlich äußerst ineffizient und sollten daher vermieden werden.
In Java sind Arraygrößen grundsätzlich nicht veränderbar. Eine mögliche (aber naïve) Lösung wäre es, bei einer Änderung des Mitarbeiterbestands jedesmal einen neuen Array mit passender Länge anzulegen und die einzelnen Mitarbeiter in den neuen Array zu übernehmen.
Sinnvolle Ansätze für das Speichern von Datenmengen unbekannter Größe behandelte das Kapitel „Dynamische Informationsstrukturen“ der Vorlesung „Algorithmen und Datenstrukturen“ im letzten Semester. Anbieten würde sich für den Fall der Mitarbeiterliste eine Lineare Liste, besser noch eine Baumstruktur.
Es gibt aber auch direkt in Java geeignetere Datenstrukturen, deren Größe beliebig verändert werden kann, etwa die des Collection-Frameworks im Paket „java.util“ (vgl. API-Dokumentation).
byte byteDimArray [][] = new Byte[][16]
nicht zulässig?Dafür gibt es gleich zwei Gründe:
Weil Java Groß- und Kleinschreibung unterscheidet (also „case-sensitive“ ist), sind byte
und Byte
zwei verschiedene Datentypen. Konkret handelt es sich bei byte
um den primitiven Typ für ganze Zahlen im Intervall [−128, 127], während Byte
die gleichnamige Klasse im Paket „java.lang“ ist. Dies ist ein Fehler im Skript; es müsste darin eigentlich byte byteDimArray [][] = new byte[256][16]
heißen (analog auch in den anderen Beispielen dort).
Mehrdimensionale Arrays sind eigentlich geschachtelte Arrays. Mit new byte[256][16]
wird also ein neuer Array (Typ: byte[][]
) mit 256 Elementen erzeugt, von denen jedes einzelne nichts anderes enthält als jeweils wiederrum einen Array mit 16 Elementen. Jedes der 256 „Unter-Arrays“ ist damit vom Typ byte[]
, während jedes der darin jeweils enthaltenen 16 Elemente den Typ byte
hat.
Arrays müssen nicht „rechteckig“ sein, d. h. es ist möglich, die Größen der einzelnen „Unter-Arrays“ individuell zu variieren. Dazu kann man erst den „Haupt-Array“ mit den 256 Elementen erstellen und später die 256 „Unter-Arrays“ einzeln erzeugen und einfügen. Das geht dann mit dem Ausdruck new byte[256][]
; da der Typ des Arrays zweidimensional sein soll, hier aber eigentlich ja nur ein eindimensionaler Array erzeugt werden würde, wird der Datentyp durch Anhängen eines leeren eckigen Klammerpaars ([]
) auf einen Array von Arrays geändert.
Der Ausdruck new byte[][16]
ist ungültig, weil dabei versucht wird, „Unter-Arrays“ zu erzuegen, ohne einen „Haupt-Array“ zu haben, in den Referenzen zu den „Unter-Arrays“ abgelegt werden könnten.
Da mehrdimensionale Arrays eigentlich geschachtelte Arrays sind und diese wiederrum nichts anderes als Objekte sind, die auf andere Objekte verweisen, sind der Anzahl der Dimensionen durch Java selbst keine Grenzen gesetzt.
Neben dem zur Verfügung stehenden Arbeitsspeicher besteht bei Arrays mit sehr vielen Dimensionen allerdings das Problem, dass die entsprechenden Teile des Quellcodes durch unzählige eckige Klammern stark aufgeblasen werden. Auch heute noch haben viele Programme und Betriebssysteme Schwierigkeiten, mit Dateien mit mehr als 2 GiB zu arbeiten, so dass vermutlich Arrays mit mehr als ein paar hundert Millionen Dimensionen problematisch sind. ;-) Ich habe das aber nie ausprobiert. So weit ich das sehe, ist in der Java Language Specification auch die Länge eines Arrays nicht definiert. Damit hängt das Maximum der Größe von Arrays ausschließlich von der Umgebung ab.
Definiert wird dies durch die Schreibrichtung der abendländischen Kultur. Wir schreiben in Zeilen von links nach rechts und fügen weitere Zeilen von oben nach unten hinzu. Mehrdimensionale Arrays sind eigentlich geschachtelte Arrays, d. h. der erste Array enthält mehrere zweite (jeweils einen pro Zeile).
Dies Zuordnung gilt übrigens nur dann, wenn die Arrays auf dem Bildschirm mit zwei geschachtelten Schleifen und der Methode System.out.print
ausgegeben werden. Andernfalls können die Array-Inhalte durchaus auch in anderer Reihenfolge ausgelesen und ausgegeben werden.
i
und j
vertausche?Allgemein wird beim Vertauschen von Arrayindizes entweder gar nichts passieren (wenn man wirklich alle Indizes austauscht) oder es wird wahrscheinlich ein Problem geben, weil der Code nicht mehr das tut, was er soll. Möglicherwiese kommt es zu Laufzeitfehlern.
Einige Variablentypen können nur „by reference“ kopiert werden, andere nur „by value.“ Alle primitiven Typen (z. B. int
und double
) können nur „by value“ kopiert werden, alle anderen dagegen nur „by reference.“ Zu diesen anderen zählen also alle Arrays (etwa int[]
) genau so wie alle auf Klassen basierenden Typen (z. B. String
). Vergleiche Skript Kapitel 4 Seite 16.
Beim Zuweisen „by value“ wird der Wert (engl. value) kopiert, das heißt, dass nach der Zuweisung zwei Variablen existieren, die den gleichen Wert haben.
Beim Zuweisen „by reference“ wird nicht der eigentliche Wert, sondern nur die Referenz (also der „Zeiger“) auf diesen Wert kopiert, so dass nach der Zuweisung zwei Variablen existieren, die auf denselben Wert verweisen. Wird der Wert über eine der Variablen verändert, so kann man auf den geänderten Wert auch über die andere Variable zugreifen.
==
vergleichen?Man kann Strings mit ==
vergleichen. Da Strings aber Objekte sind, vergleicht man dann auf Identität und nicht auf gleichen Inhalt. Es wird also die „reference“ verglichen, nicht der „value.“ Beispiel mit Circle aus dem Skript (gleiches Prinzip, aber einfacher zu erklären):
Circle a = new Circle(); a.r = 5; Circle b = a; // Zuweisung by reference System.out.println(a == b); // true System.out.println(b.r); // 5 a.r = 2; System.out.println(b.r); // 2
Hier existiert nur ein Circle
-Objekt – schließlich wird ja auch der Konstruktur nur einmal aufgerufen! a
und b
zeigen auf dieselbe Instanz, folglich sind Änderungen unabhängig von der Referenz (a
oder b
), die zum Zugriff auf die Instanz verwendet wird, immer wirksam und sichtbar.
String a = "test"; // gleichwertig mit …= new String("test"); String b = "test"; // gleichwertig mit …= new String("test"); System.out.println(a == b); // false
Für Strings, die mit Anführungszeichen direkt in den Code geschrieben werden ("test"
), wird immer automatisch von Java eine neue Instanz erstellt, so als ob man den Konstruktor der String-Klasse aufrufen würde. Daher werden hier eigentlich zwei (versteckte) „Konstruktoren“ aufgerufen, also auch zwei verschiedene Instanzen erstellt. Und die können trotz „zufälligerweise“ gleichem Inhalt nicht identisch sein, daher liefert a == b
hier immer false
zurück.
(Anmerkung: Diese Antwort ignoriert String-Internierung, die in Java konstante Strings vergleichbar macht; vgl. JLS2 3.10.5:7 und String#intern().)
Indem zunächst die Klassenstruktur mit ihren Variablen und Methoden allgemein beschrieben wird (vgl. Vorlesung „Algorithmen und Datenstrukturen“) und anschließend die einzelnen Methoden jeweils für sich in natürlicher Sprache beschrieben werden.
Einige Datentypen sind zueinander kompatibel; diese kann man ineinander „casten,“ indem man den Zieldatentyp in runden Klammern vor den umzuwandelnden Wert schreibt. Im folgenden Beispiel ist also (int)
der Type-Cast:
double kommazahl = 3.8;
int ganzzahl = (int)kommazahl;
Im Beispiel wird die Zahl 3,8 in eine Ganzzahl konvertiert. Die Variable ganzzahl
enthält hinterher den Wert 3.
Nicht unmittelbar zueinander kompatible Typen muss man „per Hand“ umwandeln. Die Vorgehensweise ist entscheidend von den jeweiligen Typen abhängig. Beispielsweise kann die Methode Integer.parseInt(String)
einen als Parameter übergebenen Wert vom Typ String
in den Typ int
konvertieren. In vielen Fällen gibt es aber noch keine passende Methode zum Konvertieren; die muss man sich dann selber schreiben.
Integer.parseInt
aus der Klasse Utils
?Streng genommen wird die Methode parseInt(String)
in der Klasse Integer
definiert. Integer
ist Bestandteil von Java und in der API-Dokumentation beschrieben (im Paket „java.lang“). Utils
verwendet Integer.parseInt(String)
, um eine in Textform vorliegende Zahl in eine Zahl vom Typ int
umzuwandeln, mit der man dann auch rechnen kann.
public
ermöglicht den Zugriff auf das Programm auch aus anderen Paketen heraus. Pakete werden bei größeren Softwareprojekten in Java zur Codeorganisation eingesetzt; in dieser Vorlesung benötigten wir sie noch nicht. Dennoch möchte ich aus bestimmten Gründen empfehlen, vorerst alle Klassen, Methoden etc. public
zu deklarieren. Vergleiche Skript Kapitel 6, Seite 36.
Ja (mit der Ausnahme von sog. „inneren Klassen,“ die Ihr aber frühestens im nächsten Semester behandelt).
Keine, sofern die Dateinamen mit den Klassennamen übereinstimmen (wie immer einschließlich Groß- und Kleinschreibung) und alle Dateien im selben Verzeichnis liegen. Um Quelltextdateien auf mehrere Verzeichnisse aufzuteilen, muss mit Paketen und Sichtbarkeitsmodifikatoren gearbeitet werden (vgl. Skript Kapitel 6, Seite 36 f.).
nums[i] = Math.random() * 100
in Zeile 5?Math.random()
erstellt eine Pseudo-Zufallszahl im Intervall [0, 1). Math.random() * 100
erweitert dieses Intervall auf [0, 100). Mit =
wird diese Zufallszahl dann der Variablen nums[i]
zugewiesen, wobei nums
ein Array ist und i
der Index desjenigen Array-Elements, dessen Wert durch die Zufallszahl ersetzt werden soll.
Die gesamte for-Schleife sorgt also dafür, dass der gesamte Array nums
mit Zufallszahlen im Bereich zwischen 0 und 100 gefüllt wird.
System.arraycopy(a, 0, b, 0, a.length);
? Und was bedeutet sie?Bei dieser Zeile handelt es sich um den Aufruf einer Klassenmethode aus der Klasse System
, die Bestandteil von Java ist. An dieser Stelle der Vorlesung wurde eine einfache Art und Weise benötigt, einen Array „by value“ zu kopieren. Das kann man auch einfach selbst machen, indem man mit einer Schleife alle Elemente des Arrays durchläuft und jedes einzeln „by value“ kopiert. In diesem Fall hat Dr. Bürg jedoch statt dessen auf eine bereits existierende Methode, die intern genau das macht, zurück gegriffen. Er wusste bereits, dass eine solche Methode in der Klasse System
existiert.
Um selbst so eine Methode zu finden, muss man in der Dokumentation der Java-API suchen. In deren Index kann man durch geschicktes Suchen die arraycopy-Methode durchaus finden. Etwas einfacher ist es, gezielt auf der Website von Sun mit Google zu suchen. So oder so gelangt man schließlich in die Dokumentation der Klasse System
, in deren alphabetischer Methodenliste dann ganz oben die arraycopy-Methode steht.
Dort ist die Arbeistweise der Methode mitsamt der Bedeutung der Parameter (Argumente) genau erklärt. Kurz gefasst wird ein Teil des im ersten Parameter übegebenen Arrays (hier a
) in den Array im dritten Parameter (b
) kopiert. Der kopierte Teil beginnt bei der durch den zweiten Parameter angegeben Stelle in a
(0
) und wird in b
beginnend an der Stelle des vierten Parameters eingesetzt (0
). Die Anzahl der zu kopierenden Elemente wird als letzter Parameter übergeben (hier a.length
).
Folglich wird in dieser Zeile im Skript der gesamte Arrayinhalt von a
in den Array b
kopiert.
if (i == j) matrix[i][j] = 1;
?==
ist in Operator, der zwei Werte auf Gleichheit prüft und dessen Ergebnis („gleich“ oder „ungleich“) den Typ boolean
(Wert true
oder false
) hat. ==
gehört damit zu den Vergleichsoperatoren (wie auch etwa <
oder >=
). Eine Liste einiger gängiger Operatoren befindet sich im Skript im Kapitel 3 auf Folie 5; entgegen der Überschrift enthält die Liste aber nicht nur arithmetische, sondern auch andere Operatoren.
Zu beachten ist der Unterschied zwischen dem Vergleichen „by reference“ und „by value.“ Außerdem darf ==
nicht mit dem Zuweisungsoperator =
verwechselt werden! Beispiel:
int a = 0; System.out.println(a == 0); // true System.out.println(a == 1); // false System.out.println(a); // 0 System.out.println(a = 1); // 1 System.out.println(a); // 1
Das Ergebnis des Ausdrucks a = 1
ist also nicht vom Typ boolean
, sondern nimmt Typ und Wert der Variablen an. Üblicherweise steht eine Zuweisung wie a = 1;
als eigene Anweisung in einer eigenen Zeile. Sie kann jedoch auch an anderen Stellen stehen, was gelegentlich zu Verwirrung führt.
main
-Methode, was ist die sort
-Methode?Die main
-Methode ist diejenige Methode, die beim Programmstart mit java ArraySortMeth
aufgerufen wird. Die sort
-Methode sortiert den übergebenen Array aufsteigend mit Hilfe des Selection-Sort–Algorithmus (vgl. Kapitel „Sortieren“ der Vorlesung „Algorithmen und Datenstrukturen“).
short
und triangle
und wie komme ich darauf?short
ist ein Schlüsselwort, das für einen bestimmten primitiven Datentyp (ähnlich int
, den man hier stattdessen hätte wählen können) steht. Möchte man Zahlen in Variablen speichern, so muss immer darauf geachtet werden, dass der Zahlenbereich des Variablytyps ausreichend ist; bei int
hat man da erfahrungsgemäß nur selten Probleme. Vergleiche Skript Kapitel 1 Folie 1.
triangel
ist im Skript der Name eines (zweidimensionalen) Arrays aus Zahlen vom Typ short
. Variablennamen sind (in bestimmten Grenzen) frei wählbar.
Welche mathematischen Operationen die Sprache Java beherrscht, ist in der Java Language Specification im Kapitel über Typen, Werte und Variablen festgelegt. Einige davon sind auch im Skript enthalten. Auf Fließkommazahlen können weniger Operatoren als auf Ganzzahlen angewandt werden.
Darüber hinaus unterstützt die Entwicklungsumgebung (genauer: das „Framework“) Java eine Reihe weiterer Operationen; diese sind in der API-Dokumentation zu finden (interessant sind vor allem die Klassen Math
sowie BigInteger
und BigDecimal
im Paket „java.lang“). Da sich aber alle numerischen mathematischen Funktionen und Operationen auf die „Grundrechenarten“ zurückführen lassen, die die Sprache Java unterstützt, kann man letztlich beliebige numerische Operationen mit Java ausführen – einige davon muss man Java aber erst durch Schreiben einer entsprechenden Methode selbst beibringen.
In der Sprache Java gibt es keine Matrizen; eine passende Datenstruktur muss man selbst entwickeln, etwa einen zweidimensionalen Array. Auf Arrays lassen sich aber keine der mathematischen Operatoren von Java anwenden, so dass man selbst Methoden für das Rechnen mit Matrizen schreiben muss. Das ist aber eigentlich nicht sonderlich schwierig (wäre eine nette Übungsaufgabe!). Auf diese Weise kann man Java leicht beibringen, mit Matrizen zu rechnen.
Math.random()
erstellt eine Pseudo-Zufallszahl im Intervall [0, 1). Math.random() * 10
erweitert dieses Intervall auf [0, 10). In den Sortier-Beispielen werden Arrays von Ganzzahlen sortiert, wofür Zufallszahlen im Intervall [0, 1) ungeeignet sind; daher muss dieses Intervall vergrößert werden. Letztenendes ergeben sich dann Zufalls-Ganzzahlen im Bereich zwischen 0 (einschließlich) und 10 (ausschließlich).
Frei übersetzt aus dem New Oxford American Dictionary:
• einen String oder Text analysieren und in logische oder syntaktische Teile zerlegen, häufig um auf Übereinstimmung mit einer logischen Grammatik hin zu prüfen
Im Falle von Integer.parseInt(String)
geht es also darum, die einzelnen Ziffern aus dem übergebenen String zu extrahieren und sinnvoll (also entsprechend der üblichen dezimalen Arithmetik) zu einer Zahl, mit der man dann später was rechnen kann, zusammenzufügen.
Nein. Allerdings ist es wahrscheinlich möglich, die Fähigkeiten des Terminals mit terminfo(5)
oder so abzufragen und dann irgendwelche Codes an System.out
auszugeben, die dann das Terminalfenster „löschen.“ Ich habe aber keine Ahnung, wie das konkret geht. Fragt vielleicht nächstes Semester mal in der Vorlesung „Betriebssysteme“…
Eine for-Schleife besteht aus der Deklaration (beispielsweise for ( ; ; )
) und dem zu wiederholenden Code. Um bei mehreren zu wiederholenden Codezeilen deren zugehörigkeit zur Schleife zu kennzeichnen, müssen diese in einen Block ({ … }
) gesetzt werden; bei einer einzelnen Anweisung ist das nicht unbedingt nötig.
Man braucht die geschweiften Klammern bei for-Schleifen also nur dann, wenn mehr als eine Anweisung wiederholt werden soll. Es ist trotzdem dringenst zu empfehlen, bei allen Schleifen, Bedingungen etc. immer geschweifte Klammern zu schreiben! Tut man das nicht, ist der Code nicht nur uneinheitlicher und damit unübersichtlicher, sondern vor allem viel schwieriger zu verändern, weil sich der Bearbeiter beim Hinzufügen von Zeilen nun plötzlich auch über eventuell fehlende Klammern Gedanken machen muss.
int i;
zu initialisieren und ihr in einer for-schleife erst den Wert zuzuweisen?Mit int i;
wird die Variable i
ja gerade eben nicht initialisiert! Das ist nur eine reine Deklaration. „Initialisieren“ bedeutet „einen Anfangswert zuweisen.“
Grundsätzlich kann man durchaus auch nicht initialisierten Variablen erst in einer Schleife einen Wert zuweisen. Allerdings darf man dann nicht lesend auf die Variable zugreifen, solange nicht absolut sicher ist, dass ihr zuvor tatsächlich ein Wert zugewiesen worden ist. Das kann gerade bei Schleifen und Bedingungen manchmal zu Verwirrung führen, weswegen es keine schlechte Idee ist, alle Variablen gleich bei der Deklaration zu initialisieren.
i < Arraylänge
wiederholt?Weil man die obere Grenze des Arrays nicht überschreiten kann und darf und weil die Zählung des Indizes bei null beginnt, muss es bei Arraydurchläufen eine Abbruchbedingung geben, die den Zugriff auf den Array mit einem Index größer oder gleich der Arraylänge verhindert.
Es gibt noch mindestens drei alternative Bedingungen neben i < Arraylänge
, die dieses Ziel ebenfalls erreichen. Edsger Wybe Dijkstra hat schon 1982 die Vor- und Nachteile dieser Varianten kurz gegenüber gestellt und kam zu dem Schluss, dass i < Arraylänge
tatsächlich die einzig sinnvolle Art und Weise ist. Vergleiche E. W. Dijkstra, Why numbering should start at zero, Nuenen 1982, Edsger W. Dijkstra Archive #831 (PDF).