Skip to content

Instantly share code, notes, and snippets.

@LukasKalbertodt
Last active November 21, 2017 16:27
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save LukasKalbertodt/4cac3cdd9041672d9dc8 to your computer and use it in GitHub Desktop.
Save LukasKalbertodt/4cac3cdd9041672d9dc8 to your computer and use it in GitHub Desktop.
Java Style Guide

Java Style Guide für InfoA

Seinen Code ordentlich zu formatieren und sich an diverse Konventionen zu halten hilft erheblich beim Verständnis und dem Schreiben des Codes. Aus diesem Grund werden in InfoA für falsch formatierten Code immer Punkte abgezogen. Dieser Guide listet die wichtigsten Vorgaben auf, an die man sich halten muss.

  1. Namen

Es gelten folgende Namenskonventionen:

  • Variablen: lowerCamelCase
  • Klassen: UpperCamelCase
  • Konstanten: SCREAMING_SNAKE_CASE
  • Methoden: lowerCamelCase

Sinnvolle Benennung

Alle Namen sollen sinnvoll und beschreibend sein. Ein "berühmter" Witz sagt, dass Namensgebung die schwierigste Aufgabe eines Programmierers ist -- das hat seinen guten Grund. Sich intensiv zu überlegen, wofür eine Variable oder Methode genutzt wird, und erst nach Minuten einen Namen zu vergeben ist oft sinnvoll.

Um auf einen guten Namen zu kommen, kann man sich folgende Frage stellen: "Welche Rolle spielt die Variable in meinem Programm?". Anhand von guten Variablen- und Methodennamen erschließt sich sehr schnell der Sinn dieser Variable/Methode aber auch eines ganzen Codeteils.

Gewissermaßen eine Ausnahme stellt das berühmte i dar. Es wissen aber alle Programmierer, was eine Variable mit dem Namen i tut, somit erfüllt der Name wieder seinen Zweck. Allerdings sollte man schon bei verschachtelten Schleifen, die z.B. die Zeilen und Spalten einer Matrix durchlaufen, sinnvolle Namen, wie zeile und spalte (oder auch: x und y), geben, anstatt auf i und j zurück zu greifen.

  1. Einrückung

Alle sog. "Blöcke" werden eingerückt. Blöcke sind die Rümpfe von if/else, for, while, do-while, switch, Methoden und Klassen. Blöcke sind in der Regel von geschweiften Klammern { } umschlossen. In der Vorlesung werden einzeilige Blöcke vorgestellt, bei denen man die geschweiften Klammern weglassen kann. Dies ist aber mit zahlreichen Nachteilen verbunden, sodass in Abgaben IMMER Klammern gesetzten werden müssen.

Die öffnende geschweifte Klammer wird immer in die Zeile mit dem Kopf des Blocks geschrieben.

if (a != 0) {    // Richtig
// ---------------------------------------
if (a != 0) 
{                // Nicht richtig

Der Code des Rumpfes/Blocks startet in der nächsten Zeile um eine Einheit weiter eingerückt. Nach dem Block folgt die schließende geschweifte Klammer wieder eine Einheit weniger eingerückt, also auf der selben Höhe, wie der Kopf.

// Richtig
if (a != 0) {
    a = 7;
    IO.println("Hallo");
}

// Falsch: Öffnende geschweifte Klammer in neuer Zeile
if (a != 0)
{
    a = 7;
    IO.println("Hallo");
}

// Falsch: Nicht eingerückt
if (a != 0) {
a = 7;
IO.println("Hallo");
}

// Falsch: Schließende geschweifte Klammer zu weit eingerückt
if (a != 0) {
    a = 7;
    IO.println("Hallo");
    }

Auf eine schließende geschweifte Klammer darf in der selben Zeile nur while (...) (do-while Schleife), else oder else if (...) folgen. Sonst ist } immer alleine in ihrer Zeile. Also nicht:

if (x < 0) {
    x = 3;
} IO.println(x);   // Neue Zeile nach `}`!

"Eine Einheit einrücken" bedeutet entweder 2 oder 4 Leerzeichen oder; es muss lediglich konsistent sein und sich nicht in einer Datei abwechseln. vi rückt standardmäßig mit 2 Leerzeichen ein.

Abgesehen von Blöcken wird nichts eingerückt. Im Folgenden sieht man, wie man es nicht machen darf:

int a = 4;
a++;
    int b = 7;
        int c = a + b;
        IO.println("derp");

Hier wird kein neuer Block angefangen, also gehören alle Anweisungen auf die selbe Einrückungshöhe!

  1. Spacing

Alle binären Operatoren werden links und rechts von einem Leerzeichen umschlossen. Binäre Operatoren sind:

  • Arithmetische (+ Zuweisung): + - * / % (+= -= *= /= %=)
  • Vergleichs: < > == != <= >=
  • Logische: && || ^
  • Bitweise (+ Zuweisung): & | ^ (&= |= ^=)
  • Shift (+ Zuweisung): << >> >>> (<<= >>= >>>=)
// Richtig
if (n < -1.0 && n > 1.0) {
    n += n / 5.0;
}

// Falsch: Alle Leerzeichen vergessen
if (n<-1.0&&n>1.0) {
    n+=n/5.0;
}

Alle unären Operatoren werden direkt an den Operanden geschrieben. Unäre Operatoren sind: Logisches Nicht (!), bitweise Nicht (~) sowie Inkrement und Dekrement (++ --).

// Richtig
while (!prim) {
    n++;
}

// Falsch: Unnötige Leerzeichen
while (! prim) {
    n ++;
}

Außerdem:

  • Leerzeichen nach jedem Komma (IO.print("hi", 4) und nicht IO.print("hi",4))
  • Leerzeichen um geschweifte Klammern, wenn nötig (if (x) { und nicht if (x){, sowie } else { und nicht }else{
  • Um verschiedene Dinge voneinander zu trennen, immer nur maximal 1 Leerzeichen einsetzen, nicht mehrere.
int x = 3 + 4;        // richtig
int  x  = 3    +   5; // falsch
  1. Kommentare

Kommentare stehen über dem Code, den sie beschreiben -- nicht darunter. Wenn die Kommentare sehr kurz sind, können sie auch in der selben Zeile stehen, wie der Code, den sie beschreiben. Außerdem sind sie eingerückt, wie normaler Code. Es empfiehlt sich // Kommentare in Methoden zu verwenden. Nach // kommt immer ein Leerzeichen, um die Lesbarkeit zu erhöhen.

// ---- Richtig ----
// Alle Zeile der Matrix durchlaufen
for (int zeile = 0; zeile < matrix.length; zeile++) {
    // Alle Spalten der Zeile durchlaufen
    for (int spalte = 0; spalte < matrix[zeile].length; spalte++) {
        // hier mehr Code
    }
}

// ---- Falsch ----
for (int zeile = 0; zeile < matrix.length; zeile++) {
        //Alle Spalten der Zeile durchlaufen
    for (int spalte = 0; spalte < matrix[zeile].length; spalte++) {
    // hier mehr Code
    }
}
// Alle Zeile der Matrix durchlaufen

Kommentare sollten immer gesetzt werden, um Code zu erklären. Denkt aber dran, nicht zu wiederholen, was der Code eh schon sagt. Es geht nicht darum, jede einzelne Zeile zu erklären, sondern die Funktion und den Sinn eines Codeteils gut zu erklären.

// ---- Schlecht: Sagt nur, was der Code eh schon sagt
// Zahl vom Benutzer einlesen
int n = IO.readInt("Bitte Zahl eingeben: ");

// Variable w deklarieren
int w = 1;
while (w*w < n) {
    // w inkrementieren
    w++;
}

// Ergebnis ausgeben
IO.println(w);

// ---- Gut: Abstrakt beschreiben, was der Code tut
int n = IO.readInt("Bitte Zahl eingeben: ");

// Berechne die Wurzel von n, indem w solange erhöht wird, bis
// das Quadrat von w größer als n ist. Das Ergebnis ist also die
// ganzzahlige Wurzel + 1
int w = 1;
while (w * w < n) {
    w++;
}

IO.println(w);
  1. Zeichenlimit pro Zeile

Es gibt viele gute Gründe für ein Limit der Zeichen pro Zeile -- unser Totschlagargument ist der Drucker, der die Zeichen nach dem Limit zwangs-umbricht und der Code unlesbar wird. Also müssen alle Abgaben das 80 Zeichen Limit einhalten.

Um lange Zeilen auf mehrere Zeilen aufzuteilen, gibt es mehrere Möglichkeiten. Grundsätzlich gilt: Überall wo ihr ein Leerzeichen macht, könnt ihr auch ein Zeilenumbruch machen (Ausnahme: String Konstantenbezeichner/Literale). Oft macht es auch Sinn komplizierte Rechnungen auf mehrere Zeilen aufzuteilen und Zwischenergebnisse in Variablen zu speichern. Manchmal gibt es auch ebenso sinnvolle, aber kürzere Variablenamen. Hier gibt es keine wirklich feste Regel; man muss selber darauf achten, dass es gut lesbar ist, indem man z.B. entsprechend einrückt. Lange String Literale kann man übrigens mit + aufsplitten:

// Schlecht
IO.println("Du wartest auf einen Zug, ein Zug der Dich weit weg bringen wird. Du weißt wohin der Zug Dich hoffentlich bringen wird, aber Du weißt es nicht sicher");

// Umgebrochen
IO.println("Du wartest auf einen Zug, ein Zug der Dich weit weg bringen wird. "
           + "Du weißt wohin der Zug Dich hoffentlich bringen wird, "
           + "aber Du weißt es nicht sicher");

Diverses

  • Variablen sollten so spät deklariert werden, wie möglich. Ein Schleifenzähler, der nur in der Schleife gebraucht wird, sollte auch erst dort deklariert werden (for (int i = 0; i < 3; i++) statt int i; for (i = 0; i < 3; i++)). Insbesondere heißt das auch, dass man bloß nicht alle Variablen am Anfang des Programms deklarieren soll -- dies ist lediglich in der Vorlesung an der Tafel sinnvoll.
  • Eine komplett leere Zeile kann hin und wieder zur Lesbarkeit eingefügt werden
  • Wann immer es geht: for-Schleife nutzen! Es geht fast immer...
  • Doppelter Code sollte immer vermieden werden
  • Globale Variablen sollten immer vermieden werden
/****************************** FormatExample.java **************************/
import AlgoTools.IO;
/**
* @author Willi Wacker <rzkuerzel@uos.de>
* @author Susi Sorglos <susi@sorglos.net>
*
* Ein sinnloses, aber schön formatiertes Beispiel
*
*/
public class NiceExample {
public static void main(String[] argv) {
// Zahl (>0) vom Benutzer einlesen
int inputNumber = 0;
do {
inputNumber = IO.readInt("Bitte eine Nummer eingeben: ");
} while (inputNumber <= 0);
// Testen ob Zahl ungerade ist
if (inputNumber % 2 != 0) {
// Wenn die Zahl ungerade ist, handelt es sich um Kekse.
// Anzahl der Kekse bestimmen durch Iterationen von Collatz.
int iterations = 0;
int collatzNumber = inputNumber; // Zahl zum veraendern kopieren
// Collatz durchlaufen
while (collatzNumber != 1) {
if(collatzNumber % 2 == 0) {
collatzNumber = collatzNumber / 2;
} else {
collatzNumber = collatzNumber * 3 + 1;
}
iterations++;
}
IO.println("Deine Eingabe hat Kekse gebracht! Herzlichen "
+ "Glueckwunsch zu diesem Gewinn! Die Anzahl der Kekse ist: "
+ iterations);
} else {
// Wenn die Zahl gerade ist, handelt es sich um Kuchen
IO.print("Deine Eingabe wurde zu einem ");
// Sorte ermitteln
switch (inputNumber) {
case 2:
IO.print("Erdbeerkuchen");
break;
case 4:
IO.print("Schokokuchen");
break;
case 6:
IO.print("Kaesekuchen");
break;
default:
IO.print("Hundekuchen");
}
IO.println("! Lass es dir schmecken!");
}
}
}
@deichbrise
Copy link

Sehr schöner Styleguide, lediglich der Tatsache, dass die öffnende geschweifte Klammer des Blocks in der 1. Zeile steht, würde ich nicht ganz zustimmen. Aber das ist ja die ewige Diskussion unter Programmierern...

@y0urself
Copy link

Gut geschrieben.

@amittelstedt
Copy link

amittelstedt commented Nov 9, 2016

Wann immer es geht: for-Schleife nutzen! Es geht fast immer...

Man kann nicht nur fast immer die for-Schleife statt der while-Schleife nutzen, sondern immer.
z.B. Für einen Boolschen Ausdruck booleanExpression:
Kann man immer statt:
while(booleanExpression) {
//Code
}

Das hier schreiben:
for(;booleanExpression;) {
//Code
}

Für die do-while-Schleife geht es ähnlich wenn man etwas umdenkt:
do {
//Code
} while(booleanExpression)

Ist das gleiche wie:
for(int i = 0; i==1 && booleanExpression;i = 1) {
//Code
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment