Zum Inhalt springen

Java-Syntax

aus Wikipedia, der freien Enzyklopädie
Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 2. September 2005 um 09:20 Uhr durch Jpp (Diskussion | Beiträge) (Ausdrücke: Formatierung). Sie kann sich erheblich von der aktuellen Version unterscheiden.

Die Java-Syntax, also die Syntax der Programmiersprache Java ist, ebenso wie deren Semantik, in der Java Language Specification definiert.

Im folgenden soll nur ein grober Überblick über die Java-Syntax gegeben und deren Besonderheiten herausgestellt werden, für die Details sei auf die Java-Sprachspezifikation von SUN Microsystems und das WikiBook Java verwiesen.

Lexikalische Struktur

Java-Programme werden in Unicode geschrieben. Im Gegensatz zu anderen Sprachen können Bezeichner (englisch Identifier) von Klassen, Methoden, Variablen usw. nicht nur die Buchstaben des lateinischen Alphabets und Ziffern enthalten, sondern auch Zeichen aus anderen Alphabeten, wie z. B. deutsche Umlaute oder chinesische Schriftzeichen.

Sämtliche Schlüsselworte in Java werden klein geschrieben, z. B. "class" oder "if".

Buchstabensymbole (englisch literals) sind in Java die kleinstmöglichen Ausdrücke für Zahlen, einzelne Zeichen, Zeichenketten (Strings), logische Werte (true oder false) und das spezielle Wort null.

Trennzeichen (englisch separators) sind verschiedene Arten von Klammern sowie Komma, Punkt und Semikolon.

Java kennt die in Programmiersprachen üblichen logischen, Vergleichs- und mathematischen Operatoren. Die Syntax orientiert sich dabei an der Programmiersprache C++. So dient zum Beispiel das einfache Gleichheitszeichen "=" als Zuweisungsoperator, während für Vergleiche das doppelte Gleichheitszeichen "==" verwendet wird.

Leerzeichen, Zeilenenden und Kommentare können an beliebigen Stellen zwischen den Bezeichnern, Schlüsselworten, Buchstabensymbolen, Trennzeichen und Operatoren eingefügt werden. Eine Besonderheit von Java sind die so genannten Dokumentationskommentare, die mit "/**" beginnen und mit "*/" abgeschlossen werden. Diese Dokumentationskommentare werden von dem Werkzeug Javadoc ausgewertet, um aus Java-Quelltext z. B. Dokumentationsdateien in HTML zu generieren.

Syntax

Datentypen

Java kennt zwei Datentyparten: primitiver Datentyp und Referenz. Erstere gehört zu den Gründen, die Java strenggenommen von einer reinen objektorientierten Sprache unterscheidet.

Primitive Datentypen

Es gibt acht primitive Datentypen. Sie haben unterschiedliche Größen und Eigenschaften und werden zum Berechnen und Speichern diskreter Zahlenwerte benutzt. Sie sind ebenfalls plattformunabhängig, weil sie stets im Big Endian-Format gespeichert werden. Für jeden Typen existiert eine entsprechende Wrapper-Klasse, um auch diese als echte Objekte behandeln zu können.

Datentyp Größe¹ Wrapper-Klasse Wertebereich Beschreibung
boolean 8 bit java.lang.Boolean true / false Boolescher Wahrheitswert
char 16 bit java.lang.Character 0 ... 65.535 (z. B. 'A') Unicode-Zeichen (UTF-16)
byte 8 bit java.lang.Byte −128 ... +127 Zweierkomplement-Wert
short 16 bit java.lang.Short −32.768 ... +32.767 Zweierkomplement-Wert
int 32 bit java.lang.Integer −2.147.483.648 ... +2.147.483.647 Zweierkomplement-Wert
long 64 bit java.lang.Long −9.223.372.036.854.775.808 ...
+9.223.372.036.854.775.807
Zweierkomplement-Wert
float 32 bit java.lang.Float ±1,4E−45 ... ±3,4E+38 Gleitkommazahl (IEEE 754)
double 64 bit java.lang.Double ±4,9E−324 ... ±1,7E+308 Gleitkommazahl doppelter Genauigkeit (IEEE 754)

¹: minimaler Speicherverbrauch

Referenzen

Alle Objekte und Felder liegen im Heap-Speicher und werden deshalb über eine Adresse referenziert. Der Zeiger mit einer solchen Adresse ist eine so genannte Referenz und deren Adresse kann nicht direkt, sondern nur durch Zuweisung verändert werden.

Object a = new Object();  // a hat eine Referenz zu einem gerade neuerstellten Objekt
Object b = a;             // b hat eine Kopie der Referenz von a
a = null;                 // a hat keine Referenz mehr

Reservierte Wörter

const und goto sind zwar reserviert, aber ohne Funktion, also keine Schlüsselwörter im eigentlichen Sinn. Sie dienen lediglich dem Compiler zur Ausgabe sinnvoller Fehlermeldungen für Umsteiger von C++ oder C.

true, false und null sind Literale, jedoch ebenfalls eigentlich keine Schlüsselwörter im engeren Sinn.

Mit assert werden Assertions realisiert.

private, protected und public sind Zugriffsmodifizierer (access modifier):

Die Klasse selbst Paket-Klassen/
innere Klassen
Unterklassen Sonstige
Klassen
private Ja Nein Nein Nein
(ohne) Ja Ja Nein Nein
protected Ja Ja Ja Nein
public Ja Ja Ja Ja

Private Methoden sind von der Polymorphie ausgenommen, d. h. die Definition einer Methode derselben Signatur in einer Subklasse gilt nicht als Überschreiben (sondern als Verbergen).

abstract, final, static sind Polymorphie-Modifizierer. static kann vor allen Membern außer Konstruktoren stehen und bedeutet, dass der betreffende Member im Kontext der Klasse verwendbar ist und kein Objekt benötigt. abstract kann vor Klassen und Methoden stehen und bedeutet, dass dem betreffenden Member die Implementierung fehlt. Unterklassen sind gezwungen, sich selbst als abstrakt zu deklarieren oder die fehlende Implementierung zu liefern. final kann vor allen Membern außer Konstruktoren stehen und bedeutet, dass der betreffende Member nicht mehr verändert werden darf. Finale Variablen sind ähnlich wie Konstanten, finale Methoden dürfen von Subklassen nicht überschrieben werden und von finalen Klassen darf man erst gar keine Subklassen bilden. Zugriffe auf finale Variablen, deren Wert dem Compiler bekannt ist, dürfen vom Compiler durch den Wert der Variable ersetzt werden. Aufrufe finaler Methoden dürfen vom Compiler durch eingebundenen Code (Inlining) ersetzt werden. Private Methoden sind automatisch final.

native kann nur vor Methoden stehen und bedeutet, dass die Implementierung der betreffenden Methode nicht in Java, sondern einer anderen Programmiersprache geschrieben wurde, und von der virtuellen Maschine über eine Laufzeitbibliothek eingebunden werden muss.

strictfp kennzeichnet Klassen und Methoden, deren enthaltene Fließkommaoperationen streng nach IEEE ablaufen müssen.

package deklariert die Paketzugehörigkeit eines komplexen Datentyps. Die Namensgebung eines Pakets sollte eindeutig sein und orientiert sich meist an der URI/URL des Eigentümers bzw. Erstellers.

import importiert Symbole (vorher nur Typen, ab 5.0 auch statische Member von Klassen), so dass sie ohne voll qualifizierten Namen verwendet werden können. Der Import kann hierbei über das Wildcard * auf komplette Pakete ausgedehnt werden.

boolean, char, byte, short, int, long, float, double und void sind Typen. void ist der Nichtstyp, notwendig, um Methoden ohne Rückgabewerte zu kennzeichnen. Für die primitiven Typen: siehe oben.

class, interface, enum (ab 5.0) und @interface (ab 5.0) dienen zur Deklaration eigener Typen: Klassen, Interfaces (Schnittstellen für Klassen), Enums (Aufzählungstyp für typsichere Aufzählung, engl. typesafe enumeration) und Annotations für Metadaten. enum und @interface sind mit Java 5.0 in die Sprache aufgenommen worden.

try, catch, finally, throw, throws beziehen sich auf die Ausnahmebehandlung (englisch exception handling). Mit throw wird eine Ausnahme ausgelöst. Alle eventuell ausgelösten Ausnahmen einer Methode, die nicht von RuntimeException oder Error abstammen, müssen mit throws in der Deklaration der Methode (Signatur) angegeben werden. Es handelt sich also um so genannte checked exceptions. Mit try umschließt man einen Block, in dem eventuell eine Ausnahme auftreten könnte. Mit catch fängt man nach einem try-Block die dort aufgetretene Ausnahme ab, mit finally hängt man an einen try- oder catch-Block einen Block für Aufräumarbeiten an (z. B. Schließen von Dateien).

extends und implements dienen der Vererbung: extends der genetischen Erweiterungsvererbung von Klasse zu Klasse oder Interface zu Interface und implements der Implementierungsvererbung von Interface zu Klasse. Außerdem wird extends bei Generics für Typerweiterung verwendet.

super und this beziehen sich im Objekt-Kontext auf das aktuelle Objekt in seinem tatsächlichen Morph (this) bzw. im Morph der Superklasse (super). this wird verwendet, um in Konstruktoren überladene Konstruktoren aufzurufen und um in Membern auf verdeckte Member äußerer Strukturen zu verweisen. super wird in Konstruktoren zum Aufruf des Superklassenkonstruktors und in überschreibenden Methoden zum Aufruf der überschriebenen Methode verwendet. Außerdem wird super bei Generics für Typeingrenzung verwendet.

new reserviert Speicher für neue Objekte (inklusive Arrays) auf dem Heap.

if, else, switch, case, default, while, do, for, break, continue dienen der Bedingungsprüfung und Schleifensteuerung und bedienen sich einer imperativen Syntax.

return liefert Werte aus einer Methode an die aufrufende Methode zurück.

volatile ist ein Modifizierer für nicht-lokale Variablen und verbietet dem JIT-Compiler Registeroptimierungen auf diese Variable, weil mehrere Threads sie gleichzeitig verwenden könnten (insbesondere im Kontext nativer Methoden).

synchronized verwendet ein Objekt als Semaphor mit Lock-Pool und sorgt so dafür, dass jeglicher mit synchronized an denselben Semaphor gebundener Code nur ein einziges Mal gleichzeitig ausgeführt werden kann. Alle anderen Threads, die solche Code-Blöcke ausführen möchten, werden solange blockiert, bis der Semaphor wieder frei ist. Des weiteren dient es auch der Synchronisation eventuell vorhandener lokaler Caches bei Multiprozessormaschinen.

transient kennzeichnet nicht-persistente Variablen, die nicht serialisiert werden dürfen.

instanceof ist ein Java-Operator, der prüft, ob ein Objekt Instanz eines Typs oder Subtyps ist.


Pakete, Namen, Klassen

Ein Java-Paket (englisch package) enthält mehrere Klassen, Schnittstellen und Ausnahmen und bildet einen eigenen Namensraum.

Java-Quelltext ist auf mehrere Übersetzungseinheiten (englisch compilation units) aufgeteilt, von denen jede in einer eigenen Datei abgelegt ist. Jede einzelne Übersetzungseinheit definiert als erstes das Paket, dem sie angehört, importiert dann mehrere Klassen oder Schnittstellen aus anderen Paketen, und definiert schließlich eine oder mehrere Klassen und Schnittstellen.

Java unterscheidet einfache Namen, die nur aus einem Bezeichner bestehen, und vollqualifizierte Namen, die aus einer Reihe von Bezeichnern bestehen, die durch Punkte getrennt sind. Die einfachen Namen sind nur ein Hilfsmittel, das dem Programmierer Tipparbeit ersparen und die Lesbarkeit des Programms erhöhen soll. Der Compiler übersetzt sie immer in vollqualifizierte Namen.

Das folgende Beispiel zeigt ein Hallo-Welt-Programm in Java.

package org.wikipedia;

import static java.lang.System.out;

public class HalloWelt {

  public static void main(String[] arguments) {
    out.println("Hallo Welt.");
  }

}

In diesem Beispiel wird eine Klasse "HalloWelt" definiert, die dem Paket "org.wikipedia" angehört. Per Konvention orientieren sich die Paketnamen in Java an den Internet-Domänen ihrer Entwickler, in diesem Fall also "org.wikipedia" für die Domäne "wikipedia.org". Die Richtung des Namens wird umgedreht, weil bei Internet-Domänen der Name der äußersten Einheit hinten steht, während er in Java vorne steht. Das Paket "org.wikipedia" befindet sich also "innerhalb" des Pakets "org".

Eine Import-Anweisung definiert einen einfachen Namen für einen vollqualifizierten Namen. So definiert z. B. die Anweisung "import java.io.File" den einfachen Namen "File" für die Klasse, die mit vollem Namen "java.io.File" heißt. Neben Imports von Klassen und Schnittstellen können seit Java Version 5 auch statische Felder oder Methoden von Klassen importiert werden. Ein solcher "statischer Import" wird durch das zusätzliche Schlüsselwort "static" festgelegt. Im obigen Beispiel wird das statische Feld "out" der Klasse "java.lang.System" importiert und anschließend unter dem kurzen Namen "out" verwendet.

Das wichtigste Konstrukt der Programmiersprache Java ist, da sie eine objektorientierte Sprache ist, die Klasse. Die Deklaration einer Klasse wird mit dem Schlüsselwort "class" eingeleitet. Anschließend folgt der Name der Klasse und dann werden - in geschweiften Klammern - die Felder und Methoden der Klasse definiert.

Eine Besonderheit von Java stellt die Schnittstelle dar. Eine solche Schnittstelle besteht nur aus abstrakten Methoden, deren Implementierung erst von den Klassen festgelegt werden, die sie "implementieren". Die Deklaration einer Schnittstelle sieht ähnlich aus wie die Deklaration einer Klasse, sie wird jedoch mit dem Schlüsselwort "interface" eingeleitet.

package org.wikipedia;

public interface Article {

  String getName();

  void setContent(String aContent);

}

Da alle Methoden einer Schnittstelle abstrakt sind, kann das Schlüsselwort "abstract" bei den einzelnen Methoden entfallen. Ebenso kann das Schlüsselwort "public" vor den einzelnen Methoden entfallen, weil Methoden einer Schnittstelle immer öffentlich sind, also aus allen anderen Paketen sichtbar.

Methoden und Felder

Das eigentliche Verhalten einer Klasse wird in ihren Methoden definiert. Die Signatur einer Methode besteht aus ihrem Namen und den Typen ihrer Parameter. Außerdem hat jede Methode einen bestimmten Rückgabetyp oder "void", wenn sie nichts zurück gibt, und kann eine Reihe von Ausnahmen in einer sogenannten "throws"-Klausel definieren.

Beispiel einer konkreten Methode.

public double summe(double a, double b) throws NotANumberException {
  if (Double.isNaN(a) || Double.isNaN(b)) {
    throw new NotANumberException();
  }
  return a + b;
}

Diese Methode erwartet zwei doppelt genaue Fließkommazahlen als Parameter und gibt die Summe der beiden ebenfalls als Fließkommazahl zurück. Falls eine der beiden Zahlen keine gültige Fließkommazahl ist, wirft sie eine Ausnahme namens "NotANumberException". Beispiel einer abstrakten Methode.

public abstract double summe(double a, double b) throws NotANumberException;

Felder sind Variablen, die zu einem Objekt gehören. Sie sind durch einen Typ und einen Namen definiert und können wahlweise bereits wenn das Objekt erzeugt wird, initialisiert werden.

Beispiel einer Variablendeklaration ohne Initialisierung.

private int x;

Klassenvariablen werden als statische Felder deklariert. Diese Felder existieren nicht einmal pro Objekt, sondern nur einmal pro Klasse. Das Schlüsselwort "final" verhindert, das eine Variable nach ihrer Initialisierung ihren Wert ändert. Es wird zum Beispiel verwendet, um Konstanten zu definieren.

Beispiel einer statischen und finalen Variablendeklaration mit Initialisierung.

private static final int X = 2;

Anweisungen

Java unterstützt die üblichen Anweisungen, die auch von anderen Programmiersprachen bekannt sind. Mehrere Anweisungen werden durch Semikolons getrennt und mit geschweiften Klammern zusammengefasst.

Als Kontrollstrukturen stehen zur Verfügung:

  • Die gewöhnliche binäre Verzweigung (ein Schlüsselwort "then" existiert nicht):
if (Bedingung) { Anweisung; } else { Anweisung2; }
  • Eine Fallunterscheidung, wobei der Ausdruck nur ein logischer Wert, ein Zeichen oder eine ganze Zahl sein kann.
switch (Ausdruck) {
  case Konstante1: Anweisung1; break;
  case Konstante2: Anweisung2; break;
  default: Anweisung3;
}

Java unterscheidet drei verschiedene Arten von Schleifen.

  • Wiederhole eine Anweisung solange die Bedingung wahr ist, wobei die Bedingung vor der Anweisung geprüft wird.
while (Bedingung) { Anweisung; }
  • Wiederholung einer Anweisung solange die Bedingung wahr ist, wobei die Bedingung erst nach der Ausführung geprüft wird.
do { Anweisung; } while (Bedingung);
  • Initialisiert einen Wert, wiederholt die Anweisung solange die Bedingung wahr ist und führt eine zweite Anweisung nach jedem Schleifendurchlauf aus.
for (Initialisierung; Bedingung; Anweisung2) { Anweisung1; }

Ausdrücke

Im Gegensatz zu einigen älteren Programmiersprachen, wie beispielsweise C, ist die Reihenfolge der Auswertung von Ausdrücken in Java bereits in der Sprachdefinition festgelegt: Operatoren gleichen Rangs werden immer von links nach rechts ausgewertet. Im folgenden Beispiel wird also garantiert zuerst geprüft, dass "object" nicht "null" ist, bevor dessen Methode "pruef" aufgerufen wird. Da außerdem der rechte Teil des logischen Und-Operators nur dann ausgewertet wird, ist dieser Ausdruck sicher und kann niemals eine "NullPointerException" verursachen. (Eine "NullPointerException" wird in Java immer dann geworfen, wenn versucht wird, die Objektreferenz "null" aufzulösen.)

if (objekt != null && objekt.pruef()) {
    System.out.println("geprüft");
} else {
    System.out.println("ungeprüft oder Prüfung misslungen");
}

http://java.sun.com/docs/books/jls/third_edition/html/j3TOC.html - die Java Language Specification (Java-Sprachspezifikation) definiert die Semantik und die Syntax der Programmiersprache Java.

Vorlage:Wikibooks1