In diesem Abschnitt ist das Beispiel von oben nochmals aufgeführt in einer „best-practise“-Version. Hierbei ändern sich zwei wesentliche Dinge:
class Robot { // Deklaration der Klasse Robot /** Der Durchmesser des Roboters. */ int dRobot; /** Die Richtung in die der Roboter schaut. */ float winkel; /** Die x-Position des Roboters. */ float posX; /** Die y-Position des Roboters. */ float posY; /** * Der "default"-Konstruktor erstellt ein Roboter-Objekt mit Standardwerten für alle notwendigen Parameter. */ public Robot() { this.dRobot = 50; this.winkel = PI; this.posX = 100; this.posY = 100; } /** * Dieser Konstruktor erstellt ein Roboter-Objekt mit allen notwendigen Parametern, * entsprechend der Vorgabe durch die Eingabeparameter. */ public Robot(int dRobot, float winkel, float posX, float posY) { this.dRobot = dRobot; this.winkel = winkel; this.posX = posX; this.posY = posY; } /** * Diese Methode zeichnet einen Roboter an seiner aktuellen Position und Ausrichtung. */ void drawRobot(){ // Zeichne einen Kreis an der Position des Roboters. ellipse(posX,posY,dRobot,dRobot); // Zeichne eine Linie welche die Orientierung des Roboters angibt. line(posX,posY,posX+cos(winkel)*50, posY+sin(winkel)*50); } /** * Diese Methode bewegt einen Roboter ein kleines Stück in die Richtung in die er gerade schaut. */ void moveRobot(){ this.posX = this.posX+cos(this.winkel)*2; this.posY = this.posY+sin(this.winkel)*2; } /** * Diese Methode dreht den Roboter um einen gewissen Winkel welcher von "winkelaenderung" vorgegeben wird. */ void turnRobot(float winkelaenderung){ this.winkel = winkel + winkelaenderung; } }
Im Paradigma des Objektorientierten Programmierens werden Zugriffsmodifizierer verwendet um die Rechte anderer Objekte einzuschränken (Datenkapselung). Unter Java werden folgende Zugriffsmodifizierer verwendet:
Die Klasse selbst, innere Klassen | Klassen im selben Package | Unterklassen | Sonstige Klassen | |
---|---|---|---|---|
private | Ja | Nein | Nein | Nein |
(default) | Ja | Ja | Nein | Nein |
protected | Ja | Ja | Ja | Nein |
public | Ja | Ja | Ja | Ja |
Der Zugriffsmodifizierer private verhindert jeden Zugriff von außerhalb einer Klasse auf den entsprechenden Member. Üblicherweise werden Klassenvariablen auf diese Weise deklariert und können dann nur über sog. getter- und setter-Methoden verändert werden. Für dieses Vorgehen gibt es gute Gründe, siehe Kapselung.
Der Zugriffsmodifizierer (default) wird implizit verwendet, d.h. wenn kein anderer Zugriffsmodifizierer angegeben wird ist der Member (default) oder package private. Auf Member mit diesem Zugriffsmodifizierer kann nur innerhalb eines Package zugegriffen werden.
Wie ihr vielleicht bemerkt habt kommt Processing ganz ohne Zugriffsmodifizierer aus. Das liegt daran das der Processing-Sketch zusammen mit allen Tabs in einem Package liegt und daher (default) ausreicht um auf alle Member zugriff zu haben. Um es dem Programmieranfänger einfach zu machen müsst ihr nicht extra angeben das sich alles in einem Package befindet, das übernimmt der Processing-Compiler.
Der Modifizierer protected ist etwas freizügiger als (default). Auf solche Member können auch Subklassen zugreifen, sogar wenn sie in einem anderen Package liegen als der betroffene Member.
Protected wird im Zusammenhang mit API-Programmierung und Templates verwendet.
Mit public gestattet ihr allen Klassen zugriff auf den jeweiligen Member. Wird in der Regel bei wichtigen Methoden, Konstruktoren und Datentypen eingesetzt.
Das ist letztendlich Geschmacksache und kommt darauf an wie und wo du an Code arbeitest.
Felix sagt: „Hier im Kurs sind Zugriffsmodifizierer unnötig, KISS-Prinzip“.
Ein Java Programmierer würde sagen: „In einfachen Fällen reicht private für Variablen und public für Methoden, Konstruktoren und Datentypen. Es gilt die Faustregel - so streng wie möglich, so freizügig wie nötig“.
public class PointIn2D { private int x, y; // private Klassenvariablen public PointIn2D(int x, int y) { // Konstruktor, für alle nutzbar setX(x); setY(y); } public int getX() { // getter-Methode return x; } public int getY() { return y; } public void setX(int x) { // setter-Methode zum Ändern der Klassenvariable this.x = x; } public void setY(int y) { this.y = y; } }
In Java kann unter Vererbung eine Spezialisierung einer bestehenden Klasse verstanden werden. Wir unterscheiden dazu in Super- und Subklassen. Die Superklasse (oder Elternklasse) beinhaltet eher allgemeine Attribute und Methoden. Die Klasse „Stift“ könnte z.B. Attribute für Stiftlänge, Stiftfarbe, usw. enthalten. Die Subklasse bekommt von der Superklasse alle Attribute und Methoden vererbt, erweitert dieses Set aber um eigene Attribute und Methoden. Z.B. könnte die Subklasse „Kugelschreiber“ das Attribut Stift eingefahren/Stift ausgefahren hinzufügen und eine Methode die diesen Zustand verändert.
Eine ausführliche Beschreibung wie Vererbung funktioniert findet ihr hier: http://www.java-tutorial.org/vererbung.html
In diesem Abschnitt geht es um Fehlerbehandlung von Fehlern die zur Laufzeit eures Programmes auftreten. Das sind alle Fehler die nicht schon beim Kompilieren eures Codes auftreten. Dazu gehören Fehler wie versuchter Zugriff auf bisher nicht erzeugte Objekte oder Division durch Null.
Im Gegensatz zu anderen Programmiersprachen, wie etwa C, muss in Java ein Fehler nicht gleich zum Programmabsturz führen. Solche Fehler werden in Java als Exceptions behandelt und können abgefangen werden. Am Beispiel der Roboter-Klasse führen wir eine Null-Pointer-Exception herbei, indem wir das Robot-Objekt nicht initialisieren und trotzdem versuchen die drawRobot-Methode aufzurufen:
Robot myRobot; void setup() { size(199,199); } void draw() { myRobot.drawRobot(); }
Dieser Code führt zum Abbruch des Programms und es wird der Hinweis NullPointerException ausgegeben. Der Vorteil des sogenannten try-catch-Blocks ist das der Fehler nicht zum Absturz führt und eine detaillierte Fehlerausgabe mit Angabe der fehlerhaften Programmzeile generiert wird.
Robot myRobot; void setup() { size(199,199); } void draw() { try { myRobot.drawRobot(); } catch(Exception e) { println("Exception: " + e.getMessage()); e.printStackTrace(); } }
Die Ausgabe zeigt euch den Pfad in dem der Fehler aufgetreten ist, wobei die erste Zeile eure Message ist, die zweite der Exception-Typ und die dritte gibt euch am Ende der Zeile die entsprechende Zeile in eurem Code:
Exception: null java.lang.NullPointerException at sketch_160720a.draw(sketch_160720a.java:24) at processing.core.PApplet.handleDraw(PApplet.java:2402) at processing.awt.PSurfaceAWT$12.callDraw(PSurfaceAWT.java:1527) at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:316)