|  Home  |  IT-KnowHow  |  Sonstiges KnowHow  |  Bookmarks  |  Über mich  |  Impressum  |  1und1 Shop  | 
Google


Referenzierte Quellen 
Aufbau meiner Werkbank 
Synergy 
Unix/Linux 
Debian 
Linux-Distributionen 
Paketverwaltung 
Fileserver 
Samba 
Grafische Oberfläche 
Festplatte - Boot, Partition, Raid, LVM 
SSH Tunnel, VPN 
FAQ 
Tips und Tricks 
Shellprogrammierung 
Windows 
cygwin 
ssh 
andLinux 
UltraISO 
Windows 7 
Windows CE 
Virtualisierung 
VMWare 
VirtualBox 
Cloud Computing 
Google App Engine GAE 
Storage 
AmazonWebServices 
Cloudfoundry 
RightScale 
Office-Pakete 
Serienbriefe mit OpenOffice 
OpenOffice Calc 
Sicherheit 
Abwehrmechanismen 
Zertifikate + SSL 
Zertifikate + eMail-Kommunikation 
Backup 
Tools 
Bacula 
Installation 
Tools 
Virenscanner 
Online-Banking 
Rechnerbetreuung 
Muli 
Schnecke 64 
Schnecke 256 
Opaks 
cooltek 
Laptop-Pflege 
Computer im Remote-Einsatz 
Software-Entwicklung 
Erfolgreiches Vorgehen 
Agile Softwareentwicklung 
Test-Driven-Development 
Software-Factory 
Domain-Driven-Design 
Model-Driven-Architecture 
CMMI 
Google AdWords 
Organisation 
Team 
Rollen im Projekt 
Anforderungen 
Anforderungs-Entwicklung 
Anforderungs-Management 
Google Analytics 
Architektur 
Best Concepts and Patterns 
Best Practices 
Camel 
IPF 
Design 
Best Practices 
Schnittstellen 
Persistenz 
ExceptionHandling 
Deklarative Entwicklung 
SOA 
UML Tools 
Programmierung 
Best Practices 
Java Core 
Classloader 
Generics 
JMX 
Anotationen 
Networking 
Threads 
I/O, Dateien, Steams 
Reflection 
Java EE 
Groovy 
Google Web Toolkit - GWT 
GUI Entwicklung 
HTTP 
Zeichencodierung 
XML 
XPath 
Json 
JSF 
CSS 
Firefox 
Eclipse Platform 
Eclipse als Java-IDE 
Derivate 
Organisation 
Debugging 
J2EE 
CMR mit WSAD 5.1 
PlugIns 
Subversion 
Git Plugin 
Sun Application Server 
JBoss 
Jetty 
Tomcat 
WebServices 
WSDL 
RESTful Webservices 
SOAP Webservices 
JMS - ActiveMQ 
Persistenz 
JDBC 
JPA / Hibernate 
JPA in Eclipse 
Oracle 
Performance / Tuning 
DB2 
MySQL 
MySql Administrator 
HSQLDB 
Microsoft SQL Server 
DBVisualizer 
Toad 
TOra 
Spring 
AOP 
Refactoring 
Logging 
Regex 
Lucene 
Mail 
Mailserver 
Mailclient 
JavaMail 
DNS 
LDAP 
Mobile Computing 
Platformen 
Android 
Handyauswahl 
Tablet a la iPad  
... aus Sicht des Handynutzers 
App Markets 
... aus Sicht des Entwicklers 
Plattform 
Programmierung 
... aus Sicht des Hackers 
Apps im Test 
Buildprozess 
ant 
maven 
Gradle 
Versionsverwaltung 
Subversion 
Git 
Zertifikate + SSL 
Dokumentation 
Kosten/Nutzen 
Index-Server 
Testen 
FIT 
FitNesse 
Selenium 
Performance 
JMeter 
soapUI 
EasyMock 
Code Analyse 
Lizenzmodelle 
HL7, CDA, CCD 
IHE Profile + Transaktionen 
ITIL 
IRC 
Netzwerke 
Provider 
Telefon 
Breitband-Internet 
Webhosting 
Webhoster 
Trafficanalyse 
Suchmaschinen 
Spam 
Internet-Werbung / AdSense 
1und1 - SmartDrive 
CMS 
Exponent 
Joomla 
Foto-Galerie 
Verschiedene 
(W)LAN 
Multimedia 
Gimp 
XnView 
ImageMagick 
Fotografie 
Digitale Fotografie 
Audio 
Voice-over-IP 
Video 
Video-over-IP 
DLNA, UPnP 
Videorekorder 
Hardware 
MP3 Player 
(Multi-Funktions-) Drucker 
Fritzbox 7390 
FritzBox 7270 
Netgear FM114P 
Mini-Fernseher 
Fernseher 2008 
Panasonic TX-P42GW20 
Fernseher 2012 
Netzwerkplayer 
Fernbedienung 
Satellitenanlage 
dbox 
Thomson IP1101 
Codemeter 
Navigationsgerät 
Pari Boy 
Kabelsalat 
Fuji Finepix S602 
Canon Ixus 40 
Canon Ixus 100 IS 
Spiegelreflex-Kamera 
Nikon D80 
Video-Kamera 
Ultra Mini PC - Asus eee PC 
Notebook 2012 
GPSMAP 76CSx 
Canon Lide 20 Scanner 
Lenovo UltraNav Tastatur 
Computer-Monitor - LG 2442 BF 
Kyocera FS-920 
Wäschetrockner 
Bluetooth Tastatur 
Aktiv-Lautsprecher 
Tablet Archos 101 G9 
Tablet zur Selbstorganisation 



IT-KnowHow  > Software-Entwicklung  > Programmierung  > Java Core 


Aufbau

Java besteht aus einem
  • Software Development Kit (SDK): wird zum Compilieren (javac.exe) von Source-Code in Bytecode benötigt. Bringt ausserdem noch einige Tools mit, so dass man keine der modernen Entwicklungsumgebungen braucht:
    • Debugger (jdb)
    • Disassembler (javap)
    • JavaDoc-Generator (javadoc)
    • jar-Archivierungstool (jar)
    • Bibliotheken (z. B. tools.jar zum Compilieren von Klasen mittels javac)
  • Java Runtime Environment (JRE): wird zum Starten von Java-Applicationen benötigt (java.exe)
    • bringt Zertifikate anerkannter CAs mit
    • bringt weitere libs mit, die man nicht extern zur Verfügung stellen muss
Auf dem Java-Kern setzen weitere Komponenten (z. B. Java 2 Enterprise Edition auf).

Sun ist bei weitem nicht der einzige Anbieter eines SDK, ein weiterer bekannter Vertreter ist IBM. Leider unterscheiden sich die Namen der Bibliotheken zwischen den verschiedenen Herstellern. Beispielsweise befindet sich die Klasse "java.lang.String" (also eine Basis-Klasse) in der Bibliothek "rt.jar", bei IBM befindet sie sich in der Bibliothek "core.jar".

Compilierung ohne javac:

Die Compiler-Bibliotheken befinden sich in der Bibliothek tools.jar. Will man beispielsweise aus eine Java-Applikation den Compiler nutzen, so benötigt man die tools.jar auf jeden Fall im Classpath. Dort ist der Java-Compiler (com.sun.tools.javac.Main) zuhause. Beispiel:

Ein ant-Skript wird nicht über eine ant.exe angestossen, sondern über:

java -jar c:\Programme\ant.jar -buildfile mybuild.xml

so wird dies zu folgender Fehlermeldung führen

unable to find a javac compiler

, sofern die tools.jar nicht im Bootclasspath ist. Mit folgenden Befehl klappts:

java -cp c:\Programme\j2sdk1.4.2\lib\tools.jar -jar c:\Programme\ant.jar -buildfile mybuild.xml

Ant macht hier nämlich nichts anderes als den Compiler programmatisch aufzurufen und braucht dazu com.sun.tools.javac.Main.

Ein anderer Anwendungsfall für das Compilieren von Java-Klassen aus einer Java-Applikation heraus ist ein Servlet-Container, der Java-Source-Code für geänderte JSPs neu erzeugt und compiliert (hier findet man weitere Informationen und auch ein Beispiel: http://java.sun.com/developer/JDCTechTips/2003/tt0722.html#2).

ACHTUNG: ab JDK 1.5 ist "com.sun.tools.javac.Main" deprecated und sollte durch "javax.tools.JavaCompilerTool" ersetzt werden. Erst ab JDK 1.6 wird der programmatische Compiler-Aufruf offiziell eingeführt.

Classpath


Sowohl beim Compilieren als auch beim Starten einer Java-Applikation ist der Classpath von entscheidender Bedeutung und hat schon so manchen Java-Novizen um den Verstand gebracht (und da schliesse ich mich ein ;-). Leider weiss man bei den modernen Entwicklungsumgebungen manchmal nicht, wie sich der Classpath zusammensetzt (es gibt hier viele Einstellungen wie hier und hier zu sehen ist). Vor diesem Hintergrund ist es manchmal sogar schneller, wenn man die gute alte Kommandozeile nutzt. Will man eine Anwendung ausserhalb der Entwicklungsumgebung nutzen, führt eh kein Weg an der Kommandozeile und ant vorbei.

Man unterscheidet den
  • BootClasspath: kann (nur in Ausnahmefällen erforderlich) über die Option "-Xbootclasspath" der Laufzeitumgebung mitgegeben werden. Dieser Classpath hat also höhere Priorität als der normale Classpath.
  • normalen User-Classpath: wird über die Option "-classpath" an den Compiler oder die Laufzeitumgebung mitgegeben
  • Extensions-Classpath
Hier findet man viele Informationen zum normalen Classpath (ich wills nicht wiederholen): http://mindprod.com/jgloss/classpath.html)

Will man wissen mit welchem Classpath eine Anwendung gestartet wurde, dann kann man programmatisch folgendes tun:

System.out.println(System.getProperty("java.class.path"));

Websphere macht das beim Starten von allein. Vielen Dank :-)

Weitere Libs

Neben den Libs, die das JDK bzw. die JRE mitbringt, kann man dem Java-Interpreter weitere Libs über die  Classpath-Variable oder mit dem Schalter "-cp" weitere Libs bereitstellen.

Für Third-Party-Libs ist das den meisten klar. Bei Java-Erweiterungen, die noch im JSR Zustand sind und noch nicht den Weg in die Auslieferungs-Bibliotheken (z. B. rt.jar) gefunden haben ist es manchmal komisch, daß man Klassen aus dem javax-Package explizit als Bibliothek zum Classpath hinzufügen muss. Ein Beispiel ist in Java 1.6 das Package
javax.annotation

Variableninitialisierung

Gute Artikel:

Möglichkeiten zur Initialisierung einer Klasse

Mit static markierte Variablen sind Klassenvariablen, d. h. alle Instanzen einer Klasse teilen sich die Variable und deshalb greift man auch eigentlich per "ClassName.property" darauf zu anstatt per "property".
  • Default Initialization - abhängig vom Datentyp erfolgt eine Initialisierung (bei int mit dem Wert 0) - in JEDEM Fall die erste Initialisierungsphase
    • private static int age;
  • by Instance Variable Initializer:
    • private static String name = "Pierre";
  • by Instance Initializers:

private static String name;

static {

name = "Pierre";

}

Man kann übrigens mehrere Instance Initializers haben - sie werden in der Reihenfolge ausgeführt wie sie im Code auftauchen.

Möglichkeiten zur Initialisierung einer Instanz

  • Default Initialization - abhängig vom Datentyp erfolgt eine Initialisierung (bei int mit dem Wert 0) - in JEDEM Fall die erste Initialisierungsphase
    • private int age;
  • by Instance Variable Initializer:
    • private String name = "Pierre";
    • die Initialisierung erfolgt in der Reihenfolge, in der der Code geschrieben wurde. Deshalb führt das auch zu einem Compilerfehler (Forward Reference):

private int x = 5 + y;

private int y = 3;

Der Compiler ist leider nicht beliebig schlau, weshalb dieser Code das gleiche Problem hat, aber nicht zu einem Compilerfehler führt:

private int x = initX();

private int y = 3;

initX() {

5 + y;

}

Allerdings führt das zu dem Ergebnis x=5 und y=3 obwohl die Initention evtl. x=8 und y=3 war. Hier gibt es also evtl. semantische Probleme.

  • by Instance Initializers: Der Code wird semantisch in jedem Konstruktor ausgeführt - auf diese Weise können sich Konstruktoren Initialisierungscode teilen (eine Alternative dazu ist der explizite Aufruf eines anderen Konstruktors)

private String name;

{

name = "Pierre";

}

Intern packt der Compiler diese Instance Initializers in alle Konstruktoren. Instance Initializer können auch Exceptions werfen - wenn es sich allerdings um CheckedExceptions handelt, dann muss JEDER Konstruktor diese Exception in der throws-Clause aufführen.

Diese Variante wird u. a. auch bei anonymen Klassen eingesetzt, die keinen Konstruktor haben (hier können übrigens auch CheckedExceptions geworfen werden!!!).
  • by Constructors
    • Konstruktoren werden vom Java-Compiler im Bytecode als Methoden mit dem Namen <init> - dies ist kein valider Methodenname im Sinne der Java-Language-Specification (JLS), d. h. man gerät nicht in die Gefahr, diese Methode überschreiben zu können!!! Manchmal sieht man diese Methoden in Stacktraces ... wie beispielsweise hier (in AnalysisServiceImpl.java:85 wird der DayReportBuilder per new DayReportBuilder() instanziiert:

java.lang.NullPointerException

at AbstractReportBuilder.<init>(AbstractReportBuilder.java:139)

at DayReportBuilder.<init>(DayReportBuilder.java:70)

at AnalysisServiceImpl.analyzeDay(AnalysisServiceImpl.java:85)     
    • Konstruktoren können einander mit this() aufrufen (Konstruktor-Methoden können nur innerhalb eines Konstruktors genutzt werden - nicht innerhalb anderer Methoden oder in Variable Initializers):

MyClass(int value) {

...

}

MyClass(int v1, String v2) {

this(v1);                              // <----- HERE

...

}

Wichtiges zu Konstruktoren

Konstruktoren werden sind keine Methoden (auch wenn der Java-Compiler zu jedem Konstruktoren intern eine passende Methode erzeugt - diese Methoden sind aber im Java-Code noch nicht existent) - sie werden nicht vererbt, aber sie sind innerhalb einers Konstruktors einer direkt (!!!) abgeleiteten Klasse zugreifbar, denn hierüber stösst die Subklasse die Initialisierung der Felder der Superklasse an.

Implementierung eines Konstruktors:

  • wenn ein Konstruktor keinen anderen Konstruktor dieser Klasse oder der Superklasse aufruft, dann erzeugt der Compiler Bytecode, in dem automatisch der leere Konstruktor der Superklasse als erste Anweisung des Konstruktors aufgerufen wird. Auf diese Weise wird implizit sichergestellt, daß die Base-First-Order-Initialisierung eingehalten wird
  • ein Konstruktor kann durch andere

Leerer Konstruktor

Hat eine Klasse keinen Konstruktor definiert, so erzeugt der Java-Compiler einen leeren Konstruktor (keine Parameter, kein Code - höchstens der super()-Aufruf) im Code. Dieser Konstruktor bekommt den die gleichen Zugriffsrechte wie die Klasse (z. B. public). Wurde der leere Konstruktor vom JavaCompiler erzeugt, so spricht man auch vom Default-Konstruktor.

Hat eine Klasse A keinen leeren Konstruktor (implizit oder explizit), dann muss sie einen anderen Konstruktor haben. In diesem Fall müssen auch die Subklassen (z. B. B) diesen Konstruktor explizit aufrufen!!! Der automatische Aufruf (durch den Java-Compiler im Bytecode erzeugte Aufruf) des leeren Konstruktors von A aus dem Konstruktor der Subklasse B scheitert, weil dieser leere Konstruktor ja nicht existiert (man bekommt einen Compilerfehler "Implicit super constructor MyClass() is undefined. Must explicitly invoke another constructor").

Leerer Konstruktor und Serializable:

???

Initialisierung bei Vererbung - Base-Class-First-Order

Wird die Instanz einer Klasse erzeugt, so sind nicht nur die Felder dieser einen Klasse zu initialisieren, sondern auch die aller Superklassen.

Die Initialisierung erfolgt in Base-Class-First-Order also von oben nach unten. Somit kann eine Subsklasse immer davon ausgehen, daß die Felder aller Superklassen bereits initialisiert sind. Aber nicht andersrum!!!
Diese Order wird durch impliziten oder expliziten Aufruf eines Konstruktors der Superklasse erreicht. Führt man nicht explizit einen Aufruf eines anderen Konstruktors (per this(...)) oder eines Super-Konstruktors (per super(...)) erzeugt der Compiler einen Aufruf des leeren Super-Konstruktors im Bytecode ein (super()). Sollte die Superklasse keinen leeren Konstruktor haben, daß bekommt man einen Compilerfehler ("Implicit super constructor MyClass() is undefined. Must explicitly invoke another constructor").

Verwendung von Einschubmethoden in Konstruktoren

Ich meine sowas gemäß Template Method Design Pattern:
public abstract class AbstractClass {
public AbstractClass() {
...
einschubmethode();
}

protected abstract void einschubmethode();      // implemented by subsclass!!!
}
In diesem Fall wird während der Initialisierung der AbstractClass Felder eine Methode der Subklasse aufgerufen, deren Felder wegen des Base-Class-First-Order-Prinzips noch nicht initialisiert sind. Wenn in der Implementierung dieser Methode auf Felder der Klasse/Instanz zugegriffen wird könnte das zu unerwarteten Ergebnissen führen.
Eine Lösung könnte darin bestehen, die Einschubmethode in den Konstruktoren der Subklassen aufzurufen - man verliert dann allerdings die Bauanleitung, die man durch Einschubmethoden ja eigentlich vorgeben will.

Warum ist die Variableninitialisierung so wichtig?

Der Zustand, den das Objekt nach der Erzeugung hat ist der Ausgangszustand für die Nutzung durch einen Client. Ein schlecht initialisiertes Objekt wird wenig robust sein und die Methoden werden evtl. Probleme haben korrekt zu funktionieren (auch wenn der Code eigentlich keine Probleme aufweist). Ein Haus, das auf einem schlechten Fundament steht, wird es schwer haben ...

Wenn eine Methode offensichtlich Probleme hat ordentlich zu funktionieren und das sofort zutage tritt ist das ein Problem. Ein noch größeres Problem tritt auf, wenn der Zustand schleichend schlechter wird, es aber zu keinen RuntimeExceptions kommt. Der Aufrufer einer solchen Instanz geht über lange Zeit davon aus (und in der Zwischenzeit sind vielleicht auch schon ein paar Transaktionen erfolgreich auf der Datenbank abgeschlossen worden), daß alles in Ordnung ist. Und irgendwann kommt der BigBang und der korrupte Zustand fürht tatächlich zu einer RuntimeException. Aber eigentlich ist das noch die beste Lösung ... somit wird zumindest mal ein Problem bemerkt - schlimmer ist, daß in der Zwischenzeit vermutlich schon einiges schiefgelaufen ist, es aber keiner gemerkt hat.

Deshalb sollten Methoden eine Exception werfen sobald sie merken, daß sich ein ungültiger Zustand eingeschlichen hat (am besten mit Postconditions prüfen). Und natürlich sollten Methoden die Eingangsparameter auf ihre Validität prüfen!!! FAIL-FAST!!!

Warum Variableninitialisierung im Konstruktor?

Nach dem Aufruf des Konstruktors (evtl. auch des leeren Konstruktors) sollten Objekte einen gültigen Zustand haben, so daß sich ALLE Methoden sinnvoll verhalten. Ist das nicht der Fall, so zwingt man den Nutzer einer solchen Klasse, weitere Attribute nach der Erzeugung der Instanz zu setzen. Solche Objekte sind für den Benutzer schwierig zu benutzen, denn das Information-Hiding-Prinzip ist verletzt. Der Nutzer muss ausserdem wissen, daß er nach der Erzeugung (z. B. per MyClass myclass = new MyClass()) noch 3 Setter aufrufen muss. Diese Information muss kommuniziert und auch immer eingehalten werden. Aber selbst wenn das der Fall ist, können Refactorings (es muss vielleicht neuerdings noch einer vierte Setter-Methode aufgerufen werden) problematisch werden, weil sie nicht zur Compile-Zeit, sondern erst zur Laufzeit zu Problemen führen - na hoffentlich hat man dann genügend automatiserte Tests ...

Ziel:

Im besten Fall schafft man es, daß ein Objekt ab seiner Erzeugung bis zum Ende seines Lebens einen gültigen Zustand haben. Leider verlangen einige Frameworks leere Konstruktoren - in diesem Fall sollte man in den public Methoden selbst prüfen, ob das Objekt bereits komplett gültig initialisiert ist (ansonsten eine Exception werfen) ... das ist natürlich nicht schön, aber eine Alternative gibt es dazu nicht.

FAQ - Tips'n Tricks

Frage: Wie mache ich in JDK 1.3.x aus einem Array ([]) eine ArrayList?
Antwort: new ArrayList(java.util.Arrays.asList(myEckigeKlammernArray))

Frage: Wie instanziiere ich ein anonymes Array?
Antwort: new Class[] { String.class, Integer.class }; new int[] {1, 2, 3}

Frage: Wie implementiere ich eine anonyme Klasse (also eine anonyme Implementierung des Interfaces)?
Antwort: Beispiel:

public interface Closure {
public void on(Object object);
}

public class AntragsPartner {
public void apply(Closure closure) {
...
}
}

public class SomeClass {
AntragsPartner antragsPartner = ...;
antragsPartner.apply(
new Closure() {
public void on(Object object) {
...
}
}
);
}

Frage: Was ist ein Dynamic Proxy, welche Vorteile haben sie und wie setze ich sie ein?
Antwort: Eine gute Darstellung dieses seit Java 1.3 vorhandenen Konzepts findet man hier und hier. Ein Dynamic Proxy implementiert zur Laufzeit ein oder mehrere beliebige Interfaces - der Dynamic Proxy wird folgendermassen instanziiert:

java.lang.reflect.Proxy.newProxyInstance(classLoader, new Class[] { MyInterface.class}, myInvocationHandler)

Der übergebene "myInvocationHandler" wird über Aufrufe des Proxy per invoke-Methode des InvocationHandler-Interface) informiert:

public Object invoke(Object proxy, Method method, Object[] args)

Dabei ist "proxy" die Instanz des DynamicProxy (von dem der invoke-Aufruf kommt) und "method"/"args" ist die aufgerufene Methode des Interfaces (im Beispiel: MyInterface). Die eigentliche Durchführung des Requests wird i. a. vom InvocationHandler an ein Target-Objekt delegiert. Dieses Target-Objekt sollte man sich im InvocationHandler merken. Den InvocationHandler kann man nutzen, um beispielsweise Zugriffrechte zu prüfen, Logging durchzuführen und weitere CrossCutting-Concerns abzubilden - man ist hier übrigens recht nah an Aspektorientierter Programmierung, die teilweise auch über Dynamic Proxies abgebildet ist. Ein komplettes Beispiel findet man hier.

Frage: Beim Ausführen einer Java-Applikation mit dem Interpreter "java" bekomme ich die Fehlermeldung "unsupported major.minor.version 48.0". Was mache ich falsch?
Antwort: Das liegt höchstwahrscheinlich an einer Inkompatibilität im Bytecode. Die Klassen wurden vielleicht mit einer jüngeren Java-Version compiliert als sie jetzt interpretiert werden sollen (compiliert mit java 11.4, ausgeführt mit java 1.3).

Frage: Kann ich eine Inner-Class auch von aussen nutzen?
Antwort: Das kommt auf die Berechtigung an und aus welcher Klasse man es nutzen will. Wenn die Klasse beispielsweise Package-protected deklariert ist wie die Klasse DateConverter im folgenden Beispiel

public class AntragFromXmlCreator {
...
static class DateConverter {
...
}
}

dann kann ich diese Klasse aus einer Klasse im gleichen Package folgendermassen instanziieren (sinnvoll beispielsweise für JUnit-Tests, die i. a. im gleichen Package liegen - wenn auch in einem anderen Projekt):

new AntragFromXmlCreator.DateConverter()

Frage: Wie gebe ich den StackTrace einer Exception in einen String aus?
Antwort:
StringWriter stringWriter = new StringWriter();
exception.printStackTrace(new PrintWriter(stringWriter));
String exceptionString = stringWriter.toString();