Java

Aus Jonas Notizen Webseite
Zur Navigation springen Zur Suche springen

Inhaltsverzeichnis

Quellen

https://de.wikipedia.org/
https://www.tutorialspoint.com/java/index.htm
http://openbook.rheinwerk-verlag.de/javainsel/
https://docs.oracle.com/javase/8/docs/api/

...und weitere Quellen, die ich immer verlinken werde...

Java-Technologie

Aufbau der Java-Technologie
Programmier-
sprache
Java Quelltext (.java)
JDK Entwicklungswerkzeuge
Java-Compiler, …
Java Bytecode (.class, .jar)
JRE Java Programmierschnittstelle (API)
Java Virtual Machine (JVM)
mit Just-in-time-Kompilierung
Betriebssystem Windows, Linux, Solaris, Mac OS X, …

Die Java-Technologie ist eine Reihe von Computersoftware und -spezifikationen, die von James Gosling bei Sun Microsystems entwickelt und später von der Oracle Corporation übernommen wurde. Sie bietet ein System zur Entwicklung von Anwendungssoftware und deren Bereitstellung in einer plattformübergreifenden Computerumgebung.

Zur Java-Technologie gehören folgende Bestandteile:

  • die Programmiersprache Java, um Programme zu formulieren
  • das Java Development Kit – ein Entwicklungswerkzeug, das grundlegende Teile wie einen Übersetzer (Quelltext -> Bytecode) und die Java (Standard) Bibliotheken enthält
  • die Java-Laufzeitumgebung – eine standardisierte Software-Plattform, um die entwickelten Bytecode-Programme ausführen zu können.


Java Runtime Enviroment (JRE)

Mit ihr werden Java-Anwendungen weitgehend unabhängig vom darunter liegenden Betriebssystem ausgeführt.
Die JRE definiert die Anwendungsprogrammierschnittstellen (APIs, darunter java.lang.String) eindeutig und maschinenunabhängig und enthält die virtuelle Maschine (JVM) zur eigentlichen Ausführung des Bytescodes. Die virtuelle Maschine und die API müssen aufeinander abgestimmt sein, und werden deshalb zur JRE zusammengefasst

  • Für die meisten Betriebssysteme ist sie kostenlos verfügbar. [1]
  • Sie stellt eine Softwareplattform dar, auch Java-Plattform genannt.

Für die Programmierung mit Java wird das Java Development Kit (JDK) (oder eine anderes Entwicklungswerkzeug, welches ebenfalls Java-Bytecode erzeugen kann) benötigt.

Editionen

Seit dem Erscheinen von Java 2 wird die Java-Laufzeitumgebung in der Form, wie sie von Sun zur Verfügung gestellt wird, in Editionen unterteilt, da Java-Anwendungen auf unterschiedlichen Endgeräten mit unterschiedlichen Charakteristiken eingesetzt werden können, und nicht jeder Anwendungsbereich die gleichen APIs benötigt:

  1. Java Card: Minimale Version für Chipkarten
  2. Standard Edition: Genereller PC/Server/...-Einsatz. Grundlage für Java-EE- und Java-ME.
  3. Micro Edition (Java ME): Für Smartphones oder Tablets (sog. embedded consumer products)
  4. Enterprise Edition (Java EE): Angereichert an Programmierschnittstellen für die "transaktionsbasierte Ausführung von mehrschichtigen Unternehmens- und Web-Anwendungen". (Konkurent: .NET von Microsoft)

Weitere Versionsinformationen sind im Wikipedia-Artiekl: Java-Technologie#Versionen und in der Java-Insel beschrieben.

Java Virtual Machine (JVM)

Übersicht der Java Virtual Machine (JVM)-Architektur, basierend auf "The Java® Virtual Machine Specification Java SE 7 Edition".

Die Java Virtual Machine (abgekürzt Java VM oder JVM) ist der Teil der Java-Laufzeitumgebung (Java Runtime Environment, JRE) für Java-Programme, der für die Ausführung des Java-Bytecodes verantwortlich ist.

  • Die JVM dient dabei als Schnittstelle zur Maschine und zum Betriebssystem und ist für die meisten Plattformen verfügbar
  • Die JVM ist meist in den Programmiersprachen C oder C++ geschrieben

Bestandteile

Die Bestandteile der JVM sind

Funktionsprinzip

Vom Java-Compiler erzeugter, plattformunabhängiger Bytecode wird von einer plattformabhängigen JVM-Implementierung ausgeführt.

  • Die virtuelle Maschine arbeitet dabei wie ein Interpreter, ist jedoch wesentlich schneller, da zB. keine Syntaxüberprüfungen mehr vorgenommen werden müssen.

"The Java® Virtual Machine Specification"

Durch die "Java® Virtual Machine Specification" wird sichergestellt, dass der Bytecode auf jeglicher Maschine exakt gleich intepretiert/ausgeführt wird.

JVM-Sprachen

Java

Die Programmiersprache Java ist der am häufig genutzte Weg um Quellcode zu schreiben, welcher später in Java's-Bytecode kompiliert und von einer virtuellen Maschine (JVM) ausgeführt wird.

Andere JVM-Sprachen

Neben Java gibt es auch andere Sprachen, welche für die Java-Technologie entworfen wurden:

  • Groovy, eine sehr Java-nahe, statisch wie dynamisch typisierte Programmiersprache
  • Scala, eine Sprache, die Eigenschaften von Java mit funktionaler Programmierung vereint,
  • Kotlin, eine 2011 vorgestellte Sprache von JetBrains
  • JRuby, eine annähernd Ruby-kompatible Implementierung,
  • Jython (früher: JPython), eine reine Java-Implementierung der Programmiersprache Python,

Das coole an Sprachen, die für die Java-Technologie entworfen wurden, ist, dass sie kompatibel zu einander sind. Dass heißt das man mithilfe der Programmiersprache Kotlin auf eine Klasse/Bibliothek zugreifen kann, welche in Java geschrieben wurde.

Scripts mithilfe einer JVM-Sprache ausführen

Es sind auch eine Reihe von Skripting-Sprachen verfügbar, die von Java aus aufrufbar sind.

  • Dazu gehört JavaScript (mittlerweile standardisiert als ECMAScript) mit dem „Rhino“-Interpreter (ein Mozilla-Projekt) bis Java 7 bzw. mit dem „Nashorn“-Interpreter ab Version 8.

JVM-Übersetzer für nicht-Java Programmiersprachen

Im Prinzip könnte jede Programmiersprache als Grundlage für die Generierung des Java-Bytecodes genutzt werden, meistens existieren aber keine entsprechenden Bytecode-Compiler.

  • Wikipedia bietet hierzu einen Eintrag aller bekannten Bytecode-Compiler.

Verzicht auf manuelle Speicherverwaltung

Java

  • verzichtet auf manche "low-level Programmieraspekte", wie Pointers für direkte Speicher-Manipulation, und
  • besitzt ein eher grundlegendes Arbeitsspeicher-Model, indem alle Objekte auf dem Heap gespeichert werden (Wobei einige native Java-Objekte durch Escape-Analysen auch auf den Stack gelegt werden können).

Liste/Vergleich von JVM-Implementierungen

Siehe List of Java virtual machines und Comparison of Java virtual machines

IntelliJ: Bytecode anzeigen

Die Entwicklungsumgebungen IntelliJ IDEA bietet eine kleine aber dennoch interessante Funktion: Mithilfe von [View -> Show Bytecode] wird der kompilierte Bytecode der derzeitigen Klasse angezeigt.

Beispiel-Quellcode eines "Hello-World" Programms in der Programmiersprache Java:

package at.pixeltutorials;

/**
 * Created by PixelTutorials (aka. Jonas Pammer) on Mai, 2019.
 * Website: https://www.pixeltutorials.at/
 */
public class TestClass {
    public static void main(String[] args){
        System.out.println("Hello, World!");
    }
}

kompilierter Bytecode

// class version 52.0 (52)
// access flags 0x21
public class at/pixeltutorials/TestClass {

  // compiled from: TestClass.java

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 7 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lat/pixeltutorials/TestClass; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 9 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "Hello, World!"
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L1
    LINENUMBER 10 L1
    RETURN
   L2
    LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1
}

Sicherheitsvorteile

Sie überwacht zur Laufzeit die Ausführung des Programms, verhindert also zum Beispiel Pufferüberläufe, welche zu unvorhersehbarem Verhalten wie etwa dem Absturz des Programmes führen.

Optimierungsverfahren

Um die Ausführungsgeschwindigkeit von Java-Programmen zu erhöhen setzen die meisten Java-VMs sogenannte JIT-Compiler (JITC) ein, die unmittelbar während des Programmablaufs den Bytecode „Just In Time“ („Gerade rechtzeitig“) dauerhaft in Maschinencode übersetzen.

  • Eine Weiterentwicklung dieses Ansatzes ist der Hotspot-Optimizer von Sun, welcher mit dynamischer Optimierung arbeitet.

Abschottung von Threads

Die Java VM schottet die in ihr laufenden Prozesse vom Betriebssystem ab (Green Threads). Sie bildet standardmäßig Java-Threads durch Threads des Betriebssystems ab.

  • Somit ist es auch möglich, auf einem Betriebssystem, das kein Multithreading unterstützt, eine Java VM mit vollem Funktionsumfang anzubieten.

Software Developement Kit (SDK)

Ein Software Development Kit (SDK) ist eine Sammlung von Programmierwerkzeugen und Programmbibliotheken, die zur Entwicklung von Software dient.

Java Developement Kit (JDK)

Das Java Development Kit (JDK) des Unternehmens Oracle – ehemals von Sun Microsystems – ist eines der von Java-Entwicklern meistgenutzten Java-SDKs.

OpenJDK

OpenJDK ist die offizielle freie Implementierung der Java Platform, Standard Edition (Java SE) von Sun Microsystems und stellt den freien Nachfolger des Java Development Kit (JDK) dar.

  • Es ist in den Sprachen C++ und Java geschrieben, wird unter der GNU General Public License (GPL) (mit Ausnahmen für gelinkte externe Software) veröffentlicht und von bekannten IT-Firmen wie Apple, IBM und SAP unterstützt.

Bestandteile

Das JDK beinhaltet neben der Laufzeitumgebung Java Runtime Environment (JRE) die folgenden Entwicklungswerkzeuge:

Java-Compiler

Der Java-Compiler (javac) liest den Quellcode aus Dateien mit der Endung .java und schreibt den Bytecode in Dateien, die auf .class enden. (Die eigentlichen Dateinamen entsprechen dem Namen der zu übersetzenden Klasse.)

  • Der Java-Compiler ist in Java geschrieben und plattformunabhängig, das heißt, der Bytecode des Compilers ist auf unterschiedlichen Plattformen identisch! (Write once, run anywhere (WORA))

Java-Dokumentationswerkzeug

Das Software-Dokumentationswerkzeug javadoc erzeugt aus dem Java-Quellcode eine Programmdokumentation als HTML-Hypertext.

  • Dazu werden neben strukturellen Informationen auch Javadoc-Kommentare sowie Annotationen berücksichtigt.

Zur Java Standard-Edition Bibliothek gibt es auch eine Javadoc-Seite (SEHR HILFREICH): https://docs.oracle.com/javase/8/docs/api/

Java-Archiver

Das Programm jar erstellt Java-Archive (sog. JAR-Dateien), dessen Syntax dem Unixoiden tar-Befehl änhelt. In dem JAR-Archiv werden sämtliche .class-Dateien, Grafiken, Textdateien und sonstige Dateien, (also alles was eine Java-Anwendung benötigt) zu einer einzigen Datei zusammengepackt (Das JAR-Archiv).

Java-Debugger

Der Java-Debugger (jdb) ist ein Tool zum Debuggen einer laufenden Java-Anwendung. Es werden dabei klassische Debuggerfunktionen unterstützt, darunter:

  • Code Listing (Quellcode anzeigen)
  • Setzen von Unterbrechungspunkten (Breakpoints)
  • Inspizieren von Variablen und Datenstrukturen zur Laufzeit

Dieser mitgelieferte Java-Debugger ist nur eine kleine Demonstration, was die Java Debugging-API alles draufhat.
Die meisten Entwicklungsumgebungen bringen einen einfach zu bedienenten Debugger mit.

Und vieles mehr

Das Java-SDK beinhaltet zudem folgende weitere Bestandteile:

  1. AppletViewer: Ein eigener Browser, welcher nur die Java-Applets einer Seite in einem einzigen neuen Fenster anzeigen kann. (Fürs schnelle und einfache testen von Applets)
  2. JarSigner: Java-Anwendung signieren und überprüfen
  3. HTMLConverter: Konvertiert <applet> in <object>-Tags welche von Browsern, die mit einem JRE-Plugin ausgestattet sind, erwartet wird
  4. JConsole: Zur Überwachung (In Netbeans) von Laufzeitparametern (Speicherverbrauch vom Heap/PermGen, Funktionsweise der Garbage Collection, detailierte Informationen über erstelle Objekte, uvm.)
  5. JVisualVM: Nachgänger der JConsole ab 1.6


Begriffserklärungen: Zusammenfassung

Was Kurz-Erklärung
JDK (Java Development Kit) Sammlung von Programmen zur entwicklung von Java-Programmen (Bytecode) und dessen Komponenten
Open JDK Freie (GPL) Implementierung des Java Development Kits (JDK)
Java Compiler Wandelt Quellcode der Programmiersprache "Java" in Bytecode um
Javadocs Wandelt Java-Quellcode in eine HTML-Dokumentationswebseite um (Siehe zB. Javadocs der Java-SE API)
Java Archive ZIP-Datei mit allen wichtigen Metadaten eines Java Programms sowie dessen Dateien (kompilierte .class, Grafiken, ...)
Java Runtime Enviroment (JRE, Java Laufzeitumgebung) Ein Bündel aus der Java-API und der virtuellen Maschine (JVM) zum ausführen des Bytecodes.
Java Virtual Machine (JVM) Für die eigentliche Interpretierung sowie Ausführung eines Java-Bytecodes in einer Art "Virtuell Abgekapselten PCs"
Green Threads (Abgeschottete Threads) Damit zB. MultiThreading auch auf einem nicht-fähigen Gerät funktioniert, schottet die JVM die in ihr laufenden Prozesse vom Betriebssystem ab und behandelt sie eigenst mithilfe der JRE. (!= Systemthreads)

Setup

TODO

Java installieren

Das erste Programm

Entwicklungsumgebungen

Programmiersprache Java: Eigenschaften

Java als Programmiersprache sollte nicht mit der Java-Technologie gleichgesetzt werden.

Einführung

Java ist eine

Programmiersprache.

Java ist eine der populärsten Programmiersprachen. Konkurrierend mit C, stets auf Platz 1 oder 2 des Rankings. [2]

Grundkonzepte

Der Entwurf der Programmiersprache Java strebte hauptsächlich fünf Ziele an:

Einfachheit

Java ist im Vergleich zu anderen objektorientierten Programmiersprachen wie C++ oder C# einfach, da es einen reduzierten Sprachumfang besitzt und beispielsweise Operatorüberladung und Mehrfachvererbung von Klassen nicht unterstützt.

Vertrautheit

Wegen der syntaktischen Nähe zu C++, der ursprünglichen Ähnlichkeit der Klassenbibliothek zu Smalltalk und der Verwendung von Entwurfsmustern in der Klassenbibliothek zeigt Java für den erfahrenen Programmierer keine unerwarteten Effekte.

Robustheit

Viele der Designentscheidungen bei der Definition von Java reduzieren die Wahrscheinlichkeit ungewollter Systemfehler;

  • zu nennen sind die starke Typisierung, Garbage Collection, Ausnahmebehandlung sowie Verzicht auf Zeigerarithmetik.

Sicherheit

Siehe "Das Java-Security-Modell" und "Zeiger und Referenzen"

Dafür stehen Konzepte wie der Class-Loader, der die sichere Zuführung von Klasseninformationen zur Java Virtual Machine steuert, und Security-Manager, die sicherstellen, dass nur Zugriff auf Programmobjekte erlaubt wird, für die entsprechende Rechte vorhanden sind.

Architekturneutralität

Java wurde so entwickelt, dass dieselbe Version eines Programms prinzipiell auf einer beliebigen Computerhardware läuft, unabhängig von ihrem Prozessor oder anderen Hardwarebestandteilen.

Portabilität

Das heißt, dass primitive Datentypen sowohl in ihrer Größe und internen Darstellung als auch in ihrem arithmetischen Verhalten standardisiert sind.

  • Beispielsweise ist ein float immer ein IEEE 754 Float von 32 Bit Länge.
    • Dasselbe gilt beispielsweise auch für die Klassenbibliothek, mit deren Hilfe man eine vom Betriebssystem unabhängige GUI erzeugen kann.

Leistungsfähigkeit

Java hat aufgrund der Optimierungsmöglichkeit zur Laufzeit das Potential, eine bessere Performance als auf Compilezeit-Optimierungen begrenzte Sprachen (C++, etc) zu erreichen.

  • Dem entgegen steht der Overhead durch die Java-Laufzeitumgebung, sodass die Leistungsfähigkeit von beispielsweise C++-Programmen in einigen Kontexten übertroffen, in anderen aber nicht erreicht wird!

Parallelisierbarkeit

Java unterstützt Multithreading, also den parallelen Ablauf von eigenständigen Programmabschnitten.

  • (Moderne JVMs bilden einen Java-Thread auf Betriebssystem-Threads ab und profitieren somit von Prozessoren mit mehreren Rechenkernen.)

Objektorientierung

Quote von der Java-Insel[3]:

Objektorientierte Programmierung versucht, die Komplexität des Softwareproblems besser zu modellieren. Die Philosophie ist, dass Menschen objektorientiert denken und eine Programmierumgebung diese menschliche Denkweise abbilden sollte. Genauso wie Objekte in der realen Welt verbunden sind und kommunizieren, muss es auch in der Softwarewelt möglich sein. Objekte bestehen aus Eigenschaften; das sind Dinge, die ein Objekt »hat« und »kann«. Ein Auto »hat« Räder und einen Sitz und »kann« beschleunigen und bremsen. Objekte entstehen aus Klassen, das sind Beschreibungen für den Aufbau von Objekten.

Hackernoon-Eintrag: "10 OOP Design-Principles every Programmer should know"

Die Grundidee der objektorientierten Programmierung ist, Daten und zugehörige Funktionen möglichst eng in einem sogenannten Objekt zusammenzufassen und nach außen hin zu kapseln (Abstraktion).
Vorteile (Nur Beispiele):

  • große Softwareprojekte einfacher zu verwalten
  • hoher Grad der Wiederverwendbarkeit von Softwaremodulen.

Mehrfachvererbung

Java unterscheidet explizit zwischen Schnittstellen und Klassen.

  • Eine Klasse kann beliebig viele Schnittstellen implementieren, hat aber stets genau eine Basisklasse.
  • Java unterstützt kein direktes Erben von mehreren Klassen („Mehrfachvererbung“), jedoch die Vererbung über mehrere Hierarchie-Ebenen (Klasse Kind erbt von Klasse Vater, die ihrerseits von Klasse Großvater erbt usw.).
    • Je nach Sichtbarkeit (public, protected, default/package-private, private) erbt die Klasse Methoden und Attribute (auch Felder genannt) von ihren Klassenvorfahren.

Alle Klassen sind – direkt oder indirekt – von der Wurzelklasse Object abgeleitet.

Reflection

Java bietet eine Reflection-API als Bestandteil der Laufzeitumgebung.
Damit ist es möglich, zur Laufzeit auf Klassen und Methoden zuzugreifen, deren Existenz oder genaue Ausprägung zur Zeit der Programmerstellung nicht bekannt war.

Annotationen

Annotationen erlauben die Notation von Metadaten und ermöglichen bis zu einem gewissen Grad benutzerdefinierte Spracherweiterungen.

  • Sinn der Annotationen ist unter anderem die automatische Erzeugung von Code und anderen in der Software-Entwicklung wichtigen Dokumenten für wiederkehrende Muster anhand möglichst kurzer Hinweise im Quelltext.


Merkmale

Objektzugriff

Der Objektzugriff in Java ist über Referenzen implementiert, die den aus C oder C++ bekannten Zeigern ähneln.

  • Die Sprachdefinition (Java Language Specification) bezeichnet sie als „Reference Values“ um deutlich zu machen, dass sie als Call by value übergeben werden.

Pakete

Zusammengehörige Klassen werden in Paketen (englisch packages) zusammengefasst.
Diese Pakete ermöglichen die Einschränkung der Sichtbarkeit von Klassen, eine Strukturierung von größeren Projekten sowie eine Trennung des Namensraums für verschiedene Entwickler.

Threads, Exceptions und Garbage Collection

Weiter unterstützt die Sprache Threads (nebenläufig ablaufende Programmteile) und Ausnahmen (englisch Exception). Java beinhaltet auch eine automatische Speicherbereinigung (englisch garbage collector), die nicht (mehr) referenzierte Objekte aus dem Speicher entfernt.

Umfangreiche Bibliothek

Zu Java gehört eine umfangreiche Klassenbibliothek. Dem Programmierer wird damit eine einheitliche, vom zugrundeliegenden Betriebssystem unabhängige Schnittstelle (Application Programming Interface, API) angeboten.










Programmiersprache Java: Syntax

Java-Logo

Der Syntax von Java wurde stark von C/C++ abgeleitet.
Ungleich wie C++ gibt es keine Globalen Attribute/Funktionen. Jeder Code gehört eine Klasse an, und jeder Wert einem bestimmten Objekt.

  • Eine Ausnahme bilden die Primitive Datentypen, von denen es aber wieder eine gleichwertige "Wrapper-Klasse" (sog. Autoboxing), welche zB. einige nützliche Funktionen besitzen kann.

Die Programmiersprache Java bietet zur versimpelung der Sprache und Vermeidung von fatalen Programmierfehlern gewisse Programmierparadigmen wie das Überladen von Operatoren, Pointers oder unsignierte Integer nicht an.

Nützliche Links

The Java® Language Specification: Java SE 8

Wikipedia: Java-Syntax

Java ist auch nur eine Insel (Kostenloses Nachschlagwerk-Buch, aktuell zu Java SE 8)

Identifier

Ein identifier ist der Name eines Elements in einem Code.
Er kann folgende Zeichen besitzen:

  • Jeglichen Unicode-Character welches einen Buchstaben repräsentiert
  • Zahlen
  • Währungszeichen (Wie $)
  • Verbindungs-Interpunktionszeichen (Wie _)

Er kann nicht:

  • Mit einer Zahl anfangen
  • Den namen eines Java-Schlüsselworts, null oder true/false enthalten

Literalen

Ein Literal ist die Sourcecode-Darstellung eines:

Typ Beispiel Beschreibung (Wie)
Binär 0b11110101 0b gefolgt von einer binären Zahl
Octal 0365 0 gefolgt von einer oktalen Zahl
Hexadezimal 0xF5 0x gefolgt von einer Hexadezimalen Zahl
Dezimal 245 Dezimale Zahl
long 3000000000L 64-Bit lange Dezimale Zahl, gefolgt von einem L (Das kleine l sollte vermieden werden, weil es sehr der Nummer "1" ähneln kann.)
float 23.5F, .5f, 1.72E3F Dezimalbruch mit optionalem Exponentenindikator, gefolgt von F
double 23.5D, .5, 1.72E3D Dezimalbruch mit optionalem Exponentenindikator, gefolgt von D
char 'a', 'Z', '\u0231' Charakter oder Charakter-Escape, eingeschlossen in Quotations-Zeichen (')
boolean true, false
null reference null
String "Hello, World." Zeichenfolge und Zeichenumgehung, eingeschlossen in Anführungszeichen (")
Unicode-Charakter \u3876 \u gefolgt vom Hexadezimalen Unicode-Codepoint (Bis zu U+FFFF, aka. 65535)
Octal Escape \352 \ gefolgt von einer Oktale-Zahl die nicht 377 überschreitet
Zeilenumbruch \n
Carriage return \r
Neue Seite \f
Backslash \\
Singlequote \'
Double quote \"
Tabulator \t
Backspace \b

Seit Java SE 7 ist es für die bessere lesbarkeit möglich, bei Integer-Literalen Unterstriche (_) zwischen 2 Zahlen einzufügen. (zB. 145_608_987)

Code-Blöcke

Die Seperatoren { und } kennzeichnen einen neuen Code-Block und somit einen neuen Gültigkeitsbereich ("Scope").
Code-Blöcke können nur auf Attribute/Methoden zugreifen, die innerhalb des selben oder eines darüberliegenden Gültigkeitsbereich liegen. Beispiel:

static int x = 24;
int instanceVariable = 99;

public static void main(String[] args){
    int a;
    int x = 0; // Erlaubt, da sich diese "lokale/Methoden-innere" Variable "x" von der "statischen/Klassen-angehörigen" Variable "x" unterscheidet
    // (Die statische Variable wird natürlich nicht verändert, und kann immer noch durch "Klassenname.x" erreicht werden)

    System.out.println(instanceVariable); // Illegal. Die Variable "instanceVariable" ist eine Instanz-Variable, dH. sie gehört zu einem individuellen Objekt, und nicht zur Klasse (static) (Die main()-Methode ist statisch, und kann deshalb nur auf ihre statischen-Objekte zugreifen, wie zB. "x")

    {
        int b;
        a = 1; // Erlaubt, da "a" in einem höheren Gültigkeitsbereich ("Scope") liegt


        int a = 2; // Illegal, da es bereits eine Lokale-Variable "a" in einem höheren Gültigkeitsbereich ("Scope") bereits gibt
    }

    a = 2;
    b = 3; // Illegal, da die Variable "b" in einem höherliegendem Gültigkeitsbereich ("Scope") liegt und hiermit unbekannt ist


    int i = 0;
    for(int i = 0; i != 2; i++){} // Illegal, da eine Deklaration eines Bezeichners ("i") als lokale Variable einer Methode/Konstruktors/Initialisierungsblocks nicht innerhalb eines Parameters oder einer lokalen Variablen desselben Namens angezeigt werden darf
}

Kommentare

Die Programmiersprache Java besitzt 3 Arten von Kommentaren:

Block-Kommentare

"Traditionelle Kommentare", auch bekannt als "Block-Kommentare",

  • starten mit einem /*,
  • enden mit einem */ und
  • können sich über mehrere Zeilen spannen
/* Dies ist ein Kommentar-Block,
welcher sich auch über mehrere Zeilen strecken kann.*/

Einzeiler, End-of-Line

"End-of-Line" Kommentare starten mit einem // und "erweitern das ende der derzeitigen Zeile", da jedes Zeichen der Zeile nach dem // nur noch ein Kommentar sein kann..

// Dies ist ein End-of-Line Kommentar

Javadoc

Javadoc-Kommentare sind Kommentare die zur Beschreibung der darunterliegenden Struktur dienen sollen.

Wann sollte man Javadoc nutzen

Javadoc sollte zumindest für:

  • jede public Klasse und für
  • jedes public oder protected Mitglied einer solchen Klasse

genutzt werden. (Siehe Google Java Style-Guide)

Für simple Methoden, so wie einfache Getter/Setter die keinen neben-Effekt besitzen, und dessen Kommentar höchstwarscheinlich nur auf so etwas wie "Gets the foo" herauslaufen würden, braucht man keine Kommentare.

  • Achtung: Ausnhamen gibt es für Methoden, bei denen der Name vllt. nicht jedem herrkömmlichen Leser etwas sagt.
    • Beispiel: getCanonicalName (Damit der leser weiß, in welchem Sinn es sich zB. von getName entscheidet, und Anhand welcher Parameter der identische Name entschieden wird, oder was "Canonical" überhaupt heisst)

Javadoc-Struktur: Klasse

// import statements

/**
 * @author      Vorname Nachname <address@example.com>
 * @version     1.6          (Versionsnummer des Programmes, indem dieses Feature das letzte mal aktualisiert wurde)
 * @since       1.2          (Versionsnummer des Programmes, indem dieses Feature eingeführt wurde)
 */
public class Test {
    // Klasse
}

Javadoc-Struktur: Methode

Für Methoden sollte folgende Struktur verwendet werden:

  • (1) Eine kurze, präzise, einzeilige Beschreibung was die Methode macht [4]
  • (2) Eine größere Beschreibung welche sich auch über mehrere Paragraphen strecken kann. (Optional)
    • Jeder Absatz sollte mit einem <p>-Tag umhüllt sein [5]
    • Javadoc-Kommentare können auch alle anderen gültigen HTML-Tags für Texte besitzen (für z.B. Listen)
  • (3) Gefolgt von mehreren beschreibenden Tags (Annotationen), in folgender Reihenfolge
    • Parameter der Methode (@param)
    • Rückgabewert der Methode (@return)
    • Jegliche Fehlerklasse die diese Methode werfen könnte (@throws)
    • Andere, nicht überall genutzte Tags wie @see (Eine "siehe auch"-Tag)
/**
 * Kruze, einzeilige Beschreibung,                       (1)
 * <p> 
 * Falls es eine gibt, kommt hier die ganze Erklärung    (2)
 * rein.
 * </p>
 * Und noch mehr Erklärungen in aufeinanderfolgenden Absätzen, 
 * getrennt durch HTML-Absatzumbrüche.
 *
 * @param  variable Beschreibung der Variable...         (3)
 * @return Beschreibung des Rückgabewertes...
 */
public int methodName (...) {
    // Methoden-Scope mit einem return-Statement
}

Javadoc-Struktur: Variable

Variablen werden ähnlich wie Methoden dokumentiert, wobei der (3)-Teil mit den Tags weggelassen werden kann.

/**
 * Description of the variable here.
 */
private int debug = 0; // End-of-Line Kommentare zum kommentieren sollten nicht genutzt werden, weil sie von Javadoc und IDEs natürlich nicht beachtet werden


Übersicht der Javadoc-Tags

Tags kann man wie beschreibende @key value-Paare ansehen, die dem Programmierer schnell und aussagekräftig über Parameter/Version/Author/uvm. bescheidgeben.

  • Auch Entwicklungsumgebungen nutzen zB. die @param-Tags um den Nutzer beim Tippen anzuzeigen, wofür die Variable nochmal war. (IntelliJ Strg+Q)
Tag und Parameter Ausgabe Verwendung in seit
@author name Beschreibt den Autor. Klasse, Interface
@version version Erzeugt einen Versionseintrag. Maximal einmal pro Klasse oder Interface. Klasse, Interface
@since jdk-version Seit wann die Funktionalität existiert. Klasse, Interface, Instanzvariable, Methode
@see reference Erzeugt einen Link auf ein anderes Element der Dokumentation. Klasse, Interface, Instanzvariable, Methode
@serial Beschreibt die serialisierten Daten eines Serializable-Objekts. Klasse
@serialField Dokumentiert ein Feld eines Serializable-Objekts. Klasse, Methode
@param name description Parameterbeschreibung einer Methode. Methode
@return description Beschreibung des Rückgabewerts einer Methode. Methode
@exception classname description
@throws classname description
Beschreibung einer Exception, die von dieser Methode geworfen werden kann. Methode
@deprecated description Beschreibt eine veraltete Methode, die nicht mehr verwendet werden sollte. Sollte ab Java 5.0 immer mit der @Deprecated-Annotation verwendet werden. Methode
{@inheritDoc} Kopiert die Beschreibung aus der überschriebenen Methode. Überschreibende Methode 1.4.0
{@link reference} Link zu einem anderen Symbol. Klasse, Interface, Instanzvariable, Methode
{@linkPlain reference} Der Link wird in Standardtext statt in Quelltextzeichensatz angezeigt. Klasse, Interface, Instanzvariable, Methode 1.4.0
{@value} Gibt den Wert eines konstanten Feldes zurück. Statisches Feld 1.4.0
{@docRoot} Gibt den absoluten Pfad zum Hauptverzeichnis wieder. Package, Klassen, Felder, Methoden
{@code} Formatiert Text buchstabengetreu mit dem Quelltextzeichensatz (entsprechend <code>) und unterdrückt die Interpretierung von beinhalteten HTML oder Javadoc-Tags. Klasse, Interface, Instanzvariable, Methode 5.0
{@literal} Kennzeichnet buchstabengetreuen Text und unterdrückt die Interpretierung von beinhalteten HTML oder Javadoc-Tags. Klasse, Interface, Instanzvariable, Methode 5.0

Um das Symbol „@“ zu verwenden, ohne ein Javadoc-Tag zu beginnen, kann der HTML-Zeichen-Code „&#064;“ verwendet werden.

  • Dies ist beispielsweise nützlich, um in einem Code-Beispiel innerhalb eines Javadoc-Kommentars Java-Annotationen zu verwenden, die wie ein Javadoc-Tag mit einem „@“ beginnen.


Variablen

Variablen sind Identifikatoren, welche man einen bestimmten Wert zuweisen kann.

[Modifikatoren] <Typ> <variablenName> [= <Wert>];
[Modifikatoren] <Typ>[] <variablenName> [= <Wert>];
<variablenName> = <Wert>;

Beispiel:

int foo;        // Deklarierung der Variable mit dem Namen "foo" vom Typen "int" (Ganzzahl)
foo = 35;       // Intialisierung/Setzen des Wertes der Variable "foo"
int bar = 35;   // Deklarierung und Intialisierung der Variable in einer Zeile

Mehrere Variablen des gleichen Typens können, mithilfe eines Kommas zur trennung, in einer Zeile deklariert und intialisiert werden.

int a, b;           // Deklaration mehrerer Variablen vom Typen "int" in einer Zeile
int a = 2, b = 3;   // Deklaration und Intialisierung mehrerer Variablen vom Typen "int" in einer Zeile

Arten von Variablen:

class Point {
    static int numPoints = 24;   // "numPoints" is a class variable of type int - Bound to the class, aka. same for each object and available using only the class-name
    int x, y;               // "x" and "y" are instance variables of type int - Each individual object has it's own values of this variable(s)
    int[] w = new int[10];  // "w[0]"" is an instance variable of type array component - Same as above

    // Constructor
    Point(int xIn, yIn){ // "xIn" and "yIn" are constructor parameters - passed when creating the object
        // Intializing the values "x" and "y" of this new Object using the given constructor-Parameters
        this.x = xIn; 
        this.y = yIn;

        try { 
            /*Code*/ 
        } catch(Throwable ex){ // "ex" is a exception-parameter - only available in the following Code-Block, passed when an exception occurs
            /* Exception-Handle Code*/
        } 
        Arrays.asList(1, 2, 3).forEach(obj -> System.out.println(obj)); // "obj" is a lambda parameter - Only available in the lambda
    }

    // Instance Method - Only available through an individual object-instance
    int setX(int xIn) {       // "xIn" is a method parameter - passed when invoking this method
        int oldx = this.x;  // "oldx" is a local variable - only available in this and underlying block-scopes
        this.x = xIn;
        return oldx;
    }
}

In den folgenden Texten bedeuted "Nicht mehr vorhanden", dass die Variablen/Parameter vom Garbage Collector bereinigt werden, und somit nicht-mehr im Speicher vorhanden sind.

Klassen-Variablen

Eine Klassenvariable ist ein Feld, das mit dem Schlüsselwort static innerhalb einer Klassendeklaration (§8.3.1.1) oder mit/ohne dem Schlüsselwort static innerhalb eines interface (§9.3) deklariert wird.

Eine Klassenvariable wird erstellt, wenn ihre Klasse oder Schnittstelle vorbereitet wird (§12.3.2) und mit einem Standardwert (§4.12.5) initialisiert wird.

  • Die Klassenvariable existiert praktisch nicht mehr, wenn ihre Klasse oder Schnittstelle entladen wird (§12.7).

Instanz-Variablen

Eine Instanzvariable ist ein Feld, das innerhalb einer Klassendeklaration deklariert wird, ohne das Schlüsselwort static (§8.3.1.1) zu verwenden.

  • Die Instanzvariable ist praktisch nicht mehr vorhanden, wenn
    • auf das Objekt, von dem sie ein Feld ist, nicht mehr referenziert wird
    • und nachdem die erforderliche Finalisierung des Objekts (§12.6) abgeschlossen wurde.

Siehe Abschnitt "Programmiersprache Java: OOP -> Typen -> Objekt" für weitere Informationen in Bezug auf Objekte/Instanzen.

Lokale-Variablen

Eine lokale Variable ist eine in einer Methode deklarierte Variable.
Anders wie Klassen/Instanz-Variablen spielt die Position der Variable im Code-Block eine Rolle: Nur darunterstehende Code-Zeilen können darauf zugreifen.

  • Die lokale Variable existiert praktisch nicht mehr, wenn die Ausführung des Blocks oder der Anweisung abgeschlossen ist.

Array-Komponenten

Array-Komponenten sind unbenannte Variablen, die erstellt und mit Standardwerten (§4.12.5) initialisiert werden, wenn ein neues Objekt, das ein Array ist, erstellt wird (§15.10.2).

  • Die Array-Komponenten sind praktisch nicht mehr vorhanden, wenn auf das Array nicht mehr verwiesen wird.

Methoden/Konstruktor/Lambda-Parameter

benennen Argumentwerte, die an eine Methode, einen Konstruktor oder einer Lambda-Funktion übergeben werden können.

Für jeden in einer <Methoden/Konstruktor/Lambda>-deklaration definierten Parameter wird bei jedem Aufruf eine neue "Parametervariable" erstellt, welche mit dem entsprechenden Argumentwert aus dem <Methoden/Konstruktor>-aufruf initialisiert wird.

  • Die Paramter-variablen sind praktisch nicht mehr vorhanden, wenn die Ausführung des entsprechenden Code-Blocks beendet wurde.

Exception-Parameter

Ein "Ausnahmeparameter" wird jedes Mal erstellt, wenn eine Ausnahme von einer catch-Klausel einer try-Anweisung abgefangen wird. (§14.20)

Die neue Variable wird mit dem tatsächlichen Objekt initialisiert, das der Ausnahme zugeordnet ist. (§11.3, §14.18)

  • Der Ausnahmeparameter ist praktisch nicht mehr vorhanden, wenn die Ausführung der catch-Klausel zugeordneten Blocks abgeschlossen ist.

final-Variablen

Einer finalen Variable darf nur einmal ein Wert zugewisen werden.

  • Wenn eine finale Variable ein Objekt enthält, kann der Status des Objekts durch Operationen am Objekt geändert werden, die Variable verweist jedoch immer auf die selbe Objekt-Instanz!

Es gibt 3 Typen von Variablen, die explizit final sind:

  • Ein Feld eines interfaces (§9.3)
  • Eine lokale-Variable einer try-with-resource (§14.20.3)
  • Ein Exception-Parameter einer multi-catch-Klausel (§14.20)


Standard-Werte für Variablen

Jede Klassenvariable, Instanzvariable und Array-Komponente erhält bei der Deklarierung ohne angegebenen Wert (Beispiel: int x;) einen Standard-Wert:

Typ Standardwert
byte (byte)0
short (short)0
int 0
long 0L
float 0.0f
double 0.0d
char \u0000 (null character, empty)
jegliche Referenz (§4.3) null
  • Jeder Methoden/Lambda-Parameter (§8.4.1) wird mit dem entsprechenden Argumentwert initialisiert, der vom Aufrufer der Methode (§15.12) bereitgestellt wird.
  • Jeder Konstruktor-Parameter (§8.8.1) wird mit dem entsprechenden Argumentwert initialisiert, der von einem Klasseninstanz-erstellungsausdruck (new, §15.9) oder einem expliziten Konstruktoraufruf (super/this, §8.8.7) bereitgestellt wird.
  • Ein Exception-Parameter (§14.20) wird für das ausgelöste Objekt initialisiert, das die entsprechende Ausnahme darstellt (§11.3, §14.18).

Eine

müssen vor ihrer Verwendung explizit einen Wert erhalten. (entweder durch Initialisierung (§14.4) oder durch Zuweisung (§15.26)).

class Test {
    public static void main(String[] args) {
        System.out.println("npoints=" + Point.npoints); // npoints=0
        Point p = new Point();
        System.out.println("p.x=" + p.x + ", p.y=" + p.y); // p.x=0, p.y=0
        System.out.println("p.root=" + p.root); // p.root=null

        int num;
        System.out.println(num); // Illegal, da die lokale Variable "num" einen Wert besitzten MUSS
        
        for (int i; i != 0; i++){} // Illegal, da die lokale Variable "i" einen Wert besitzten MUSS
    }
}

class Point {
    static int npoints;
    int x, y;
    Point root;
}

Methoden

Alle Anweisungen in Java müssen sich innerhalb von Methoden befinden.
Methoden sind wie Funktionen, außer dass sie zu Klassen gehören.
Jede Methode hat:

  • einen Rückgabewert,
  • einen Identifikator (Namen) und normalerweise
  • einige Parameter, die beim Aufruf mit einigen Argumenten initialisiert werden.

Ähnlich wie in C++ haben Methoden, die nichts zurückgeben, einen als ungültig deklarierten Rückgabetyp namens void.

Methoden deklarieren

[@Annotation1]
[@Annotation2...]
[Sichtbarkeit] [static] [Modifikatoren] <Rückgabewert> <methodenName>([Parameter1][, Paramter2], ...) [throws <Exception1>, <Exception2>, ...] {
    // Code
    [return <Rückgabewert>;]
}

Syntax von Parametern: [@<Annotation> [@<Annotation2>] ...] [final] <ParameterTyp> <parameterName>

Beispiel:

public static void sayHello(String name){
    System.out.println("Hallo, " + name + "!");
}
public static boolean isCool(@NotNull final String name){
    return name.equalsIgnoreCase("PixelTutorials");
}

Methoden überladen

Im Gegensatz zu C ++ dürfen Methoden in Java keine Standard-Argumentwerte haben. Stattdessen können die Methoden überladen werden:

class Foo {
    int bar(int a, int b) {
        return (a*2) + b;
    }

    /* Overloaded method with the same name but different set of arguments */
    int bar(int a) {
        return bar(a, 0);
    }
}

Multi-Methods

Der folgende Code ruft, wenn man ihn mit der Programmiersprache Java kompiliert und ausführt, die Methode mit dem Parameter Object auf, obwohl der Wert von o eigentlich auf einen String gesetzt wurde.

  • Das liegt daran, dass die Programmiersprache Java die Auswahl, welche Methode aufgerufen werden soll, beim kompilieren anhand von statischen Objekt-Informationen her-nimmt. Also achtet Java nur auf den Variablen-Typen, und nicht auf welchen Wert er zur Laufzeit gesetzt wurde (in diesem Fall ist der Typ Object)
    • (Bei Groovy wird dies hingegen zur Laufzeit anhand des derzeitigen Objekt-Types entschieden, und würde hier somit "1" ausgeben. Siehe Groovy Differences)
public static void main(String[] args){
    Object o = "Object";
    // System.out.println(o instanceof String); // true
    System.out.println(method(o)); // 2
}
static int method(String arg) { return 1; }
static int method(Object arg) { return 2; }

Varargs

Ab JDK 5 ist es möglich, eine Methode zu erstellen die eine variable-Anzahl ("variable-length arguments", kurz varargs) an Argumenten übernehmen kann.

  • Diese Werte stehen der Methode als primitive Array ([]) zur Verfügung, und kann auch leer sein!
  • Nur der letzte Parameter einer Methode kann die vararg-Eigenschaft besitzen

Davor gab es 2 Möglichkeiten, ein ähnliches Result zu erzielen:

  • Für jede Anzahl an Parametern eine eigene Methode schreiben
public static void main(String[] args) {
    add(1, 2);
    add(1, 2, 3);
    add(1, 2, 3, 4); // Nachteil: Nur weil ich einen Parameter mehr übergeben will, müsste ich eine neue Methode erstellen - sinlos
}

public static int add(int a, int b) { return a+b; }
public static int add(int a, int b, int c) { return a+b+c; }
  • Immer eine Array ([]) als Argument zu fordern
public static void main(String[] args) {
    add(new int[0]); // Nachteil: Mann muss immer selbst eine leere Array-Instanz angeben
    int[] arr = {1, 2, 3, 4}; // Nachteil: Mann muss immer zuvor ein Array-Objekt selbst erstellen
    add(arr);
}

public static int add(int[] numbers) {
    int sum = 0;
    for(int num : numbers) { sum += num; }
    return sum;
}

Mit varargs:

public static void main(String[] args) {
    add(); // Erlaubt (leer)
    add(1, 2, 3, 4); // Erlaubt

    int[] arr = {1, 2, 3, 4}; 
    add(arr); // Erlaubt (Für die Methode ist der Parameter ja am Ende immer vom Typen "int[]")

    List<Integer> list = Arrays.asList(1, 2, 3, 4);
    add(list); // Illegal - Methode erwartet eine int[] oder eine variable-Anzahl an int's, aber keine Liste!
}

public static int add(int... numbers) {
    int sum = 0;
    for(int num : numbers) { sum += num; }
    return sum;
}

Ein gutes Beispiel, wie varargs genutzt werden können, findet man in der Methode format(String format, Object... args) vom String-Objekt.

Main-Methode

Jedes Projekt braucht einen Startpunkt. In Java ist dies die sog. main-Methode, welche wie folgt aussehen muss (Nur der Parametername "args" kann abgeändert werden).

public static void main(String[] args){
    // Code
}
// Oder
public static void main(String... args){
    // Code
}

Start-Argumente

Beim starten einer Java-Anwendung kann der Benutzer eine Liste an Parametern übergeben, die dem Programmierer mithilfe der main-Methode in Form einer String[] übergeben wird. Beispiel:

public class Test {
    public static void main(String[] args){
        for(String s : args){ System.out.println("'"+s+"'"); }
    }
}
$ javac Test.java 
$ java Test 1 2 3
'1'
'2'
'3'
$ java Test "Hello World" "Pankaj Kumar"
'Hello World'
'Pankaj Kumar'
$ java Test
$

Operatoren

Arithmetische Operatoren (Rechnen)

Zuordungs Operatoren (Wert verändern)

Relationale Operatoren (Vergleichen)

Bitweise Operatoren (Bitmanipulation)

Logische Operatoren (True, wenn)

Weitere Operatoren

Operatoren überladen/überschreiben

Kontrollstrukturen

Kontrollstrukturen sind Anweisungen in imperativen Programmiersprachen, die dazu dienen den Ablauf eines Computerprogramms zu steuern.

Der Anweisungs-Block wird nur dann ausgeführt, sofern die Bedingung den boolschen-Wert true ergibt.

Bedingte Anweisungen

if-else_if-else

if-else Einzeiler

switch-case

Schleifen

while

do-while

for-i

for-each

Sprung Anweisungen

Labels

break

continue

return

Fehlerbehandlung

try-catch-finally

try-with-resource

throw

assert

Thread-Parallelitätssteuerung

Universelle Datentypen - java.lang

Klassen im Packet java.lang müssen nicht implizit durch eine import-Anweisung importiert werden.

java.lang.Object

Jede Klasse besitzt java.lang.Object als ihre Superklasse (Erbt von dieser).
Alle Werte können in diesen Typen konvertiert werden, bei Primitiven-Datentypen geht dies aber nur durch sog. Autoboxing:

int number = 24;
Integer boxedNumber = new Integer(number);
System.out.println(boxedNumber.getClass().getCanonicalName()); // "java.lang.Integer"

Siehe §4.3.2 für mehr Informationen zur Klasse Object.

java.lang.String

Java's grundlegender String-Typ. (§4.3.3)

Der String-Pool

Wenn wir einen String mit doppelten Anführungszeichen erstellen, sucht Java zuerst nach dem String mit demselben Wert im JVM-String-Pool.

  • Wenn er gefunden wird, wird der Verweis auf diese Instanz zurückgegeben.
  • Andernfalls wird ein neues String-Objekt erstellt ("x" wird = new String("x")) und in den String-Pool eingefügt.

Auf diese Weise spart die JVM viel Platz, wenn derselbe String in verschiedenen Threads verwendet wird.
Wenn jedoch ein neuer Operator an einem String verwendet wird, wird explizit ein neuer String im Heap-Speicher erstellt.

Siehe auch baeldung.com: "Guide to Java String Pool"

Unveränderbar (Immutable)

Da ein String-Objekt unveränderbar ist, erzeugt jede String-Methode (zB. toLowerCase()) ein neues String-Objekt.

  • Also nur myString.replace("A", "B"); aufzurufen wird nichts am String myString verändern.

Um einem String einen neuen Wert zu geben, muss man es explizit auf eine neue String-Instanz zeigen lassen:

  • myString = myString.replace("A", "B");. (Im Hintergrund wird daraus myString = new String(myString.replace("A", "B"));!)
    • (Hierbei wird die alte String-Instanz für den Garbage Collector markiert)

StringBuilder: Strings konkatieren

Die StringBuilder-Klasse stellt eine (im Gegensatz zum String) veränderbare Kette aus Zeichen dar, welche von Java zur Manipulation von Strings genutzt wird.

  • Wenn man hier append(..) (oA.) aufruft, ändert sich das interne char-Array des StringBuilders, anstatt das eine neue String-Instanz erstellt und direkt zurückgegeben wird.

Die StringBuilder-Klasse benutzt das sog. "Builder"-Paradigma:

  • jede Methode, die etwas am Objekt verändert, gibt (anstatt void) sich selbst (this) zurück.
    • Hierdurch werden Einzeiler wie im folgendem Code gar erst möglich


Was im Hintergrund passiert: Erklärung mit Beispielen

Wenn man Strings konkatiert (zusammenfügt), wird beim Kompilier-Prozess

String d = a + b + c;

automatisch in

String d = new StringBuilder().append(a).append(b).append(c).toString();

"umgewandelt". (§4.3.3: "The string concatenation operator + (§15.18.1) implicitly creates a new String object when the result is not a constant expression (§15.28).")

  • Da hier nur 1 StringBuilder sowie String-Instanz erstellt wird, muss man sich beim normalen konkatieren also keine sorgen machen.
  • Das Problem, das bei jeder Konkentation eine neue StringBuilder- sowie String-Instanz erstellt wird, wird erst bei Schleifen bemerkbar:
int total = 50000;

String s = ""; 
for (int i = 0; i < total; i++) { 
    // Im Hintergrund wird bei jedem Schleifen-durchgang eine neue StringBuilder- sowie String-Instanz erstellt
    // (`s = new StringBuilder().append(s).append(String.valueOf(i)).toString();`)
    s += String.valueOf(i); 
} 
// 4828ms

Weswegen man hierbei auf den +(=) Operator verzichten sollte:

StringBuilder sb = new StringBuilder(); // Nur eine StringBuilder-Instanz wird (von uns) erstellt
for (int i = 0; i < total; i++) { sb.append(String.valueOf(i)); } 
// (Am Ende kriegt man den gebauten String einfach mit `sb.toString()` zurück)
// 4ms
Was im Hintergrund passiert: Im Bytecode

Zur Veranschaulichung, dass beim konkatieren mithilfe vom "+"-Operator sich eigentlich immer eine neue eigene Instanz der StringBuilder-Klasse sich darum kümmert, habe ich folgendes Java-Programm mithilfe von IntelliJ "[View -> Show Bytecode]" ausgeben lassen und kommentiert.

...
9     String str1 = "Hallo ";
10    String str2 = "Welt";
11 
12    String result = str1 + str2;
13    System.out.println(result);
...

Das Ergebniss: (Anfängliche Intialisierungsschritte <init>()V und Dekonstruktions-Operatoren des Garbage-Collectors wurde weg-gelassen, farbige kommentare ("//") sind von mir:)

...
public static main([Ljava/lang/String;)V
 L0       // Variable "str1" wird in die lokale-Variable "1" gespeichert: 
  LINENUMBER 9 L0
  LDC "Hallo "
  ASTORE 1
 L1       // Variable "str1" wird in die lokale-Variable "2" gespeichert: 
  LINENUMBER 10 L1
  LDC "Welt"
  ASTORE 2
 L2        // String-Konkentation mithilfe des StringBuilders erfolgt und das Ergebniss wird in die lokale Variable "3" gespeichert: 
  LINENUMBER 12 L2
           // Neue StringBuilder-Instanz wird erstellt und für die nächste Operationen, die ein Objekt benötigen (zB. "INVOKEVIRTUAL"), auf den Stack gelegt 
  NEW java/lang/StringBuilder
  DUP
  INVOKESPECIAL java/lang/StringBuilder.<init> ()V       
  ALOAD 1         // Lädt eine Referenz von der lokalen-Variablen 1 (geladen vom Stack. Wert: "Hallo ") auf den Stack. 
  INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;         // Ruft die Methode StringBuilder#append() des vorher auf den Stack gelegten StringBuilders auf. Parameter werden ebenfalls vom Stack entnommen ("Hallo "). Das Resultat (StringBuilder) wird für die nächste Objekt-Operation wieder auf den Stack gelegt. 
  ALOAD 2         // Lädt eine Referenz von der lokalen-Variablen 2 (geladen vom Stack. Wert: "Welt") auf den Stack
  INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;      // Ruft die Methode StringBuilder#append() des vorher auf den Stack gelegten StringBuilders auf.  Parameter werden ebenfalls vom Stack entnommen ("Welt"). Das Resultat (StringBuilder) wird für die nächste Objekt-Operation wieder auf den Stack gelegt. 
  INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;  // Ruft die Methode StringBuilder#append() des vorher auf den Stack gelegten StringBuilders auf.  Das Resultat wird als Wert für die nächste "ASTORE"-Operation auf den Stack gelegt. 
  ASTORE 3  // Speichert eine Referenz des ebens auf den Stack-geladenen Objekts ("Hallo Welt") in die lokalen Variable "3" 
 L3        // Ausgabe des gerade konkantierten Strings, gespeichert in der lokalen-Variable "3": 
  LINENUMBER 13 L3
  GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  ALOAD 3
  INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
 ...

Die englische-Version von Wikipedia besitzt eine eigene Seite mit allen möglichen Bytecode-Operationen, welche ich auch für meine Kommentare genutzt habe: https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings


StringBuilder vs StringBuffer

StringBuffer war die einzige Wahl für die String-Manipulation bis Java 1.4, jedoch mit den Nachteil, dass alle öffentlichen Methoden synchronized sind.

  • StringBuffer bietet somit Thread-Sicherheit für Perfomance-Verluste
    • Da wir in den meisten Szenarien keine String-Manipluation in einem Mutlithread-Umgebung durchführen, führte Java 1.5 eine neue Klasse StringBuilder ein, die (mit Ausnahme von Threadsicherheit und -synchronisierung) dem StringBuffer sehr ähnelt.
  • Der StringBuffer bietet Funktionen wie substring, length, capacity, trimToSize (etc.)
    • Da die String-Klasse diese Funktionen aber bereits bietet, wurden sie beim StringBuilder weggelassen.

Siehe journaldev.com: "Performance-Benchmark: StringBuffer vs. StringBuilder in Java 1.5, 1.6 und 1.8" und baeldung.com: "StringBuilder and StringBuffer in Java"

  • 20%~ mehr Perfomance für den StringBuilder

Hervorhebende Methoden

Eigenschaft Beschreibung
static String valueOf(Object obj) Wenn das Argument null ist, wird "null" zurückgegeben. Anderenfalls obj.toString().
  • Für primitive-Datentypen (darunter auch char[]'s) gibt es auch noch eigene Implementierungen.
String.valueOf(123); // "123"
String.valueOf(null); // "null"
String a.toUpperCase()

String a.toLowerCase()

Den String, konvertiert in Groß/Klein-buchstaben.
"MiKa moder!".toLowerCase(); // "mika moder!"
"MiKa moder!".toUpperCase(); // "MIKA MODER!"
int a.length() Gibt die Anzahl an 16-Bit Charakteren vom String a zurück. (Siehe auch CharSequence.html#length)
"Mika".length(); // 4
boolean a.isEmpty() Gibt true zurück, wenn a.length() 0 ergibt
"".isEmpty(); // true
"         ".isEmpty(); // false
"         ".trim().isEmpty(); // true
boolean a.equals(Object anObject)

boolean a.equalsIgnoreCase(String anotherString)

Gibt true zurück, wenn die String-Präsenz von anObject, mit diesem String übereinstimmt. Folgendes ist hierbei zu beachten:
  • Wenn anObject nicht vom Typen String, wird es mithilfe von anObject#toString() umgewandelt
  • Man sollte String#equalsIgnoreCase (String) anstatt #equals benutzen, wenn einem die Groß/Klein-schreibung nicht interessiert
  • (Immer .equals verwenden, da == nur schaut ob beide auf die gleiche Instanz zeigen)
"Hallo".equalsIgnoreCase("Hallo"); // true
"Hallo".equalsIgnoreCase("HALLO"); // true

"Hallo".equals("HALLO"); // false
"Hallo".equals("Hallo"); // true

"Hallo".equals("Hallo   "); // false
"Hallo".equalsIgnoreCase("Hallo   "); // false
"Hallo".equalsIgnoreCase("Hallo   ".trim()); // true
String a.trim() Gibt eine Kopie von diesem String zurück, in der alle führenden und nachgestellten Leerzeichen entfernt werden.
"Hallo, Welt!   ".trim() // "Hallo, Welt!"
char[] a.toCharArray() Gibt eine Kopie dieses Strings als char[]-Representation zurück. (Eine Array, in der jeder Eintrag ein Buchstabe ist.)
"Hallo".toCharArray(); // [H, a, l, l, o]
String a.substring(int beginIndex, int endIndex) Gibt einen Kopie des Strings zurück bei welchem alle Zeichen vor der beginIndexten-Stelle sowie auch nach der endIndexten-Stelle weggelassen wurden
//1234567
 "Hallo, mein Name ist Jonas.".substring(7); // "mein Name ist Jonas."
 "Hallo, mein Name ist Jonas.".substring(21, 26); // "Jonas"
boolean a.startsWith(String prefix)

boolean a.endsWith(String suffix)

Gibt true zurück, wenn dieser String mit der exakt selben Zeichenkette wie prefix/suffix startet/endet.
"Hallo".startsWith("hallo"); // false
"HaLLO".toLowerCase().startsWith("hallo"); // true
String[] a.split(String regex) Teilt den String in eine String[] auf. (soz. in Silben) Siehe Beispiel:
"Hallo, mein Name ist Jonas.".split(" "); // ["Hallo,", "mein", "Name", "ist", "Jonas."]
"Hallo".split(""); ["H", "a", "l", "l", "o"]
a.replace(String regex, String replacement) Gibt eine Kopie dieses Strings zurück, bei welchem alle Vorkommnise von regex durch replacement ersetzt wurde
"Mika ist cool. Ich bin cool.".replace("cool", "uncool"); // "Mika ist uncool. Ich bin uncool."
"Mika ist cool. Ich bin cool.".replaceFirst("cool", "uncool"); // "Mika ist uncool. Ich bin cool."

java.lang.Throwable

Superklasse aller Fehler/Exceptions.

  • Jede Exception, die nicht von RuntimeException oder Error erben, wird als checked-Exception angesehen. (Fehler, die gefangen werden müssen)

Hervorhebende Eigenschaften eines Throwable-Objekts:

Eigenschaft Beschreibung
StackTraceElement[] getStackTrace() Liefert eine primitive Liste der letzten Methoden-Aufrufe in Form von StackTraceElementen zurück.

Jedes StackTrace-Element bietet auskunft über: (Alle Methoden beziehen sich auf den "von diesem Stack-Trace-Element dargestellten Ausführungspunkt")

printStackTrace() Gibt eine Übersicht der letzten Methoden-Aufrufe innerhalb der Konsole in Textform aus.
Beispiel:
package at.pixeltutorials.playground;

public class Main {
    public static void main(String[] args) {
        machEtwasMit(null);
    }

    public static void machEtwasMit(String input){
        /*
         * Hier kann eine NPE auftreten, wenn der Parameter "input" null ist.
         * Dar die "NullPointerException" eine "RuntimeException" ist, müssen wir allerdings keinen explizite Fehlerbehandlung machen.
         *
         * Das Programm nimmt einfach den erst-niedrigsten "try-catch" Block zur Fehlerbehandlung.
         * Falls es wie hier keinen gibt, "gilt das Programm sozusagen als try-catch". 
         *  > Das Programm wird mit Fehler-Code 1 beendet, und die StackTrace-Elemente mittels Throwable#printStackTrace ausgegeben.
         */
        System.out.println(input.toLowerCase());
    }
}

Ausgabe beim Ausführen:

Exception in thread "main" java.lang.NullPointerException
	at at.pixeltutorials.playground.Main.machEtwasMit(Main.java:9)
	at at.pixeltutorials.playground.Main.main(Main.java:5)

Process finished with exit code 1
getCause() Gibt die Ursache dieses Throwables (oder null wenn die Ursache nicht vorhanden oder unbekannt ist) zurück.
getMessage() Gibt die detailierte Beschreibung des Throwables zurück. (Beim Konstruktor übergeben)

Für diese Klasse gibt es 3 Konstruktoren:

Primitive Datentypen

Typ Wrapper-Klasse Standardwert Größe min. Größe max. Größe
boolean java.lang.Boolean false ? (eig. Größe nicht genau definiert) false true
byte java.lang.Byte (byte)0 8-bit -127 +128
short java.lang.Short (short)0 16-bit -32_768 (-2<up>15</up>) +32_767 (2<up>15</up>-1)
int java.lang.Integer 0 32-bit -2_147_483_648 (-2<up>31</up>) +2_147_483_647 (2<up>31</up>-1)
long java.lang.Long 0L 64-bit -9_223_372_036_854_775_808 (-2<up>64</up>) +9_223_372_036_854_775_807 (2<up>63</up>-1)
float java.lang.Float 0.0f 32-bit (-/+)340_282_346_638_528_860_000_000_000_000_000_000_000.000000 (-3.4028235E38)
double java.lang.Double 0.0d 64-bit

(-/+)179_769_313_486_231_570_000_000_000_000_000_000_000_000_000_000_000_000
_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_
000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_
000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_
000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_
000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000.000000

char java.lang.Character \u0000 (null character, empty) 16-bit '\u0000' (0) '\uffff' (65,535)
jeglicher Referenztyp (§4.3) java.lang.Object null

Typenumwandlung

Call by Value VS Call by Reference

Boxing und unboxing

Überblick

Primitive Arrays

2/3-Dimensionale Arrays

Programmiersprache Java: OOP

Typen

Die Programmiersprache Java ist eine statisch sowie stark typisierte Sprache.

  • statische Typisierung bedeutet, dass jede Variable und jeder Ausdruck einen Typ besitzt, der zur Kompilierzeit bekannt ist.
  • starke Typisierung bedeutet, dass der Typ den Wert limitiert, welche von einer Variable angenommen werden kann.
  1. Primtive Datentypen (§4.2), darunter:
    1. boolean (Boolscher Wert, entweder true oder false)
    2. numerische Typen
      1. byte, short, int, long, und char für Ganzzahlen (§4.2.1),
      2. float and double für Fließkommazahlen (§4.3.2)
  2. Referenz-Typen (§4.3), darunter:
    1. Klassen (Classes)
    2. Schnittstellen (Interfaces)
    3. Ansammlungen (Arrays)
    4. null

Siehe [#Primitive_Datentypen] für eine detailiertere Auflistung der verfügbaren Typen. (min/max.-Größe, Standardwert, Wrapper-Klasse)

Objekt

Ein Objekt ist eine dynamisch erstellte Instanz eines Referenz-Typen.

  • Die Werte eines Referenztyps sind Referenzen auf Objekte.
  • Alle Objekte, einschließlich Arrays, unterstützen die Methoden der Klasse Object (§4.3.2).
  • String-literale ("") werden als String-Objekt repräsentiert.

Objekt-Operatoren

Die Operatoren, die an Objekten angewandt werden können, sind:

Konstruktor

Ein Konstruktor ist eine spezielle Methoden, die aufgerufen wird um ein Objekt/Instanz einer Klasse zu erstellen.

  • Wie bei Methoden kann einem Konstruktor auch Parameter übergeben werden, mit dem dieser dann zB. die Werte des Objekts intialisiert.

Syntax einer Konstruktor-Deklaration (Siehe Constructor Modifier und ParameterList):

Annotationen
[Sichtbarkeit] <Klassenname>(ParameterList){
    [super(ParameterList);] // Konstruktor von Vater-Klasse aufrufen
    // Constructor-Method Body
}

Beispiel:

public class Test {
    public static void main(String[] args){
        // Neues Objekt von der Klasse "Point3D" mithilfe des Konstruktors erstellen und in die Variable "myPoint" speichern
        Point3D myPoint = new Point3D(1, 2, 3);
        /** Ausgabe:
         * Point2D-Konstruktor aufgerufen. { x=1, y=2 }
         * Point3D-Konstruktor aufgerufen. { z=3}
         */
    }
}

class Point2D {
    int x, y;

    public Point2D(int xIn, int yIn) { // Konstruktor-Deklaration
        System.out.println("Point2D-Konstruktor aufgerufen. { x="+xIn+", y="+yIn+" }");
        // Objekt-Variablen mithilfe der übergebenen Parameter-Variablen initialisieren:
        this.setX(xIn); // Innerhalb des Konstruktors kann man zudem auch schon Methoden aufrufen, die zu diesem Objekt gehören
        this.y = yIn;
    }

    void setX(int xIn){
        this.x = xIn;
    }
}

class Point3D extends Point2D {

    int z;

    public Point3D(int xIn, int yIn, int zIn) { // Konstruktor-deklaration von Point3D
        super(xIn, yIn); // Konstruktor von Erbklasse "Point2D" aufrufen
        System.out.println("Point3D-Konstruktor aufgerufen. { z="+zIn+"}");
        // Objekt-Variablen mithilfe der übergebenen Parameter-Variablen intialisieren:
        this.z = zIn;
    }
}

Neues Objekt erstellen

Um ein neues Objekt eines Referenztypen zu erstellen, muss man mithilfe von new den Konstruktor der Klasse aufrufen.
Syntax:

new Referenztyp[<Generic>]([ArgumentList])

Beispiel (Variable namens "list" vom Typen List<String> auf eine neue Instanz vom ArrayList-Objekt zeigen lassen):

List<String> list = new ArrayList<String>();

Intializers

Instantiierung eines Objekts

Konstruktor und Klassen/Objekt-Intializers

Felder eines Objektes bekommen

Klassen-Modifikatoren

Sichtbarkeit-Modifikatoren

Die Zugriffsmodifizierer legen den Zugriff auf Klassen, Methoden und andere Mitglieder fest.

Die folgende Tabelle zeigt, ob Code innerhalb einer Klasse zugriff auf eine andere Klasse/dessen Member besitzt.
(Abhängig von der Packet-zugehörigkeit von der aus zugegriffen wird)

Modifizierer Gleiche oder eingebettete (nested) Klasse Andere Klassen im gleichen Packet Erbende Klassen in anderen Packeten Nicht-Erbende Klassen in anderen Packeten
private ja nein nein nein
default (package private) ja ja nein nein
protected ja ja ja nein
public ja ja ja ja

Anders gesagt:

  • public für uneingeschränkten Zugriff (von jeder Klasse aus)
  • protected für Zugriff von Klassen, die im gleichen Packet liegen oder die von der Klasse erben
  • (nichts) für Zugriff aus Klassen die im gleichen Packet liegen
  • private für Zugriff nur von der gleichen Klassen (oder Klassen, die innerhalb dieser Klasse sind)

Vererbung

this

instanceof

https://en.wikipedia.org/wiki/Java_syntax#Overriding_methods [Methoden überschreiben]

Packages

import

import static

Klasse

Innere Klassen

Abstrakte Klassen

Interface

Enum

@Annotationen

Generics

Einkapsulung von Daten (Getter, Setter)

Up/Down-Casting von Objekten

Standardbibliothek

String

StringBuilder

Escape-Sequences

Datum + Zeit

Wrapper-Klassen für Zahlen

Die Math-Klasse

Das Collection Framework

Reguläre Expressionen

Das File-Objekt

Dateien beschreiben

Dateien lesen

Reflections

Multithreading

Networking

Applets

Spiele-Programmierung

Java als Web-Backend?

Unterschied zu anderen Sprachen

Weiteres

Java Style Guide

Java Code-Beispiel

Entwicklungsumgebungen

Benutzer steuern

Keylogger

E-Mails senden

Excel-Datei lesen und erstellen mit