Archive-name: de/comp-lang-java/faq
Posting-Frequency: weekly (Friday) Last-modified: 2004-10-02 Version: 1.44 URL: ftp://rtfm.mit.edu/pub/usenet-by-group/de.answers/de/comp-lang-java/faq, http://www.faqs.org/faqs/de/comp-lang-java/faq Expires: 21 Oct 2005 00:00:00 CEST Tab-Width: 4 Page-Width: 74 See reader questions & answers on this topic! - Help others by sharing your knowledge FAQ (Frequently Asked Questions) der Newsgroup de.comp.lang.java v1.44 ========================================================================= Inhalt: ------- 1. Allgemeines 1.1. Allgemeine Hinweise zum Posten 1.2. Worum geht es in dieser Newsgroup? 1.3. Wie kommen häufig gestellte Fragen in diese FAQ? 1.4. Wie kommen die Bugs aus der FAQ? 1.5. TAGS-Konventionen zu einem einheitlichen Aussehen 1.6. Ich bekam als Antwort auf meine Frage eine seltsame Buchstaben- kombination zugeschickt. Was ist das und wie kann ich es lesen? 2. Was man über Java wissen sollte 2.1. Was ist Java? 2.2. Verwandtschaft von Java mit anderen Sprachen? 2.3. WebBrowser und Java 2.4. Erste Schritte in Java 2.5. Ich habe das HelloWorld-Programm aus meinem Java-Buch abgeschrieben, aber es funktioniert nicht. :-( 3. Häufig gepostete Fragen 3.1. [LANG] - Die Sprache Java. 3.1.1. Gibt es in Java keine Zeiger wie in C++? 3.1.2. Warum ist Referenz nicht gleich Referenz? 3.1.3. Wie werden in Java Funktionsparamter übergeben, by value oder by reference? 3.1.4. Warum gibt es in Java keine Destruktoren wie in C++? 3.1.5. Warum funktioniert die equals Methode nicht? 3.1.6. Wenn ich eigene Objekte mit einer Hashtable/HashMap verwalte, kommt es zu sonderbaren Effekten. Wieso? 3.1.7. Was bedeutet das Schlüsselwort final? 3.1.8. Warum wird der dynamische Parametertyp bei überladenen Funktionen nicht beachtet? 3.1.9. Was bedeutet super()? 3.1.10. Was sind anonyme Arrays? 3.1.11. Gibt es in Java einen Prä-Prozessor wie in C++? 3.1.12. Existiert der const Modifizierer von C++ auch in Java? 3.1.13. Wie kann man Referenzen von Übergabeparametern ändern? 3.1.14. Wie erzeuge ich eine tiefe Kopie eines Objektes mit möglichst wenig Aufwand? 3.1.15. Wie kann ich in Java eine dem Programmierer unbekannte Anzahl gleichartiger Objekte erzeugen und ihnen passende Namen zuweisen, also label1, label2 usw.? 3.1.16. Wie kann ich den Typ enum aus C++ in Java umsetzen? 3.1.17. Kann man Mehrfachvererbung mit Java simulieren? 3.1.18. Wie realisiere ich eine variable Parameterliste? 3.1.19. Wie realisiere ich eine Methodenauswahl nach den dynamischen Parametertypen? 3.1.20. Sind Methoden in Java immer virtuell? 3.1.21. Ich stosse ab und zu auf den Begriff "Wrapper-Klassen". Könnte mir jemand erklären was das ist? 3.1.22. Warum ist private nicht privat? 3.2. [STRING] - Strings. 3.2.1. Wie vergleiche ich zwei Strings in Java? 3.2.2. Wie wandle ich einen String in einen Integer? 3.2.3. Wie wandle ich einen Integer in einen String um? 3.2.4. Wie wandle ich einen Integer in einen HexString um? 3.2.5. Wie kann ich eine Zahl formatieren und wie lege ich die Anzahl der Nachkommastellen fest? 3.2.6. Wie kann ich ein Datum formatieren? 3.2.7. Wie kann ich in einem String oder StringBuffer mehrere Zeichen suchen und ersetzen? 3.2.8. Gibt es reguläre Ausdrücke in Java (regular expressions)? 3.3. [IO] - Eingabe/Ausgabe, Streams, etc. 3.3.1. Verlangsamt Serialisierung mein Programm? 3.3.2. Wie kann ich rekursiv einen Verzeichnisbaum abarbeiten? 3.3.3. Wie kann ich aus Dateien zeilenweise lesen? 3.3.4. Wie kann ich Exponentialzahlen (z.B. 1.09E+008) aus einer Datei lesen? 3.3.5. Wie kann ich mit Java Dateien kopieren? 3.3.6. Warum kann ich meine Bilder nicht mehr laden, nachdem ich sie in ein Jar gesteckt habe? 3.4. [NET] - Netzwerk. 3.4.1. Wie kann ich einen Ping in Java realisieren? 3.5. [AWT] - Abstract Window Toolkit. 3.5.1. Wenn ich einen Listener bei mehreren Buttons anmelde, wie kann ich dann unterscheiden, welcher gedrückt wurde? 3.5.2. Kann ich ein Fenster (Frame/JFrame) maximieren? 3.5.3. Wie tausche ich die Kaffeetasse im blauen Balken aus? 3.6. [SWING] - Swing, das bevorzugte GUI. 3.6.1. Wie mache ich globale Font-Änderung für meine Komponenten? 3.6.2. Wie kann ich bei der Eingabe in ein JTextField die Anzahl der eingebbaren Zeichen beschränken? 3.6.3. Wie setze ich den Cursor an den Anfang der JTextArea? 3.6.4. Wie scrolle ich an das Ende der JTextArea? 3.6.5. Wie bekomme ich es hin, das der Benutzer in meiner JTable keine Eingaben tätigen kann? 3.6.6. Wie bekomme ich eine horizontale ScrollBar bei JTable? 3.6.7. Wie scrolle ich ans Ende von JTable? 3.6.8. Wie verhindere ich ein reordering der Spalten bei JTable? 3.6.9. Wie verhindere ich ein Resizen der Spalten bei JTable? 3.6.10. Wie ändere ich die Hintergrundfarbe von JScrollPane? 3.6.11. Wie kann ich ein JLabel dazu bringen, seinen Hintergrund zu füllen? 3.6.12. Warum reagiert meine GUI nicht, während eine längere Berechnung ausgeführt wird? 3.7. [APPLET] - Java-Applets und ihre Zusammenarbeit mit Browsern. 3.7.1. Welche JDK-Version sollte ich für Applets verwenden, die möglichst allgemein lauffähig sein sollen? 3.7.2. Wie bekomme ich den Internet Explorer dazu, das Plugin anstelle der integrierten JVM zu benutzen. 3.7.3. Was dürfen unsignierte Applets nicht aus Sicherheitsgründen? 3.8. [SERVER] - Servlets und andere Server-Implementierungen in Java. 3.9. [NONCORE] - Klassen/Packages, die über den Kern der Sprache hinausgehen, also Java3D etc. 3.10. [OOP] - OOP-Konzepte und Patterns in Java. 3.10.1. Was bedeutet Vererbung im OO-Kontext? 3.10.2. Was bedeutet Aggregation im OO-Kontext? 3.10.3. Was bedeutet Assoziation ist OO-Kontext? 3.10.4. Was bedeutet Benutzung im OO-Kontext? 3.10.5. Worin liegen die Unterschiede zwischen abstrakten Klassen und Interfaces? 3.10.6. Was ist eine anonyme innere Klasse? 3.10.7. Was ist ein immutable Objekt? 3.11. [JDK] - Virtuelle Maschinen, alles über JDKs, deren Installation und Verwendung. 3.11.1. Was ist ein Java Development Kit (JDK) 3.11.2. Was ist ein Java Runtime Environment (JRE) 3.11.3. Was ist eine Java Virtual Machine (JVM) 3.11.4. Wie konfiguriere ich JDK1.3/1.4 unter Linux oder Unix? 3.11.5. Wie installiere und konfiguriere ich das jdk unter Windows 9x/Me/NT/2000 richtig? 3.12. [TOOLS] - Java-Zusatz-Tools, zum Beispiel IDEs, Build-Tools, Profiler, etc. 3.12.1. Welche IDE muss ich verwenden? 3.12.2. Wie kann man eine Java-Anwendung in eine EXE-Datei umwandeln? 3.13. [MATH] - Mathematik, Arithmetik, Gleitpunktzahlen, Funktionen. 3.13.1. Warum rechnet Java falsch? 3.13.2. Wie runde ich eine Gleitkommazahl? Wie formatiere ich eine Gleitkommazahl? 3.13.3. Wie kann ich in Java Zufallszahlen im Bereich 0..n erzeugen? 3.14. [MISC] - Alles, was nicht in eine der anderen Rubriken paßt. 3.14.1. Ich komme mit dem import-Statement nicht klar, was mache ich falsch? 3.14.2. Warum gibt es Probleme bei final Werten in Verbindung mit elementaren Typen? 3.14.3. Was bedeuten "$" im Namen von Class-Files? 3.14.4. Wie lassen sich Bilder im Dateiformat XYZ laden oder speichern? 3.14.5. Was geht nicht mit Java? 3.14.6. Wie kann ich in meinem Java-Programm ein HTML-Dokument anzeigen lassen? 3.14.7. Unter Windows werden in der Konsole (DOS-Eingabe- aufforderung) die Umlaute falsch ausgegeben. Wie kann ich das korrigieren? 3.15. [ERROR] - Fehlermeldungen. 3.15.1. Warum findet Java den Konstruktor nicht? 3.15.2. Warum bekomme ich eine "NoClassDefFoundError" Fehlermeldung beim Starten von java? 3.15.3. Warum bekomme ich eine "Couldn't read <Name>" Fehlermeldung beim Kompilieren mit javac? 3.15.4. Warum bekomme ich eine "class <Name> must be defined in a file called <Name>" Fehlermeldung beim Kompilieren von javac? 3.15.5. Warum wird beim Zugriff auf ein korrekt initialisiertes Objekt-Array eine NullPointerException geworfen? 3.15.6. Warum bekomme ich eine NullPointerException, wenn ich versuche, auf Methoden oder Attribute von in einem Array gespeicherten Objekten zuzugreifen? 3.15.7. Warum meckert der Compiler bei nicht initialisierten final Variablen? 3.15.8. Was hat die Compilerfehlermeldung "... is deprecated" zu bedeuten? 3.16. [ClassLoader] - Alles über Classloader 3.16.1 Wie funktionieren Classloader? 3.16.2 Warum macht der Classloader im Servlet-Container Probleme? Warum funktioniert das Einlesen von Ressourcen ueber den Classloader bei mir nicht? 3.16.3 Wie lade ich eine Klasse neu? 3.16.4 Wie baue ich einen Plugin-Mechanismus? 3.16.5 Gibt's dazu auch Beispielcode? 3.16.6 ClassLoader Ressourcen zu 3.16.1 bis 3.16.5 4. Bücher zum Thema Java 4.1. Kann mir jemand gute Literatur zum Thema Java empfehlen? 5. Themenverwandte Internet Ressourcen 5.1 WWW-Sites 5.2 Newsgroups 5.3 Mailinglisten 6. JavaScript Internet Ressourcen. 6.1 WWW-Sites 6.2 Newsgroups 7. Credits _____________________________________________________________________ 1. Allgemeines ============== 1.1. Allgemeine Hinweise zum Posten ----------------------------------- Wenn du neu im Usenet bist, solltest du auf jeden Fall die Texte in der Newsgroup <news:de.newusers.infos> lesen. Du findest sie auch im WWW auf <URL:http://www.kirchwitz.de/~amk/dni/>. In diesen Texten erhält man einen Überblick über die im Usenet üblichen Regeln ("Netiquette"). Auf diese Weise lassen sich die meisten Anfänger- fehler verhindern und man vermeidet, gleich für sein erstes Posting wegen formaler Fehler angeschnauzt zu werden. Falls du Fragen zu den Regeln im Usenet hast, stelle sie bitte in der Newsgroup <news:de.newusers.questions>. Weitere Links zum Thema: <URL:ftp://rtfm.mit.edu/pub/usenet/de.answers/de-newusers/> <URL:http://learn.to/quote/> 1.2. Worum geht es in dieser Newsgroup? Autor: Markus Reitz --------------------------------------- In der Newsgroup <news:de.comp.lang.java> sollen Probleme und Lösungen, die sich im Zusammenhang mit der Programmiersprache Java ergeben, diskutiert werden. Die Newsgroup beschäftigt sich nur mit der Sprache Java! JavaScript oder herstellerspezifische Implementierungen (z.B. Microsoft J++) besitzen eigene Newsgroups, in denen diese spezifischen Probleme und Lösungen diskutiert werden. Neben der Newsgroup <news:de.comp.lang.java> gibt es noch weitere (vor allem englischsprachige) Newsgroups, die sich mit der Programmiersprache Java beschäftigen. Siehe auch den Abschnitt "Themenverwandte Newsgroups". 1.3. Wie kommen häufig gestellte Fragen in diese FAQ? Autor: Uwe Günther ----------------------------------------------------- Nachdem man festgestellt hat, dass man in einem bestimmten Zyklus die gleiche Frage immer wieder mit der gleichen Antwort beantworten muss - nur weil die Unis ihren Lehrplan mal wieder geändert haben, in der c't mal wieder ein "Wie programmiere ich ein Applet"-Kurs unter die Leute gebracht wurde oder aus welchen Gründen auch immer - sollte man einen neuen Thread mit dem Subject: [FAQ] Neue Frage: <Frage> beginnen. Der Body dieser Nachricht sollte folgendes Format besitzen: --Schnipp-- Frage: <Frage> Antwort: <Antwort> Beispiel: <Code> --Schnapp-- Wobei der Text in spitzen Klammern, dein Part ist. Die Zeilenlänge darf 74 Zeichen nicht überschreiten. Der Code sollte die Code Conventions von Sun einhalten - wenn du nicht weisst was das ist, solltest du besser keine FAQ schreiben. Der gesamte Text darf keine Tabs enthalten und bei Bedarf gegen 4 Leerzeichen ersetzt werden. Es ist von grossem Vorteil wenn der Beispiel-Code kompilierbar ist. Das steigert die Qualität der FAQ ungemein. Nachdem der Thread dann von den Regulars und den anderen Lesern von de.comp.lang.java gebührend behandelt wurde und etwaige Probleme ausgeräumt wurden, muss die Endfassung des Textes an die im Header dieser FAQ angegebene e-mail Adresse des FAQ-Maintainers von de.comp.lang.java gesendet werden. Dabei ist es erwünscht die Rubrik, oder gleich die zukünftige Fragennummerierung mit anzugeben. Weiterhin muss die Message-ID mit angegeben werden, damit der FAQ-Maintainer überprüfen kann ob er nicht von irgendjemanden vereimert wird. ;-) 1.4. Wie kommen die Bugs aus der FAQ? Autor: Uwe Günther ------------------------------------- Nachdem man festgestellt hat, dass die FAQ einen Fehler hat, kann man diesen dem FAQ-Maintainer unter Angabe eines Workarounds direkt mitteilen. 1.5. TAGS-Konventionen zu einem einheitlichen Aussehen Autor: Markus Reitz -------------------------------------------------------- Neben den schon weiter oben genannten Ratschlägen, die allgemeine Anmerkungen zum Posten in Newsgroups darstellen, folgen nun einige spezifische Anmerkungen zur Schreibweise des Subjects in der Newsgroup <news:de.comp.lang.java>. Vor einiger Zeit wurde die Einführung sogenannter Tags vorgeschlagen, die die Übersichtlichkeit steigern sollen. Hintergrund ist die einfache Idee, den Inhalt des Postings durch ein eindeutiges Schlüsselwort, das Tag, zu charakterisieren. Mit einem entsprechend konfigurierten Newsreader ist es dann möglich, sich solche Nachrichten hervorheben zu lassen, wodurch man direkt auf einen Blick Postings beispielsweise zum Thema Swing überschauen kann. Tags werden an den Anfang des Subjects, in eckigen Klammern eingeschlossen, geschrieben. Damit Einheitlichkeit gewahrt wird, sollten die folgenden Tags verwendet werden. Wenn jeder seine eigenen Tags spezifizieren würde, wäre die Einheitlichkeit verloren und es würde genau-soviel Chaos wie vorher herrschen. Deshalb an dieser Stelle die Bitte, sich an die Vorgaben zu halten. Bei Änderungsvorschlägen bitte in die Newsgroup zur Diskussion posten und bei Akzeptanz in der Newsgroup werden die Vorschläge an dieser Stelle in die FAQ aufgenommen. Liste der Tags in <news:de.comp.lang.java>: Tags für Fragen: [LANG] - Frage bezüglich der Sprache Java. [STRING] - Fragen die unmittelbar mit Strings zu tun haben. [IO] - Frage bezüglich Eingabe/Ausgabe, Streams, etc. in Java. [NET] - Frage bezüglich Netzwerk. [AWT] - Frage bezüglich AWT. [SWING] - Frage bezüglich Swing. [APPLET] - Frage zu Java-Applets und ihre Zusammenarbeit mit Browsern. [SERVER] - Frage zu Servlets und anderen Server-Implementierungen in Java. [NONCORE] - Fragen zu Klassen/Packages, die über den Kern der Sprache hinausgehen, also Java3D etc. [OOP] - Frage bezüglich OOP-Konzepten und Patterns in Java. [JDK] - Frage zu virtuelle Maschinen, alles über JDKs und deren Installation und Verwendung. [TOOLS] - Frage zu einem Java-Zusatz-Tool, zum Beispiel IDEs, Build-Tools, Profiler, etc. [MATH] - Mathematik, Arithmetik, Gleitpunktzahlen, Funktionen. [MISC] - Alles, was nicht in eine der anderen Rubriken paßt. [ERROR] - Fragen zu Fehlermeldungen. [OT] - Alles was hochgrading off-topic ist und nicht direct mit Java zu tun hat. ;-) Tags "nicht"-Fragen: [INFO] - Allgemeine Informationen, z.B. Links auf Webseiten [DISCUSSION] - Diskussion zu einem Java-spezifischen Thema [ANNOUNCE] - Vorstellung neuer Software [PROST] - Darf nur von Regulars verwendet werden, die wissen was sie tun. ;-) 1.6. Ich bekam als Antwort auf meine Frage eine seltsame Buchstaben- kombination zugeschickt. Was ist das und wie kann ich es lesen? Autor: Stephan Menzel -------------------------------------------------------------------- Diese Kombination ist eine sogenannte Message-ID und bezeichnet einen Usenetartikel eindeutig. Vorgehensweise bei Message-ID-Angaben: (a) man hat einen Newsreader, der damit umgehen kann: - einfach draufklicken. (b) Wenn man Opera ab Version 6 verwendet, kann man in der Adressleiste einfach "r msgid:a2kpac.14g.1@aljoscha-rittner.de" eingeben und gelangt zur entsprechenden Seite von Google Groups. (c) ansonsten: http://groups.google.com, dann msgid:a2kpac.14g.1@aljoscha-rittner.de eingeben. (d) Wenn man gerne URLs bastelt: http://groups.google.com/groups?q=msgid:a2kpac.14g.1@aljoscha-rittner.de (e) oder http://groups.google.com/advanced_group_search dann Msg-ID bei "Beitrags-ID" eingeben. 2. Was man über Java wissen sollte ================================== 2.1. Was ist Java Autor: Markus Reitz ------------------------ Zuersteinmal: Java ist nicht JavaScript. JavaScript ist eine Sprache, die federführend von der Firma Netscape entwickelt wurde, um die Inhalte von Webseiten dynamischer und interaktiver zu gestalten. JavaScript-Programme werden in den HTML-Quelltext der Seite eingebettet und vom Browser interpretiert und ausgeführt. Zwar macht JavaScript viele Anleihen bei Java, ist aber bei weitem nicht so flexibel. Die deutsche Newsgroup zu JavaScript findet man unter: <news:de.comp.lang.javascript> Die Website von de.comp.lang.javascript ist unter folgendem Link zu finden: <URL:http://www.dcljs.de/> Java wurde von der Firma SUN Microsystems mit dem Ziel entwickelt, eine moderne, objektorientierte Sprache zu schaffen. Durch das Ziel der Plattformunabhängigkeit ist Java vor allem im Zusammenhang mit der Entwicklung von Web-Applikationen im Internet eine der am häufigsten verwendeten Sprachen. Doch Java beschränkt sich nicht nur auf das Erstellen von Effekten für die Webseite, Java ist eine ausgewachsene Programmiersprache, mit der man alle anstehenden Probleme lösen kann. Die Syntax der Sprache ist an die von C++ angelehnt, Schlüsselwörter sind verändert bzw. in der Bedeutung erweitert worden, bestimmte Features von C++ wurden zugunsten der Übersichtlichkeit bzw. Sicherheit nicht in Java verwendet. Java Programme liegen im sogenannten Bytecode vor, der Maschinencode für einen fiktiven Prozessor, der von der VM, der virtuellen Maschine, ausgeführt wird. Durch die Verwendung des Bytecodes wird die Plattformunabhängigkeit von Java garantiert - dieser Vorteil wird aber durch die, im Vergleich zu anderen Sprachen wie C++, wesentlich langsamere Ausführungsgeschwindigkeit bezahlt. 2.2. Verwandtschaft von Java mit anderen Sprachen? Autor: Markus Reitz ------------------------------------------------- Java bietet eine Reihe von neuen Features, orientiert sich aber auch an etablierten Sprachen wie C++, Smalltalk oder Objective-C. Prinzipien aus diesen Sprachen wurden übernommen und teilweise erweitert. Die folgende Tabelle gibt einen kleinen Überblick über Features von Java, die aus anderen Sprachen quasi "entliehen" wurden. |-----|-----------|-------------| | C++ | Smalltalk | Objective-C | |--------------------------------|-----|-----------|-------------| | Primitive Datentypen | * | | | |--------------------------------|-----|-----------|-------------| | Universelle Basisklasse Object | | * | | |--------------------------------|-----|-----------|-------------| | Garbage Collection | | * | | |--------------------------------|-----|-----------|-------------| | Konstruktoren | * | | | |--------------------------------|-----|-----------|-------------| | Statische Typen | * | | | |--------------------------------|-----|-----------|-------------| | Bibliothek von Standardklassen | | * | | |--------------------------------|-----|-----------|-------------| | Interfaces | | | * | |--------------------------------|-----|-----------|-------------| Im Vergleich zu C++ besitzt Java folgende Unterschiede: - noch keine Templates - keine Operatorenüberladung - kein prozeduraler Overhead - keine Mehrfachvererbung von Klassen 2.3. WebBrowser und Java Autor: Markus Reitz ------------------------ Java zählt immer noch zu den jüngeren Programmiersprachen und die Entwicklung verläuft in manchen Bereichen noch mit hoher Geschwindigkeit. Insbesondere Zusatz-APIs findet man inzwischen wie Sand am Meer und praktisch jede neue Version des JDK bietet Verbesserungen oder Erweiterungen der bestehenden APIs. Dies wird insbesondere dann problematisch, wenn man Java-Applets schreiben möchte, die in den bekannten Web-Browsern von Netscape oder Microsoft laufen sollen. Häufig werden aktuelle Features nur unzureichend oder gar nicht unterstützt. Deshalb sollte man folgendes beachten: - Ältere Webbrowser unterstützen (im günstigsten Fall) nur die Version 1.0 der Sprache - Neuere Webbrowser unterstützen zumindest teilweise die Sprachversion 1.1 (Netscape ab Version 4.04-J2, Microsoft Internet-Explorer ab Version 4.0) Um eine möglichst große Anzahl an Plattformen bedienen zu können, muß man sich also am besten auf Features der Sprache beschränken, die schon in der UrVersion vorhanden waren. Bei neueren Features läuft man Gefahr, daß Besitzer älterer Browser ausgeschlossen werden. Eine andere Alternative bietet die Verwendung des von SUN erhältlichen Java-Plug-In's. Dadurch werden ältere Browserversionen mit den Features der aktuellen Java-Version aufgerüstet, wodurch es dann sogar möglich wird, Java 2 - Programme in Browsern ablaufen zu lassen, obwohl diese die neueste Version ursprünglich noch gar nicht unterstützen. Das Plug-In ist auf der Homepage von SUN erhältlich, problematisch ist allerdings die Größe. Es ist daher fraglich, ob Benutzer bereit sind, sich wegen eines kleinen Homepage-Effektes ein großes Plug-In herunterzuladen. Sinn macht das Plug-In daher nur bei wirklich größeren Programmen oder bei Programmen, die für den Einsatz im Intranet ausgerichtet sind. 2.4. Erste Schritte in Java Autor: Markus Reitz --------------------------- Erfahrungen mit der Sprache Java zu sammeln ist nicht schwer. Sofern man einen Internetzugang hat, kann man sich die Sprachdefinition, Compiler etc. direkt von SUN besorgen. Es empfiehlt sich die Verwendung der Java-Implementierung von SUN, denn diese ist die Referenz für alle anderen Implementierungen und normalerweise die, die sich am aktuellsten Sprachstandard orientiert. Lösungen von Microsoft oder Symantec haben den Nachteil, daß Features modifiziert oder gar nicht implementiert werden oder Fähigkeiten hinzukommen, die der ursprünglichen Sprachimplementation fehlen. Außerdem bietet das JDK (Java Development Kit) von SUN den Vorteil, daß es kostenlos verfügbar ist. Nachteil: Compiler und Tools arbeiten kommandozeilenorientiert, d.h. man schreibt den Quellcode mit einem beliebigen Editor, speichert die Datei und führt dann den Compiler aus. Bei Fehlern lädt man die Datei, korrigiert den Fehler und das ganze Spiel beginnt von vorne. Abhilfe schaffen IDEs (Integrated Development Environment), die das JDK steuern und Fehler und andere Meldungen direkt anzeigen, ohne daß der Quelltexteditor verlassen werden muß. Ein weiterer Vorteil von IDEs ist das Feature Syntax Highlighting, das die Schlüsselwörter der Sprache farblich hervorhebt und dadurch die Übersichtlichkeit steigert. Bezugsquelle für das JDK: <URL:http://www.java.sun.com/j2se/> 2.5. Ich habe das HelloWorld-Programm aus meinem Java-Buch abgeschrieben, aber es funktioniert nicht. :-( Autor: Hubert Partl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Newbie: Ich habe das HelloWorld-Programm aus meinem Java-Buch abgeschrieben, aber es funktioniert nicht. :-( Oldie: Das ist schon richtig :-) so, das HelloWorld-Beispiel dient dazu, dass Du die typischen Anfaenger-Fehler kennenlernst und in Zukunft vermeiden kannst. In diesem Fall kann ich nur raten: Du hast wahrscheinlich einen der folgenden typischen Newbie-Fehler gemacht: * Du hast das Programm nicht genau genug abgeschrieben (Tippfehler, Gross-Kleinschreibung, Sonderzeichen, Leerstellen), lies doch die Fehlermeldungen und Korrekturhinweise, die der Compiler Dir gibt. * Du hast das Programm nicht unter dem richtigen Filenamen abgespeichert. Wenn die Klasse HelloWorld heisst, muss das File HelloWorld.java heissen, nicht helloworld.java und auch nicht HelloWorld.java.txt, im letzteren Fall versuch es mit notepad "HelloWorld.java" * Du hast beim Compiler nicht den kompletten Filenamen mit der Extension angegeben (wieder mit der richtigen Gross-Kleinschreibung): javac HelloWorld.java * Du hast bei der Ausfuehrung nicht den Klassennamen ohne die Extension angegeben (wieder mit der richtigen Gross-Kleinschreibung): java HelloWorld * In der Umgebungsvariable PATH ist das Directory, in dem sich die JDK-Software befindet, nicht neben den anderen Software-Directories enthalten, versuch set PATH=%PATH%;C:\jdk1.2\bin oder wie immer das auf Deinem Rechner heissen muss. * Die Umgebungsvariable CLASSPATH ist (auf einen falschen Wert) gesetzt. Diese Variable sollte ueberhaupt nicht gesetzt sein, nur in seltenen Spezialfaellen und dann so, dass sie sowohl die Stellen enthaelt, wo die Java-Klassenbibliotheken liegen, als auch den Punkt fuer das jeweils aktuelle Directory. * Du hast den Compiler nicht in dem Directory bzw. Folder aufgerufen, in dem Du das Java-File gespeichert hast. * Du hast ein Applet als Applikation aufgerufen, oder umgekehrt. Applikatonen, die eine main-Methode enthalten, musst Du mit java Classname aufrufen. Applets, die ein "extends Applet" oder "extends JApplet" enthalten, musst Du innerhalb eines geeigneten HTML-Files mit appletviewer xxxxx.html oder mit Netscape oder Internet-Explorer aufrufen. Mehr darueber findest Du in meiner Java-Einfuehrung auf <URL:http://www.boku.ac.at/javaeinf/jein1.html#software> Aehnliche Hinweise findest Du im Java Glossary von Roedy Green auf <URL:http://mindprod.com/gloss.html> 3. Häufig gepostete Fragen ========================== 3.1. [LANG] - Frage bezüglich der Sprache Java. ----------------------------------------------- 3.1.1. Gibt es in Java keine Zeiger wie in C++? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Im Prinzip gibt es ein Konstrukt, das den Zeigern anderer Programmiersprachen, wie zum Beispiel C++, sehr ähnlich ist: die sogenannten Referenzen. Referenzen können, im Vergleich zu Zeigern in C++, nicht manipuliert werden; die sogenannte Zeigerarithmetik, bei der man Zeiger auf beliebige Speicherinhalte zeigen lassen kann, wurde aus Sicherheitsgründen nicht in Java übernommen. Der Inhalt einer Referenz kann einer anderen Referenz zugewiesen werden, Referenzen können miteinander verglichen werden. Wird in Java eine Objektvariable angelegt, so ist dies nichts weiter als ein Speicherplatz für eine Referenz für ein Objekt des angegebenen Typs. Der new-Operator erzeugt das eigentliche Objekt und liefert die Referenz darauf zurück, die dann in der Objektvariablen gespeichert wird. 3.1.2. Warum ist Referenz nicht gleich Referenz? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Problem: public class Test { public static void main (String[] args) { String a = "A"; String x = a; System.out.println(x); a = "B"; System.out.println(x); } } Die Ausgabe sollte doch eigentlich so aussehen: A B Denn die Variable x speichert doch eine Referenz auf den String a! Im ersten Fall hat a den Wert A und damit auch x, das ja auf diesen String verweist. Im zweiten Fall wird a geändert und damit müßte sich doch auch eigentlich der Wert von x ändern, weil x auf a verweist. Die Ausgabe, die das Programm liefert, ist jedoch: A A was eigentlich nicht in das Bild einer normalen Referenz passt. Ursache: Der Fehler liegt darin begründet, daß a nicht der eigentliche String ist, sondern nur eine Referenz auf diesen String. Durch x = a referenzieren beide Variablen den selben String und durch a = "B" verweist a auf einen anderen neuen String mit dem Wert B. Dies ändert jedoch nichts an der Referenz, die in x gespeichert ist. Somit ist die Ausgabe völlig korrekt. 3.1.3. Wie werden in Java Funktionsparamter übergeben, by value oder by reference? Autor: Paul Ebermann ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Java kennt (genauso wie C, aber im Gegensatz zu etwa Pascal) kein "Call by Reference". Wenn Objekte als Parameter verwendet werden, wird eben die Referenz "by Value" übergeben. Somit kann man zwar das Objekt (so es veränderbar ist) verändern, aber nicht die originale Variable, die als Parameter verwendet wurde. Beispiel für Call by Reference: (Pascal - [Syntax bestimmt nicht korrekt]) program ReferenzTest(input, output); var x, y : integer; procedure swap (var a: integer; var b: integer); var h : Integer; begin h := a; a := b; b := h; end; begin x := 1; y := 2; swap(x,y); writeLn('x = ', x, ', y = ', y , '.'); end. Das Programm gibt am Ende x = 2 und y = 1 aus. In Java geht das nicht: package de.dclj.faq; class CallByReferenceTest { static void swap (int a, int b) { int h = a; a = b; b = h; } public static void main(String[] test) { int x = 1; int y = 2; swap(x,y); System.out.println("x = " + x + ", y = " + y); } } Zur Call-by-Reference-Simulation bietet sich die Verwendung von Arrays an - da diese Objekte sind, wird ja nur die Referenz übergeben. class CallByReferenceSimulation { static void swap(int[] a, int[] b) { int h = a[0]; a[0] = b[0]; b[0] = h; } public static void main(String[] test) { int[] x = {1}; int[] y = {2}; swap(x,y); System.out.println("x = " + x[0] + ", y = " + y[0]); } } Einen etwas anderen Blickwinkel zu diesem Thema bietet die Webseite: <URL: http://purl.net/stefan_ram/pub/java_referenzvariablen_als_argument_de> 3.1.4. Warum gibt es in Java keine Destruktoren wie in C++? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In Java hat der Programmierer unter normalen Umständen keinen direkten Einfluß darauf, wann Objekte aus dem Speicher entfernt werden. So ist es unter anderem nicht möglich, ein Objekt per Befehl aus dem Speicher zu löschen. Dies erledigt der sogenannte Garbage-Collector, der Objekte aus dem Speicher entfernt, auf die keine Referenz mehr verweist. Dadurch wird verhindert, daß der Programmierer mehr oder minder mutwillig Speicherfehler erzeugen kann, die das Programm zum Absturz bringen könnten. Wann die Entfernung aus dem Speicher erfolgt, liegt im Ermessen des Computers. Es gibt also keinen definierten Zeitpunkt, wann ein Objekt nicht mehr existiert und deshalb ist es in den meisten Fällen nicht sinnvoll, Operationen zu definieren, die beim Löschen des Objektes ausgeführt werden. Man könnte (ungültige) Annahmen voraussetzen - zum Beispiel bei verketteten Listen, daß das Nachfolgerelement noch existiert, obwohl dies gar nicht der Fall ist - und damit Fehler verursachen. Destruktoren wie in C++ existieren deshalb nicht. Ist es aus irgendeinem dringenden Grund dennoch nötig, Operationen beim Löschen des Objektes auszuführen, so kann man eine Methode finalize definieren, die bei der Speicherbereinigung abgearbeitet wird. Wobei nicht garantiert wird die finalize methode überhaupt von der JVM abgearbeitet wird. 3.1.5. Warum funktioniert die equals Methode nicht? Autor: Markus Reitz Autor: Martin Erren ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Problem 1: Man hat eine eigene Klasse entworfen und möchte nun testen, ob zwei Objekte gleich sind. Fein, denkt man sich, dafür bietet die Klasse Object ja die Methode equals(Object obj). Doch das Programm mit Verwendung der equals(Object obj) Methode wird zwar korrekt übersetzt, der Vergleich funktioniert jedoch nicht so, wie er eigentlich sollte. Per Default sagt equals(), dass es sich um die selbe Instanz einer Klasse handelt: public class Object { ... public boolean equals(Object other) { return this == other; } ... } Lösung 1: Die Methode equals(Object obj) muß für jede Klasse neu überschrieben werden. Schließlich kann man nicht in allgemeiner Weise die Gleichheit zweier Objekte einer Klasse spezifizieren. Das ist insbesondere dann wichtig falls die Objekte in einer Set oder als Keys für eine Map verwendet werden. Dann muss allerdings auch die Methode hashCode() überschrieben werden. Siehe dazu auch die nächste Frage in dieser FAQ! Dazu sollte man sich auf alle Fälle in der JavaDoc zu Object, speziell die Methoden hashCode() und equals(Object obj) ansehen: <URL:http://java.sun.com/j2se/1.4/docs/api/java/lang/Object.html> Für Strings beispielsweise sind equals() und hashCode() bereits überschrieben. So dass String-Objekte als Keys verwandt werden können. Problem 2: public class Test { private int a; private int b; public Test (int a , int b) { this.a = a; this.b = b; } public boolean equals(Test other) { return (this.a == other.a) && (this.b == other.b); } } Verwendet man nun Objekte dieser Klasse in Containerklassen und hier insbesondere Methoden, die auf die Methode equals des Objekts zurückgreifen, so funktioniert dies nicht. Der Grund liegt in der falschen Signatur der Methode equals. Der Parameter muß vom Typ Object sein und nicht vom Typ der Klasse, zu dem die Methode gehört. Ansonsten existieren für die Klasse Test zwei Versionen der equals Methode: Eine, die von der Klasse Object geerbt wurde und die als Parametertyp auch Object besitzt und als zweite die oben definierte. Containerklassen verwenden aber die erste und da diese nicht verändert wurde, wird nicht das gewünschte Ergebnis erzielt. Lösung 2: public class Test { private int a; private int b; public void Test (int a , int b) { this.a = a; this.b = b; } //Die hash-Funktion ist aus dem Buch "Effective Java" von //Joshua Bloch. public int hashCode() { int result = 17; result = 37*result + this.a; result = 37*result + this.b; return result; } public boolean equals(Object obj) { //Für eine bessere Performance. if (this == obj) { return true; } //Wenn (obj == null) dann gibt instanceof false zurück //Siehe JLS 15.20.2 if (!(obj instanceof Test)) { return false; } Test other = (Test)obj; return (this.a == other.a) && (this.b == other.b); } } 3.1.6. Wenn ich eigene Objekte mit einer Hashtable/HashMap verwalte, kommt es zu sonderbaren Effekten. Wieso? Autor: Ingo R. Homann, Gerhard Bloch ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Damit eigene Objekte als Schluessel in einer Hashtable/HashMap funktionieren, muessen zwei Bedingungen erfuellt sein: 1. Wenn man die equals-Methode einer Klasse überschreibt, sollte man beachten, dass man auch die hashCode-Methode überschreiben muß! 2. Nichts, was equals-Vergleiche beeinflusst, darf geaendert werden, waehrend das Objekt in einer Hashtable/HashMap ist! Insbesondere darf sich der Hashcode nicht aendern. Man muss sicherstellen, dass Objekte, die laut der equals-Methode gleich sind, auch einen identischen Hashcode haben müssen. Der Umkehr-Schluss, dass ungleiche Objekte (bei denen equals false liefert) zwangsläufig auch unterschiedliche Hashcodes haben müßen, gilt nicht. Trotzdem sollte man als Programmierer versuchen, möglichst darauf hinzuarbeiten (und keineswegs z.B. für alle Objekte einen gleichen, konstanten Hashcode liefern!), damit die Implementierungen von Hashtable und HashMap effizient arbeiten können. Als Folge dieser Forderung sollten zur Berechnung des Hashcode genau die Attributwerte einbezogen werden, die auch zur equals-Bestimmung verwendet werden, insbesondere aber keine anderen Werte! Die Forderung kann auch so formuliert werden: a.equals(b) => (a.hashCode() == b.hashCode()) oder (a.hashCode() != b.hashCode()) => !a.equals(b) Eine Hashtable funktioniert vereinfacht folgendermassen: Die Schluessel-Wert-Paare werden beim Einfuegen in Buckets ("Eimer") verteilt. Dabei entscheidet der Hashcode des Schluessels, in welchen Bucket er kommt. Beim Suchen wird anhand des Hashcodes des Suchschluessels der Bucket ermittelt, in dem das gesuchte Objekt liegen muss. So können (außer in ungünstigen Fällen) fast alle Schluessel der Tabelle ausgeschlossen werden, der Suchschluessel muss nur noch innerhalb des Buckets gesucht werden; falls er (genauer: ein Schluessel, dessen equals-Vergleich mit dem Suchschluessel true ergibt) dort gefunden wird , wird der Wert zurueckgegeben. Dies funktioniert deshalb, weil aufgrund der obigen Forderung nur diejenigen Objekte uebereinstimmen koennen, die auch im Hashcode uebereinstimmen (equals => gleicher Hashcode). Ein Problem ergibt sich, wenn nach dem Einfuegen ein Schluessel geaendert wird. Da sich dadurch auch dessen Hashcode (mit ziemlicher Sicherheit) aendert, liegt er nun im falschen Bucket. Die Hashtable bekommt von der Aenderung ja nichts mit! Deshalb ist zu beachten, dass sich Schluessel nicht aendern, solange sie in einer Hashtable verwendet werden. Sichergestellt werden kann dies nur durch immutalbe Objekte (siehe 3.10.7). Beispiel fuer das richtige Ueberschreiben von hashCode: public class Name { private static int zaehler= 0; private String vorname private String nachname; private final int id = zaehler++; public Name(String vorname, String nachname) { this.vorname = vorname; this.nachname = nachname; } public boolean equals(Object o) { // die id ist nur fuer interne Zwecke und hat keinen Einfluss // auf Gleichheit if(o instanceof Name) { Name n = (Name)o; return vorname.equals(n.vorname) && nachname.equals(n.nachname); } else { return false; } } public int hashCode() { // Es werden genau die Werte einbezogen, die auch in der // equals-Methode verwendet werden int result = 17; result = 37*result + vorname.hashCode(); result = 37*result + nachname.hashCode(); return result; } } 3.1.7. Was bedeutet das Schlüsselwort final? Orginalautor: Markus Reitz Autor: Paul Ebermann ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Klassen oder Methoden, die das Schlüsselwort final tragen, können nicht mehr überschrieben werden, wenn von dieser Klasse abgeleitet wird. Die Verwendung dieses Schlüsselwortes bietet sich aus zwei Gründen an: - Realisierung von Sicherheitsmaßnahmen - Codeoptimierung Sicherheitsmaßnahmen werden realisiert, weil es nicht möglich ist, die Bedeutung der Methode in abgeleiteten Klassen zu verändern und damit bestehende Konzepte zu durchbrechen. Codeoptimierung deshalb, weil der Compiler nun davon ausgehen kann, daß sich an den Methoden nichts mehr ändern wird und deshalb elegantere Codeoptimierungen möglich sind. In Anbetracht der Tatsache, daß final-Methoden nicht mehr verändert werden können, muß man sich beim Programmentwurf sehr sicher sein, daß das Feature des Überschreibens definitiv nicht für diese Methode benötigt wird, ansonsten kann es bei Verbesserungen des Codes zu Problemen kommen. Wenn Variablen mit dem Schlüsselwort final deklariert werden, hat das zur Folge, dass ihr Wert nur einmal zugewiesen (initialisiert) und dann nicht mehr verändert werden kann. Wenn der Compiler das nicht nachweisen kann, gibt es einen Fehler. * Bei Exemplarvariablen kann die Zuweisung direkt in der Deklaration, in einem Initialisierungsblock oder in (dann allen) Konstruktoren erfolgen. Bei Klassenvariablen kann die Zuweisung direkt in der Deklaration oder in einem Klassen-Initialisierungsblock erfolgen. * Bei lokalen Variablen muss die Zuweisung in der Deklaration oder irgendwo später im Code, jedenfalls vor dem ersten Lese-Zugriff erfolgen. * Bei Methoden- oder Konstruktor-Parametern mit final kann keine Zuweisung erfolgen. 3.1.8. Warum wird der dynamische Parametertyp bei überladenen Funktionen nicht beachtet? Autor: Uwe Günther, Erwin Hoffmann, Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Dies ist ein korrektes Verhalten gemäß der Java-Sprachspezifikation! Die Entscheidung, welche überladene Methode bei der Übergabe eines bestimmten Parameters auszuwählen ist, wird nicht anhand des Typs des an die Methode übergebenen Objekts getroffen, sondern anhand des Typs der Referenz, die auf das übergebene Objekt verweist. Diese Entscheidung wird vom Compiler getroffen. Hier muss also zur Compilezeit entschieden werden, welche Methode aufgerufen werden soll. Der Compiler kann sich nur auf den Referenztyp beziehen, weil der Objekttyp nur dynamisch zur Laufzeit eines Programms festgestellt werden kann. Das Ganze demonstriert ein Beispielprogramm: public class BasisKlasse {} public class AbgeleiteteKlasse extends BasisKlasse {} public class Test { public static void methode(BasisKlasse eineKlasse) { System.out.println("Methode mit BasisKlasse!"); } public static void methode(AbgeleiteteKlasse eineKlasse) { System.out.println("Methode mit AbgeleiteteKlasse!"); } public static void testMethode(BasisKlasse a) { if (a instanceof AbgeleiteteKlasse) { System.out.print("Abgeleitet: "); } else { System.out.print("Basis: "); } //Welche Methode wird jetzt gerufen? methode(a); } public static void main (String[] params) { BasisKlasse a = new BasisKlasse(); AbgeleiteteKlasse b = new AbgeleiteteKlasse(); testMethode(a); testMethode(b); } } Das Programm erzeugt folgende Ausgabe: Basis: Methode mit BasisKlasse! Abgeleitet: Methode mit BasisKlasse! 3.1.9. Was bedeutet super()? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Werden Ableitungen von Klassen gebildet und dabei Funktionen redefiniert (überschrieben), so ist es in vielen Fällen nötig, auf die Funktionalität der Basisklasse zurückzugreifen. Mit super.methode() teilt man mit, daß man die Methode der Basisklasse und nicht die Methode der aktuellen Klasse benutzen will. Innerhalb eines Konstruktors ist es möglich mit super() den Konstruktor der Basisklasse aufzurufen. Findet im Konstruktor kein expliziter Aufruf mit super() statt, so wird automatisch der parameterlose Konstruktor (Standardkonstruktor) der Basisklasse aufgerufen. 3.1.10. Was sind anonyme Arrays? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Ab Java 1.1 ist folgender Code gültig: int i[]; i = new int[] {1, 2, 3}; Es ist jetzt also möglich, Arrays auch außerhalb der Definition mit den gewünschten Werten zu initialisieren, indem man mit {...} einfach die Werte angibt, die das neue Array tragen soll. 3.1.11. Gibt es in Java einen Prä-Prozessor wie in C++? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Nein. In C++ existiert der sogenannte Prä-Prozessor, der es mit bestimmten Kommandos erlaubt, Teile des Codes zu übersetzen und andere Teile bei der Übersetzung zu überspringen. Unter Umständen wäre es hilfreich, wenn auch Java eine solche Möglichkeit der Compilationssteuerung zulassen würde, doch einen Prä-Prozessor gibt es hier nicht. Man kann dies aber, zumindest ansatzweise, mit if-Statements nachbilden. Dazu definiert man eine boolesche Variable DEBUG und in Abhängigkeit von dieser Variablen sollen bestimmte Codeteile ausgeführt werden, andere dagegen nicht. public class Test { final static boolean DEBUG = true; public static void main (String[] params) { int i = 12; if (DEBUG) { System.out.println("Der Wert von i ist " + i); } } } Verwendet man nun noch Optimierer, die unbenutzten Code aus den Klassendateien entfernen, so hat man im Prinzip ein ähnliches Verhalten wie beim Prä-Prozessor von C++. In diesem Zusammenhang ist es noch erwähnenswert, daß das Verfahren nur bei der if-Abfrage möglich ist, denn das Java-System prüft normalerweise darauf, ob Codezeilen erreicht werden können oder nicht und gibt gegebenenfalls Fehlermeldungen aus. Das if-Statement ist jedoch wie oben beschrieben erweitert worden, um das gewünschte Verhalten simulieren zu können. Dahingegen wird folgender Code mit einer Fehlermeldung quittiert: public class Test { final static boolean DEBUG = true; public static void main (String[] params) { int i = 12; while (DEBUG) { System.out.println("Der Wert von i ist " + i); } } } Denn es würde sich je nach Zustand von DEBUG eine Endlosschleife ergeben. 3.1.12. Existiert der const Modifizierer von C++ auch in Java? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ kennt das Schlüsselwort const, das es erlaubt, konstante Objekte zu definieren, deren Wert man nicht ändern kann. Auf ein solches Objekt kann man nur Methoden anwenden, die ebenfalls als const definiert sind, die also die Daten des Objektes nicht ändern können. Ab Java 1.1 können Argumente mit dem Modifizierer final als konstant definiert werden. Bei einer Objektreferenz bedeutet dies allerdings nur, daß die Referenz konstant bleibt, nicht aber das Objekt, auf das die Referenz verweist. Da Java den Modifizierer const nicht kennt, ist es aber trotzdem recht einfach möglich, diesen nachzubilden. Alles, was man dazu braucht, ist ein Interface, das die konstanten Methoden enthält, die das Objekt nicht ändern können. Will man nun ein konstantes Objekt zurückgeben oder erzeugen, dann gibt man einfach eine Referenz vom Typ des Interfaces zurück und schon hat man das Gewünschte erzielt. Folgendes Beispiel soll das Ganze etwas verdeutlichen: interface KonstanterTyp { public int get(); } public class NichtKonstanterTyp implements KonstanterTyp { int i; public void set(int i) { this.i = i; } public int get() { return this.i; } } Ein konstantes Object wird dann durch KonstanterTyp A = new NichtKonstanterTyp(); erzeugt. Anmerkung: Im Prinzip wird hier durch das Interface eine Art Untermenge der Klasse NichtKonstanterTyp definiert. Bei C++ läuft dieser Prozeß im Prinzip auch so ab, nur wird hier vom Compiler automatisch diese Untermenge durch den Modifizierer const erzeugt und der Programmierer muß sich hierum nicht kümmern. In Java muß man diesen Prozeß selbst durchführen. 3.1.13. Wie kann man Referenzen von Übergabeparametern ändern? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Von Sprachen wie C++ oder Pascal kennt man die Möglichkeit, Referenzen, die an die Methode übergeben werden, innerhalb der Methode zu ändern, an die sie übergeben wurden, wodurch nach dem Aufruf der Methode die Referenzen auf andere Objekte verweisen. In Java gibt es zwei Möglichkeiten, diesen Effekt zu erreichen: - Einführen einer weiteren Ebene mit Wrapper-Klassen, die spezielle Referenzen zur Verfügung stellen. - Verwendung von eindimensionalen, einelementigen Arrays. Der erste Punkt dürfte klar sein, jedoch steht der Aufwand erst dann in einem vernünftigen Verhältnis zum Ergebnis, wenn diese Art der Parametermodifikation öfter innerhalb des Programms vorkommt, denn ansonsten lohnt sich das Design einer speziellen Klasse nicht unbedingt. Den zweiten Weg verdeutlicht das folgende Programm: public class Test { public void parameterModifikation(Object[] paramter) { parameter[0] = "Neue Referenz"; } public static void main (String[] args) { Objcet parameter[] = new Object[1]; parameter[0] = "Zu modifizierender Parameter." Test test = new Test(); System.out.println(parameter[0]); test.parameterModifikation(parameter); System.out.println(parameter[0]); } } Das Beispielprogramm liefert folgende Ausgabe: Zu modifizierender Parameter Neue Referenz 3.1.14. Wie erzeuge ich eine tiefe Kopie eines Objektes mit möglichst wenig Aufwand? Autor: Markus Reitz Autor: Ingo R. Homann ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Weist man einer Objektvariablen eine andere zu, so wird nur die Referenz kopiert und beide Objektvariablen können das Objekt modifizieren und diese Modifikation auch sehen. Dies ist das Standardverhalten von Java. Durch clone() wird z.B. eine flache Kopie von dem Vector angelegt, d.h. z.B. das Hinzufügen eines neuen Elements in die Kopie des Vektors ist im Original-Vector nicht sichtbar. Die enthaltenen Objekte jedoch werden *nicht* mitkopiert, d.h. Änderungen an den enthaltenen Objekten sind in beiden Vectoren sichtbar. Wenn wirklich alles - also auch die enthaltenen Objekte und die rekursiv darin enthaltenen Objekte - kopiert werden soll, dann braucht man eine sog. tiefe Kopie. Ein sehr eleganter Weg, eine tiefe Kopie eines Objektes zu erzeugen, verwendet den Serialisierungs-Mechanismus. Objekte, die man mit diesem Verfahren kopieren möchte, müssen also das Interface Serializable implementieren. import java.io.ByteArrayOutputStream; import java.io.ByteArrayInputStream; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.IOException; public class TiefeKopie { public static Object kopiere(Object einObjekt) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.write(einObjekt); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); } } Zuerst werden zwei Ausgabeströme angelegt: Ein Byte-Strom und ein Object-Strom. Der Byte-Strom wird hinter den Object-Strom geschaltet und das zu kopierende Objekt wird auf dem Object-Strom ausgegeben und durch die Verknüpfung der beiden Ströme schließlich in den Byte-Strom geschrieben. Dieser Byte-Strom wird dann mit einem zweiten Strom wieder eingelesen und mit einem weiteren Object-Strom wird aus den einzelnen Bytes wieder ein Objekt rekonstruiert. Die Ausgabe des Object-Stroms ist dann das kopierte Objekt. Die Anwendung der Klasse zeigt folgendes Codefragment: public class Test { public static void main(String[] args) { int einArray[] = {56, 42, 67, 90, 12, 45}; int tiefeKopieVonEinArray = (int[]) TiefeKopie.kopiere(einArray); } } Ein Array ist in Java nichts weiteres als ein Objekt und implementiert auch das Serializeable-Interface, weshalb das obige Kopierverfahren problemlos greifen kann. Anmerkung: Bei komplexen Objekten, die viele Referenzen auf andere Objekte besitzen schlägt das Verfahren meist fehl. Der Serialisierungsmechanismus verwendet Rekursion, um alle referenzierten Objekte zu speichern. Wird auf Objekte referenziert, die wieder auf Objekte referenzieren usw. kann es geschehen, daß der Stack-Speicher für die Rekursion überläuft. Diese elegante Methode der tiefen Kopie kann also nur bei einfachen Objekten angewendet werden. In Sachen Performance liegt diese Lösung jedoch um einiges hinter der klassischen Lösung alle Elemente einzeln zu kopieren. Dessen sollte man sich klar sein, wenn man diese Lösung einsetzt. 3.1.15. Wie kann ich in Java eine dem Programmierer unbekannt Anzahl gleichartiger Objekte erzeugen und ihnen passende Namen zuweisen, also label1, label2 usw.? Autor: Michael Paap, Christian Kaufhold ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das ist in dieser Form nicht sinnvoll. Die bessere Lösung besteht in der Verwendung eines Arrays. In Java kann die Größe eines Arrays bei seiner Erzeugung zur Laufzeit (einmalig!) festgelegt werden. Ein Zugriff auf die einzelnen Objekte erfolgt dann über den Arrayindex: // Deklaration Label[] myLabels; ... // Erzeugung und Initalisierung zur Laufzeit int anzahl = 10; myLabels = new Label[anzahl]; for (int i = 0; i < myLabels.length; i++) { myLabels[i] = new Label("Label Nr. " + i); } // Verwendung myLabel[3].setBackground(Color.red); 3.1.16. Wie kann ich den Typ enum aus C++ in Java umsetzen? Autor: Markus Reitz, Uwe Günther, Ulf Jährig ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ bietet einen sogenannten Enumerationstyp. Eine Variable eines solchen Typs kann nur definierte Werte annehmen, die mit symbolischen Namen bezeichnet werden. Folgende Enumeration könnte zum Beispiel für eine Ampel verwendet werden: enum Ampel {ROT, GELB, GRUEN}; Java bietet diesen Typ nicht, kann diesen aber recht einfach mit einem Interface nachbilden: interface Ampel { public static final int ROT = 1; public static final int GELB = 2; public static final int GRUEN = 3; } Ein C++ Compiler führt automatisch die Zuordnung von Variablennamen zu eindeutigen Zahlenwerten durch, in Java muß der Programmierer diesen Prozeß erledigen. Die Verwendung differiert zwischen Java und C++: Ampel eineVariable = ROT; // Das ist C++ int eineVariable = Ampel.ROT; // Das ist Java Man erkennt einen großen Nachteil der Java-Version: Da es sich um eine int-Variable handelt, ist es prinzipiell möglich, jeden Wert an diese Variable zuzuweisen, eben nicht nur Werte aus dem Wertebereich rot, gruen und gelb. Im Gegensatz dazu läßt ein C++-Compiler nur Zuweisungen von Symbolen aus dem Enumerationstyp zu. Insofern ist die Nachbildung in Java weniger sicher als das C++-Pendant und sollte daher mit Vorsicht angewendet werden. Sprich sie ist nicht Typsicher! Wie schon oben angesprochen, bietet diese direkte Implementierung den Nachteil, daß sie nicht typsicher ist. Doch es ist recht einfach möglich, mit Java eine typsichere Implementierung zu erhalten: public final class Ampel { private String name; public final int ord; private static int obereGrenze = 0; private Ampel(String name) { this.name = name; this.ord = obereGrenze++; } public String toString() { return this.name; } public static int groesse() { return this.obereGrenze; } public static final Ampel ROT = new Ampel("Rot"); public static final Ampel GELB = new Ampel("Gelb"); public static final Anpel GRUEN = new Ampel("Gruen"); } Besonders interessant ist hier die Kombination von automatischer Zuweisung eines eindeutigen Zahlenwertes mit den Symbolwerten eines Strings. Auf den ersten Blick sieht die Klassendefinition ziemlich kompliziert und unverständlich aus, das Prinzip ist jedoch nicht schwer zu verstehen: - Zuersteinmal ist die Klasse als final deklariert, wodurch verhindert wird, daß von dieser Klasse Ableitungen gebildet werden können. Der Aufzählungstyp kann also - weder absichtlich noch unabsichtlich - durch Vererbung verändert werden. - Der Konstruktor der Klasse ist private, dadurch kann er nur von der Klasse selbst aufgerufen werden. Damit sind die einzigen Instanzen, die von Ampel erzeugt werden können, die, die den public-Variablen der Klassen zugewiesen sind. - Einer Referenz vom Typ Ampel können durch diese Maßnahmen nur die Werte {ROT, GELB, GRUEN} zugewiesen werden, andere Instanzen vom Typ Ampel sind ausgeschlossen, weil sie niemals existieren werden. - Die Überlagerung von toString hat den Zweck, einem das Leben beim Debuggen einfacher zu machen. Will man nun diesen Typ von Enumeration benutzen, so sieht das in etwa wie folgt aus: public class Test { public static void main(String[] args){ Ampel meineAmpel = Ampel.ROT; if (meineAmpel == Ampel.ROT) { System.out.println("Ampel ist " + meineAmpel + ". "); System.out.println("Anhalten!"); } if (meineAmpel == Ampel.GELB) { System.out.println("Ampel ist " + meineAmpel + ". "); System.out.println("Motor starten -oder- Anhalten!"); } if (meineAmpel == Ampel.GRUEN) { System.out.println("Ampel ist " + meineAmpel + ". "); System.out.println("Gib Gas!"); } } } Dieser Typ der Enumeration verwendet also Referenzen und nicht, wie Version 1, int-Werte, die nicht typsicher sind. Durch die Typprüfungsmechanismen von Java wird diese Art von Enumeration vollkommen typsicher und steht dem enum Konstrukt von C++ nun in nichts mehr nach. Wer mehr über dieses Pattern erfahren möchte sei dem sei folgender Link empfohlen: <URL:http://www.javaworld.com/javaworld/jw-07-1997/jw-07-enumerated.html> 3.1.17. Kann man Mehrfachvererbung mit Java simulieren? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Java kennt im Gegensatz zu C++ nicht das Feature der Mehrfachvererbung, eine Klasse kann nur genau einen Vorfahren haben, im Gegensatz zu beliebig vielen bei Mehrfachvererbung. Häufig liest man, daß man die Mehrfachvererbung mit Hilfe von Interfaces nachbilden kann, indem man die Methodenschnittstelle der einzelnen Klassen als Interfaces definiert und diese Interfaces alle gleichzeitig von der fraglichen Klasse implementieren läßt, denn dies ist in Java möglich. Mit Mehrfachvererbung hat dies aber nichts zu tun, wenn man den eigentlichen Sinn und Zweck von Vererbung betrachtet. Vererbung ist eines der möglichen Prinzipien, um die Codewiederverwertung zu garantieren. Code, der in mehreren Klassen benötigt wird, ist nur in der Basisklasse aufgeführt und durch die Vererbung können die Erben auch diesen Code benutzen. Der Code steht also nur an einer Stelle. Bei der oben beschriebenen Nachbildung wird in keiner Weise Code gespart, denn die Methoden der Interfaces müssen ja von der Klasse noch implementiert werden. In diesem Sinne kann man Mehrfachvererbung nicht in Java nachbilden. 3.1.18. Wie realisiere ich eine variable Parameterliste? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Es gibt Anwendungsfälle, in denen es äußerst nützlich wäre, einer Methode eine variable Anzahl an Parametern übergeben zu können. Ein Anwendungsfall wäre eine Klasse beliebig-dimensionaler Vektoren. Ein Konstruktor dieser Klasse müßte die Initialisierung eines Vektors mit bestimmten Werten erlauben und da beliebig-dimensionale Vektoren von dieser Klasse verarbeitet werden, müßte der Konstruktor mit einer variablen Parameteranzahl arbeiten können. Mit einem Trick kann man eine variable Parameterliste realisieren: Man übergibt der Methode ein Array von Referenzen auf die Klasse Object. Da in Java alle Objekte von der Klasse Object abstammen - sie ist quasi die Klasse aller Klassen - kann das Array Referenzen auf beliebige Objekte aufnehmen. Innerhalb der Methode kann dann mit dem length-Feld des Arrays die Anzahl der übergebenen Parameter ermittelt werden. Mit dem instanceof-Operator von Java kann dann der Typ des Objekts ermittelt werden, auf den die Referenzen verweisen und an Hand dieser Informationen kann man dann festlegen, was getan werden soll. Ein Programmfragment verdeutlicht das bisher Gesagte: public class Test { void methodeMitVariablerParameterListe(Object[] parameterList) { //Länge: parameterList.length ===> Anzahl der Parameter //Typ: if (parameterList[i] instanceof <Type>) } } Ein Nachteil ist offensichtlich: Es kann nur genau eine Methode mit variabler Parameterliste geben bzw. innerhalb dieser einen Methode müssen alle Variationen berücksichtigt und implementiert werden, was nicht unbedingt zur Übersichtlichkeit des Programms beiträgt. Ein Aufruf der Methode gestaltet sich nun wie folgt: Test obj = new Test(); obj.methodeMitVariablerParameterListe(new Object[] { paramObject1, paramObject2, ..., paramObjectn }); Will man elementare Datentypen wie double oder int an die Methode übergeben, so muß man die zugehörigen Wrapper-Klassen verwenden, die diese elementaren Datentypen in Klassen einpacken. 3.1.19. Wie realisiere ich eine Methodenauswahl nach den dynamischen Parametertypen? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Java wählt Methoden nach dem statischen Typ der Übergabeobjekte aus. Man kann jedoch eine dynamische Auswahl simulieren, indem man das oben angesprochene Prinzip der variablen Parameter hierauf überträgt. Man prüft die übergebenen Referenzen mit dem instanceof-Operator, der den dynamischen Typ des Parameters liefert. Anhand dieser Informationen kann man dann ein dynamisches Verhalten der Methodenaufrufe realisieren. Auch hier gilt, wie auch schon bei der variablen Parameterliste, daß eine einzige Methode alle Aufrufmöglichkeiten abdecken muß. 3.1.20. Sind Methoden in Java immer virtuell? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In Java sind alle Methoden virtuell, eine Unterscheidung zwischen virtuellen und nicht-virtuellen Methoden wie zum Beispiel in C++, gibt es in Java nicht. Ist es für die Funktionsweise eines Objektes wichtig, daß die vorhandenen Methoden nicht überschrieben werden können - etwa um zu verhindern, daß sich die Funktionalität des Objekts dadurch grundlegend ändern läßt - so muß man entsprechende Methoden mit dem Modifizierer final deklarieren, der ein Überschreiben verhindert. 3.1.21. Ich stosse ab und zu auf den Begriff "Wrapper-Klassen". Könnte mir jemand erklären was das ist? Autor: Stephan Menzel ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Eine sehr gute Frage, auf die ich mal mit einem Zitat aus Go to Java 2 antworten moechte, denn sie wird zu selten gestellt: Zitat (Goto Java 2): Zu jedem primitiven Datentyp in Java gibt es eine korrespondierende Wrapper-Klasse. Diese kapselt die primitive Variable in einer objektorientierten Hülle und stellt eine Reihe von Methoden zum Zugriff auf die Variable zur Verfügung. Zwar wird man bei der Programmierung meist die primitiven Typen verwenden, doch gibt es einige Situationen, in denen die Anwendung einer Wrapper-Klasse sinnvoll sein kann. Diese Klassen, wie zum Beispiel "Integer" koennen einem das Leben angenehmer gestalten, wenn man Dinge tun muss, die man mit einfachen ints tun will, aber mangels vorhandener Methoden nicht kann, weil diese Primitiven eben keine richtigen Objekte sind. Zum Beispiel kann Integer (im Gegensatz zu int) Strings nach Zahlen parsen, oder Integers als Binaercode ausgeben oder als Hex oder in andere Zahlentypen umwandeln und vieles mehr. Vielleicht ist es fuer Dich noch interessant zu erfahren, dass diese Wrapper Klassen in der Praxis oftmals nicht instantiiert, sondern ihre Methoden statisch aufgerufen werden. Um zum Beispiel aus dem String "42" das betreffende int zu machen, rufst Du: String zweiundvierzig = new String ("42") ; int answer = Integer.parseInt (zweiundvierzig) ; Das brauchst Du zum Beispiel zum Auswerten von GUI-Zahlenfeldern. Ich hoffe, ich konnte ein wenig Licht ins Dunkel bringen. Das Verstaendnis von Wrapperklassen und deren Sinn halte ich naemlich fuer essentiell, wenn man mit OOP beginnt. 3.1.22. Warum ist private nicht privat? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public class Person { private int kontostand; public Person(int kontostand) { this.kontostand = kontostand; } public void addGehalt(int gehalt) { this.kontostand += gehalt; } public void klauen(Person opfer) { this.kontostand += opfer.kontostand; opfer.kontostand = 0; } public void zeigeKontostand() { System.out.println("Kontostand: " + this.kontostand); } } public class Test { public static void main(String[] args) { //Der Dieb eröffnet ein Konto Person dieb = new Person(10); //Das Opfer eröffnet ein Konto Person opfer = new Person(50000); //Das Opfer bekommt Gehalt opfer.addGehalt(10000); //Der Dieb geht an die Arbeit dieb.klauen(opfer); //Das opfer ist nun pleite!!! opfer.zeigeKontostand(); //Und der Dieb un 60000 Euro reicher. dieb.zeigeKontostand(); } } Wenn man obiges Programm testet, so wird man feststellen, daß es möglich ist, die private-Datenfelder eines Objektes zu manipulieren; diese Daten sind also nicht privat im sonst üblichen Sinne. Allerdings ist die Privatsphäre nur für Objekte der gleichen Klasse aufgehoben, Objekte anderer Klassen haben keinen Zugriff auf die private-Daten von Objekten anderer Klassen. Damit ist obige Möglichkeit nicht weiter tragisch, denn der Entwickler der Klasse kann eine wie oben gezeigte Möglichkeit wirksam unterbinden. Wenn er dies nicht tut, so ist die Schuld bei ihm zu suchen, denn nur der Entwickler allein ist für das Verhalten der Klassen zuständig und nur der Entwickler muß solche Möglichkeiten durch das Design ausschließen. Die private-Deklaration ist damit als Deklaration der Privatsphäre gegenüber Objekten anderer Klassen zu sehen, zwischen Objekten der gleichen Klasse herrscht ein freundschaftliches Verhältnis, sie dürfen sich gegenseitig in die Daten schauen. 3.2. [STRING] - Fragen die unmittelbar mit Strings zu tun haben. ---------------------------------------------------------------- 3.2.1. Wie vergleiche ich zwei Strings in Java? Autor: Markus Reitz, Martin Erren ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Problem: Man versucht zwei Strings in der Form if(stringEins == stringZwei) { System.out.println("stringEins und stringZwei sind gleich."); } zu vergleichen und erhält alles andere als ein richtiges Ergebnis. Der Grund ist der, daß mit dem "=="-Operator nur die beiden Referenzen miteinander verglichen werden, nicht jedoch die Objekte. Man erhält deshalb womöglich auch bei zwei gleichen Strings das Ergebnis, daß sie verschieden sind. Für den inhaltlichen Vergleich, nicht nur von Strings, sondern allgemein von Objekten, wird in Java die Methode equals(Object obj) verwendet, die nicht immer nur Referenzen, sondern je nach Klasse auch die Inhalte (sprich ihre Daten) vergleicht. Obige Abfrage müßte also if(stringEins.equals(stringZwei)) { System.out.println("stringEins und stringZwei sind gleich."); } lauten, damit das gemacht wird, was eigentlich gewünscht ist. Im Zusammenhang mit Strings ist noch eine Besonderheit zu erwähnen: if ("Java".equals(stringZwei)) { System.out.println("stringZwei ist gleich zu Java."); } ist zulässig, der Compiler erzeugt aus der Zeichenkette automatisch ein String-Objekt; man muß also nicht zuerst ein Objekt anlegen und den String Java dort speichern. 3.2.2. Wie wandle ich einen String in einen Integer? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Klassen können Methoden besitzen, die die Konvertierung eines Objekts dieser Klasse in ein Objekt einer anderen Klasse übernehmen. Zu dem elementaren Java-Datentyp int gibt es eine sogenannte Wrapper-Klasse Integer, die den elementaren Datentyp in einer Klasse kapselt .Diese Klasse stellt eine Methode (in diesem Fall eine Klassenmethode) zur Verfügung, die das Gewünschte leistet: public class Test { public static void main (String[] params) { String stringMitZahl = "50"; int zahl = 0; try { zahl = Integer.parseInt(stringMitZahl); } catch (NumberFormatException e) { e.printStackTrace(); } zahl = zahl + 10; System.out.println("Die Variable zahl = " + zahl); } } 3.2.3. Wie wandle ich einen Integer in einen String um? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Dieses Problem ist genau das Gegenteil des vorherigen. Wie die Klasse Integer, so besitzt auch die Klasse String eine Methode, die das Problem löst, allerdings heißt die Methode nicht parseString, was man analog schließen könnte, sondern valueOf. public class Test { public static void main (String[] params) { int zahl = 50; String stringMitZahl = String.valueOf(zahl); System.out.println("Die Variable stringMitZahl = " + stringMitZahl); } } 3.2.4. Wie wandle ich einen Integer in einen HexString um? Autor: Uwe Günther ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das Problem ist ähnlich wie das Konvertieren von einem Integer in einen String. Wie die Klasse String die Klassenmethode valueOf besitzt, um einen String zu erzeugen, so besitzt die Klasse Integer die Klassenmethode toHexString(int i). public class Test { public static void main (String[] params) { int zahl = 50; String stringAlsHex = Integer.toHexString(zahl); System.out.println("Die Variable stringAlsHex = " + stringAlsHex); } } Der Nachteil der Integer.toHexString(int i) Methode ist, dass sie alle führenden Nullen einer Hex-Repräsentation abschneidet. 3.2.5. Wie kann ich eine Zahl formatieren und wie lege ich die Anzahl der Nachkommastellen fest? Autor: Karsten Schulz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das kann die Klasse java.text.NumberFormat und deren Abkömmlinge erledigen. Beispiel: import java.text.DecimalFormat; public class Zahl { public static void main(String[] args) { double betrag = 1000d/3d; // -> 333.333333... DecimalFormat df = new DecimalFormat("#.##"); System.out.println(df.format(betrag)); df = new DecimalFormat("#.## DM"); System.out.println(df.format(betrag)); df = new DecimalFormat("0000.0000"); System.out.println(df.format(betrag)); } } Die Ausgabe des Programms sind formatierte Dezimalzahlen: 333,33 333,33 DM 0333,3333 In der API-Dokumentation der Klassen java.text.Decimalformat und java.text.NumberFormat werden alle weiteren Formatierungsoptionen erläutert. Falls die Umwandlungen von double nach String zeitkritisch durchgeführt werden müssen, lohnt sich ein Blick auf <URL:http://www.onjava.com/pub/a/onjava/2000/12/15/formatting_doubles.html> Dort erklärt Jack Shirazi (Autor des Buches "Java Perfomance Tuning") andere Konvertierungsmethoden. 3.2.6. Wie kann ich ein Datum formatieren? Autor: Karsten Schulz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das machen andere für Dich,... Benutze einfach die Klassen DateFormat aus dem package java.text. Folgendes Beispiel zeigt Dir die Anwendung der SimpleDateFormat-Klasse: import java.util.Date; import java.text.SimpleDateFormat; public class Datum { public static void main(String[] args) { SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd"); System.out.println(sd.format(new Date())); sd.applyPattern("dd.MM.yyyy"); System.out.println(sd.format(new Date())); } } Die Ausgabe des Programms sind formatierte Datümer: 2000-12-24 24.12.2000 In der API-Dokumentation zur Klasse java.text.SimpleDateFormat sind die Kürzel der verschiedenen Datumskomponenten für Tag, Monat, usw. aufgeführt. 3.2.7. Wie kann ich in einem String oder StringBuffer mehrere Zeichen suchen und ersetzen? Autor: Martin Erren ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Mit String String#replace(char,char) kann ich nur einzelne Zeichen suchen und mit StringBuffer replace(int,int,String) nur ersetzen aber nicht suchen. Lösung: Eine gute Hausaufgabe. Was man hier braucht, ist eine Kombination von int String#indexOf(String,int), String String#substring(int,int) mit einem neu aufzubauenden StringBuffer. bzw. int StringBuffer#indexOf(String,int) StringBuffer#replace(int,int,String) Die folgende Lösung ist eine unter vielen und ersetzt alle Vorkommen von "search" in "source" mit "replace": public static String replaceAll(String source, String search, String replace) { if(search.equals(replace)) { return source; //kann ja sein, dass wir nichts tun müssen } StringBuffer result = new StringBuffer(); int len = search.length(); if(len == 0) { return source; //verhindert Endlosschleife bei search.equals(""); } int pos = 0; //position int nPos; //next position do { nPos = source.indexOf(search, pos); if(nPos != -1) { //gefunden result.append(source.substring(pos, nPos)); result.append(replace); pos = nPos+len; } else { //nicht gefunden result.append(source.substring(pos)); //letzter abschnitt } } while(nPos!= -1); return result.toString(); } Da sowas praktisch überall gebraucht wird, gibt es unzählige Bibliothek(chen) im Netz, die so etwas anbieten, z.B. <URL:http://ostermiller.org/utils/StringHelper.java.html> BTW: Der Link muss as den 3 Zeilen zusammen gestzt werden! [...vielleicht noch mehr und geeignetere Adressen...] 3.2.8. Gibt es reguläre Ausdrücke in Java (regular expressions)? Autor: Karsten Schulz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Ja, natürlich. Bis zum J2SDK 1.4 muss man, um reguläre Ausdrücke in Java zu benutzen, auf externe packages zurückgreifen. Eines der ausgereiftesten ist das Regexp-Paket aus dem Jakarta-Projekt: http://jakarta.apache.org/regexp/index.html Ab dem J2SDK 1.4 wird es jedoch ein package java.util.regex geben, das somit die Möglichkeit zur Benutzung der regulären Ausdrücke direkt in die Klassenhierarchie einbaut. Nachfolgend ein Beispiel für die Nutzung dieses packages (Achtung: funktioniert nur ab J2SDK 1.4 aufwärts!). Das Beispielprogramm zeigt, wie in der Stringvariablen 'input' nach einem Muster (Pattern) gesucht wird, das auf einer Ziffer, mindestens einem Buchstaben und einer weiteren Ziffer besteht: import java.util.regex.Matcher; import java.util.regex.Pattern; public class PatternTest { public static void main(String[] args) { String input = "Test für Regex Ausdrücke 1xxx2 n444n."; Pattern p = Pattern.compile("\\d\\D+\\d"); // Muster: Ziffer, mind. ein Buchstabe, Ziffer Matcher m = p.matcher(input); if (m.find()) { System.out.println("Muster an Pos. " + m.start()); System.out.println("Muster ist: " + m.group()); } else { System.out.println("Muster nicht gefunden"); } } } Die Ausgabe dieses Programms ist: Muster an Pos. 25 Muster ist: 1xxx2 Weitere Infos in der API-Dokumentation zu java.util.regex. 3.3. [IO] - Frage bezüglich Eingabe/Ausgabe, Streams, etc. in Java. ------------------------------------------------------------------- 3.3.1. Verlangsamt Serialisierung mein Programm? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Java bietet mit dem Mechanismus der Serialisierung auf einfache Weise die Möglichkeit, den aktuellen Zustand des Objekts auf einem Datenträger, das Netz oder sonst wohin über einen Stream zu sichern. Der verwendete Mechanismus ist relativ kompliziert, damit auch alle auftretenden Möglichkeiten korrekt behandelt werden können. Sind die Daten der Objekte sehr groß, so empfiehlt es sich, eigene Prozeduren zur Speicherung zu entwickeln, die dann nicht mehr allgemeingültig, aber auf die aktuelle Verwendung angepaßt und damit schneller sind. 3.3.2. Wie kann ich rekursiv einen Verzeichnisbaum abarbeiten? Autor: Marco Schmidt ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In java.io.File befinden sich die dazu benötigte Funktionalität. Mit list() läßt man sich ein Array mit allen Dateinamen geben, mit isDirectory() läßt sich prüfen, ob es sich bei dem File-Objekt um ein Verzeichnis handelt. Indem man nun rekursiv in Unterverzeichnisse absteigt, kann man so einen kompletten Verzeichnisbaum abarbeiten. Hier ein Beispielprogramm (scantree.java): import java.io.File; public class scantree { public static void main(String[] args) { // Programm muss einen Verzeichnisnamen als Parameter haben File dir = new File(args[0]); scan(dir); } public static void scan(File dir) { // Liste aller Dateien und Unterverzeichnisse holen String[] entries = dir.list(); if (entries == null || entries.length < 1) { return; } for (int i = 0; i < entries.length; i++) { File entry = new File(dir, entries[i]); if (entry.isDirectory()) { scan(entry); // rekursiv ins Unterverzeichnis verzweigen } else { // entry ist eine Datei System.out.println(entry); } } } } 3.3.3. Wie kann ich aus Dateien zeilenweise lesen? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Generell nutzt man für die Datei-Operationen einen Buffered Reader oder eine Klasse, die von dieser abstammt. Diese Klasse besitzt dann die Methode readLine(), die eine Zeile aus der Datei ausliest und diese Zeile als einen String zurückgibt. Zu beachten ist, daß bei Erreichen des Dateiendes kein leerer String, sondern eine Nullreferenz zurückgegeben wird. Ein Programm, daß aus einer Datei zeilenweise liest, sieht in Java wie folgt aus: import java.io.*; public class ZeilenWeiseLesen { public static void main(String[] args) { try { String zeile; //Wir lesen aus "eingabe.txt". File eingabeDatei = new File("eingabe.txt"); FileReader eingabeStrom = new FileReader(eingabeDatei); BufferedReader eingabe = new BufferedReader(eingabeStrom); while ((zeile = eingabe.readLine()) != null) { System.out.println(zeile); } } catch (IOException e) { e.printStackTrace(); } } } 3.3.4. Wie kann ich Exponentialzahlen (z.B. 1.09E+008) aus einer Datei lesen? Autor: Wolfram Rühaak ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Mit Java führt folgendes Verfahren zum Ziel: Lies die Daten als Strings ein und wandel sie mittels Double.valueOf(my_string).doubleValue() in einen double Wert um. Das StreamTokenizer keine Exponentialzahlen kennt hat von Sun Bug Status bekommen (obgleich der StreamTokenizer so arbeitet wie angegeben): * BugId: #4079180 Synopsis: java.io.StreamTokenizer: Add ability to read full Java floating-point syntax Der Sun Workaround soll wohl ein: public void parseExponentialNumbers(boolean flag) sein. Beispiel: import java.io.*; import java.util.Vector; /** * @author Wolfram Rühaak * Beispielcode, der zeigt wie man Zahlen aus einer Datei liest. * Die Datei darf in diesem Fall nur Zahlen enthalten, getrennt durch * Tabulator oder Space. * Die Anzahl von Spalten/Zeilen ist nicht vorgegeben. * Um *.cvs Kompatibilät zu erhalten kann man als whitespaceChars * noch ',' und ';' hinzufügen. */ public class ReadExponential { int ni; // Anzahl Zeilen int nj; // Anzahl Spalten private ReadExponential(String Filename) { int i=0; int j=0; Vector v2 = new Vector(); try { FileInputStream fis = new FileInputStream(Filename); BufferedReader r = new BufferedReader(new InputStreamReader(fis)); StreamTokenizer st = new StreamTokenizer(r); /* * Nachfolgendes Code-Fragment von: * Mark Gritter (mgritter@pup16.stanford.edu) * Betrifft:Re: This is easy in C++ (asking for help) * Newsgroups:comp.lang.java.programmer * Datum:1997/11/07 */ st.resetSyntax(); st.whitespaceChars(' ', ' '); st.whitespaceChars('\n', '\n'); st.wordChars('0','9'); st.wordChars('e','e'); st.wordChars('E','E'); st.wordChars('.','.'); st.wordChars('+','+'); st.wordChars('-','-'); /* Ende Code-Fragment */ st.eolIsSignificant(true); try { while(st.nextToken() != st.TT_EOF) { String s1 = st.sval; if(s1!=null) { // Wert in Vector schreiben v2.addElement(s1); // Anzahl von Zeilen i=st.lineno(); } } } catch(IOException ioe) {} } catch (FileNotFoundException fnfe) {} ni = i - 1; nj = ((v2.size())/(ni+1))-1; // Anzahl von Spalten berechnen double[][] dAllValues = new double [ni+1][nj+1]; int k=0; for(i = 0;i <= ni; i++) { for(j = 0;j <= nj; j++) { Object f = v2.elementAt(k); // Vector in String s = f.toString(); // Array umspeichern /* * Double.valueOf(String s) kann jeden numerischen Wert * von String nach double umwandeln */ dAllValues[i][j] = Double.valueOf(s).doubleValue(); k++; } } } } 3.3.5. Wie kann ich mit Java Dateien kopieren? Autor: Martin Erren, Uwe Günther, Ulf Jährig, Christian Kaufhold ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Bestimmt die performanteste Lösung wäre, mit System.exec(...) die Aufgabe dem Betriebssystem zu übergeben. Der Nachteil ist hier, dass so schwer ein plattformunabhängiger Code erreichbar ist und die Fehlerbehandlung schwierig wird. Deshalb einfach eine Datei lesen und in eine andere, neue Datei schreiben, z.B. so: import java.io.*; public static void copyFile(File src, File dest, int bufSize, boolean force) throws IOException { if(dest.exists()) { if(force) { dest.delete(); } else { throw new IOException( "Cannot overwrite existing file: " + destName); } } byte[] buffer = new byte[bufSize]; int read = 0; InputStream in = null; OutputStream out = null; try { in = new FileInputStream(src); out = new FileOutputStream(dest); while(true) { read = in.read(buffer); if (read == -1) { //-1 bedeutet EOF break; } out.write(buffer, 0, read); } } finally { // Sicherstellen, dass die Streams auch // bei einem throw geschlossen werden. // Falls in null ist, ist out auch null! if (in != null) { //Falls tatsächlich in.close() und out.close() //Exceptions werfen, die jenige von 'out' geworfen wird. try { in.close(); } finally { if (out != null) { out.close(); } } } } } Das schwierigste ist hier wohl die Fehlerbehandlung, die je nach Anforderung unterschiedlich sein kann. Anmerkung: Ein paar exotische VMs optimieren read(byte[]) bzw. write(byte[]) nicht, so dass hier ein BufferedInputStream oder BufferedOutputStream evtl. angebracht ist. 3.3.6. Warum kann ich meine Bilder nicht mehr laden, nachdem ich sie in ein Jar gesteckt habe? Autor: Martin Erren, Paul Ebermann ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Auf Ressourcen in einer Jar-Datei kann man nicht mit File und FileInputStream zugreifen. Stattdessen gibt es einen allgemeineren Mechanismus, der Daten von dort holt, wo auch die Klassen hergeholt werden: URL Class#getResource(String) und InputStream Class#getResourceAsStream(String) sind Deine Freunde. Laut API-doc ist "/img.jpg" relativ zur codeBase, also dem aktuellen Eintrag im CLASSPATH, wo Deine Klasse geladen wurde, und "img.jpg" relativ zur Klasse, also im selben Verzeichnis, wo auch die Klasse liegt. Ob Applikation oder Applet, *.jar oder file-system spielt hier keine Rolle. mit System.out.println(MyClass.class.getResource("/").toString()); kannst Du jederzeit feststellen, welche codebase die jeweilige Klasse hat. Weiterhin sind InputStream ClassLoader.getSystemResourceAsStream(name); und URL ClassLoader.getSystemResource(name) für eigene Ressourcen nicht zu empfehlen, da man nicht immer seine eigene Klasse vom SystemClassLoader lädt (und nur dann wären die Ressourcen an der gesuchten Stelle). 3.4. [NET] - Frage bezüglich Netzwerk. -------------------------------------- 3.4.1. Wie kann ich einen Ping in Java realisieren? Autor: Stephan Menzel ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Eigentlich gar nicht. Ein Ping (ICMP) ist eine hardwarenahe Angelegenheit, die im Gegensatz zu dem hardwarefernen abstrahierenden Konzept von Java steht. Die Antworten einer Netzwerkkarte sind da nicht so sehr relevant, wie ein im Netzwerk vorhandener Dienst. So kann zum Beispiel ein Rechner auf Pings nicht antworten und trotzdem einen Dienst anbieten. Oder die Pings verenden an einer Firewall oder aber der Rechner auf dem das Programm laeuft, ist gar nicht in der Lage, zu pingen. Das bedeutet, es ist sicherer und besser, einfach eine Testweise Socketverbindung zu der betreffenden Zieladresse aufzubauen und eine evtl. auftretende Exception als Zeichen fuer dessen Abwesenheit im Netz zu deuten. Folgendes Beispiel soll dies zeigen: import java.io.*; import java.net.*; .. static Socket nntpsock; // Der Socket fuer die Newsverbindung static BufferedReader in; static OutputStreamWriter out; ... try { nntpsock = new Socket("news.cis.dfn.de", 119); // Verbinden nntpsock.setSoTimeout(300); // Timeout auf 300ms in = new BufferedReader( new InputStreamReader(nntpsock.getInputStream())); out = new OutputStreamWriter(nntpsock.getOutputStream()); } catch (UnknownHostException e) { System.err.println("Unknown Host.:" + e.toString()); } catch (IOException e) { System.err.println("Rechner nicht erreichbar. :" + e.toString()); } ... 3.5. [AWT] - Frage bezüglich AWT. --------------------------------- 3.5.1 Wenn ich einen Listener bei mehreren Buttons anmelde, wie kann ich dann unterscheiden, welcher gedrückt wurde? Autor: Michael Paap, Christian Kaufhold, Georg Lippitsch ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Wenn Du so vorgehst wird, egal welcher Button geclickt wird, die Methode actionPerformed(ActionEvent ev) des Listeners aufgerufen. Mit ev.getSource() bekommst Du nun eine Referenz auf die Ereignisquelle (als Object). Dann kannst Du schauen, welcher Button geclickt wurde, z.B. indem Du diese Ereignisquelle mit den vorhandenen Buttons vergleichst oder indem Du auf Button castest und ihre Beschriftung ausliest. Beispiel: Angenommen, Du hast einen ActionListener bei jedem Button eines Arrays von 4 Buttons registriert. Dann könnte die Methode actionPerformed im Listener wie folgt aussehen: public void actionPerformed(ActionEvent ev) { int pressed = -1; for (int i=0; i<myButtons.length; i++) { if (myButtons[i] == ev.getSource()) { pressed = i; break; } } // hier abhängig von pressed verschiedene Aktionen // ausführen } oder so: public void actionPerformed(ActionEvent ev) { Button pressed = (Button) ev.getSource(); System.out.println("Pressed: " + pressed.getLabel()); } Eine weitere Möglichkeit bietet die Methode Button#setActionCommand(String command) Hiermit kann ein dem Knopf ein beliebiger String zugewiesen. Dieser kann mit ActionEvent#getActionCommand() wieder abgefragt werden. Dabei wird immer der String zurückgegeben, der dem Event auslösenden Knopf zugewiesen wurde. Ein Beispiel: Button knopf1 = new Button("Max"); knopf1.setActionKommand("schlimmer bub 1"); Button knopf2 = new Button("Moritz"); knopf2.setActionKommand("schlimmer bub 2"); public void actionPerformed(ActionEvent ev) { String c = ev.getActionCommand(); if (c.equals("schlimmer bub 2")) System.out.println("Moritz wurde aktiviert"); } 3.5.2. Kann ich ein Fenster (Frame/JFrame) maximieren? Autor: Karsten Schulz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Antwort: Jein. Bis zum J2SDK 1.3 (einschließlich) gibt es keine Möglichkeit, ein Fenster zu maximieren. Es gibt nur verschiedene Workarounds, um ein Fenster möglichst flächendeckend auf dem Desktop darzustellen (s.u.) Ab dem J2SDK1.4 gibt es die Methode setExtendedState(int state) aus der Klasse java.awt.Frame, mit der das Fenster *vielleicht* maximiert wird. Ob das möglich ist, kann mittels isFrameStateSupported(int state) aus dem Toolkit ermittelt werden, denn nicht jede Plattform unterstützt solche maximierten Fenster. Für Anwendungen, die mittels einer J2SDK-Version bis 1.3 erstellt werden sollen, gibt es nur Workarounds, da es eine "Maximieren"-Funktion nicht gibt. Für den Windows-Benutzer mag das merkwürdig sein, für den Unix-Benutzer ist der Gedanke merkwürdig, dass die tatsächliche Bildschirmgrösse etwas mit der Desktopgröße zu tun haben sollte. Lange Rede, kurzer Code: um ein Fenster möglichst groß zu machen, nehme man setSize(getToolkit().getScreenSize()); Hierdurch wird das darzustellende Fenster vielleicht auf die Größe der aktuellen Bildschirmauflösung gesetzt. Ohne Berücksichtigung evtl. darzustellender Menü- und/oder Taskleisten. Falls auf der Laufzeitplattform ein öffnendes Programmfenster von einem Fenstermanager platziert wird, hat die setSize(Dimension)-Methode möglicherweise keine Auswirkung auf das Fenster. Der Programmierer sollte sich also nicht darauf verlassen, dass sein Fenster so dargestellt wird, wie er es programmiert hat! Eine weitere Möglichkeit, ein Fenster evtl. zu maximieren besteht darin, die Klasse java.awt.Robot (seit J2SDK 1.3) zu benutzen, um die Maximieren-Schaltfläche programmgesteuert anklicken zu lassen. Hinweise zur Benutzung dieser Klasse finden sich im JDC Tech Tip vom 11. Juli 2000 <URL:http://developer.java.sun.com/developer/TechTips/2000/tt0711.html) Wegen dieser ganzen Unwägbarkeiten ist es oft eine Alternative, statt das Fenster zu maximieren, es einfach Wiederherzustellen und die Größe und Position des Fensters von einem vorherigen Programmlauf zu benutzen. Hierbei sollte jedoch darauf geachtet werden, dass die Geometrie eines maximierten Fensters *nicht* durch ein normales Fenster nachgebildet wird, weil der Anwender sonst dadurch verwirrt wird, dass das Fenster maximiert aussieht, es aber in Wirklichkeit nicht ist! 3.5.3. Wie tausche ich die Kaffeetasse im blauen Balken aus? Autor: Karsten Schulz, Roger Schuster, Christian Wederhake ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das geht mit java.awt.Frame#setIconImage(java.awt.Image). Beispiel: ImageIcon icon = new ImageIcon("meinBildchen.gif"); setIconImage(icon.getImage()); 3.6. [SWING] - Frage bezüglich Swing. ------------------------------------- 3.6.1. Wie mache ich globale Font-Änderung für meine Komponenten? Autor: Linda Radecke ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das geht mit: * UIManager.put("component.font", new FontUIResource(...)); 3.6.2. Wie kann ich bei der Eingabe in ein JTextField die Anzahl der eingebbaren Zeichen beschränken? Autor: Alexander Elsholz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Wenn ein JTextField benutzt wird sollte man ein benutzerdefiniertes Document implementieren. Für AWT-Textfelder funktioniert es mit einigen Anpassungen auch über das Interface KeyListener. import javax.swing.text.PlainDocument; import javax.swing.text.BadLocationException; import javax.swing.text.AttributeSet; /** * Diese Klasse ist ein Dokument für Textfelder, welches die Eingabe auf * x Zeichen begrenzt. * * Die Zuweisung geschieht über * JTextfield.setDocument(new Validation(int anzahl)); */ public class Validation extends PlainDocument{ private int limit; /** * Konstruktor für das Validationdokument * @param int limit: maximale Anzahl der einzugebenen Zeichen */ public Validation(int newLimit){ super(); if (limit < 0){ limit = 0; } else { limit = newLimit; } } /** * Funktion überschreibt die Methode insertString von PlainDocument * @param int offset: Position * @param String str: der String * @param AttributeSet attr: Attributset */ public void insertString (int offset, String str, AttributeSet attr) throws BadLocationException { if (str == null) return; if ((getLength() + str.length()) <= limit){ super.insertString(offset, str, attr); } } } Die hier aufgezeigte Lösung zur Begrenzung von Textfeldern kann auch verwendet werden, wenn unerwünschte Zeichen in einem Textfeld nicht eingegeben werden dürfen. 3.6.3. Wie setze ich den Cursor an den Anfang der JTextArea? Autor: Linda Radecke ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das geht mit: * textarea.setCaretPosition(0); 3.6.4. Wie scrolle ich an das Ende der JTextArea? Autor: Linda Radecke ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Hierzu existieren mehrere Möglichkeiten: * textarea.setCaretPosition(textarea.getDocument().getLength()); * try{ textarea.scrollRectToVisible(textarea.modelToView( textarea.getDocument().getLength())); } catch (BadLocationException be) { be.toString(); } * (Geht auch, weniger schön): mit getText().length(); Scrolling wrappen in SwingUtilities.invokeLater(), bspw: SwingUtilities.invokeLater(new Runnable() { public void run() { // scrolling code } }; 3.6.5. Wie bekomme ich es hin, das der Benutzer in meiner JTable keine Eingaben tätigen kann? Autor: Alexander Elsholz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das funktioniert über das Tabellenmodell deiner JTable: import javax.swing.table.DefaultTableModel /** * Diese Klasse repräsentiert das Datenmodell für eine oder mehrere * Tabellen * * Die Zuweisung geschieht über JTable.setModel(new YourTableModel())); */ public class YourTableModel extends DefaultTableModel { /** * aus der API: Returns true if the cell at rowIndex and columnIndex * is editable. Otherwise, setValueAt on the cell will not change the * value of that cell. */ public boolean isCellEditable(int row, int column) { return false; } } Dieses Bespiel verhindert das Editieren von zellen in allen Zellen der Tabelle, durch Auswerten der Parameter könn aber auch einzelne Spalte, Zeilen oder Zellen gesperrt werden. 3.6.6. Wie bekomme ich eine horizontale ScrollBar bei JTable? Autor: Linda Radecke ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das geht mit: * table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 3.6.7. Wie scrolle ich ans Ende von JTable? Autor: Linda Radecke ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das geht mit: * table.scrollRectToVisible(table.getCellRect( table.getRowCount()-1,-1,true)); 3.6.8. Wie verhindere ich ein reordering der Spalten bei JTable? Autor: Linda Radecke ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das geht mit: * table.getTableHeader().setReorderingAllowed(false); 3.6.9. Wie verhindere ich ein Resizen der Spalten bei JTable? Autor: Linda Radecke ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das geht mit: * table.getTableHeader().setResizingAllowed(false); 3.6.10. Wie ändere ich die Hintergrundfarbe von JScrollPane? Autor: Linda Radecke ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das geht mit: * scrollpane.getViewport().setBackground(new Color(....)); 3.6.11. Wie kann ich ein JLabel dazu bringen, seinen Hintergrund zu füllen? Autor: Gerhard Bloch ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JLabel zeichnet (wie die meisten Swing-Komponenten) seinen Hintergrund nicht, wenn es nicht "opaque" ist (obwohl dies unterschiedliche Dinge sind). Loesung: Fuer *undurchsichtige* Hintergrundfarben funktioniert folgendes: JLabel l = new JLabel("Text"); l.setOpaque(true); l.setBackground(Color.red); Diese Loesung funktioniert auch fuer transparente Hintergrundfarben: public class JLabelWithBackground extends JLabel { public JLabelWithBackground(String title) { super(title); } public void paintComponent(Graphics g) { if (!isOpaque()) { Rectangle vr = getVisibleRect(); g.setColor(getBackground()); g.fillRect(vr.x, vr.y, vr.width, vr.height); } super.paintComponent(g); } } JLabel l = new JLabelWithBackground("Text"); l.setBackground(new Color(255, 0, 0, 128)); 3.6.12. Warum reagiert meine GUI nicht, während eine längere Berechnung ausgeführt wird? Autor: Tobias Vogele ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Im Prinzip funktioniert das folgendermaßen: Es gibt bei Swing einen besonderen Thread: den Event Dispatch Thread (EDT). Wenn immer Du ein Event von Deiner GUI bekommst, z.B. ein ActionEvent, dann befindest Du Dich im Event Dispatch Thread (EDT). In diesem werden aber nicht nur Events, die der Benutzer z.B. durch Mausklicks verursacht hat, erzeugt, sondern in diesem Thread wird auch die Anforderung für das Neuzeichnen verarbeitet Das Problem ist nun: Wenn Du im EDT (z.B. bei der Behandlung eines GUI-Events) eine längere Berechnung oder was auch immer ausführst, dann kann in diesem Thread natürlich so lange nichts anderes passieren, also kann sich auch die GUI auch nicht mehr neu zeichnen, obwohl es vielleicht nötig wäre. Das bedeutet auch, daß Änderungen, die während der Berechnung an der GUI vorgenommen werden, nicht sichtbar sind, bevor die Berechnung nicht beendet ist, da erst dann die GUI neu gezeichnet werden kann. Die Lösung ist daher: Du mußt Deine Berechnung in einem anderen Thread ausführen. Das weitere Problem dabei: Wenn diese Berechnung Änderungen an der GUI veranlaßt, dann müssen diese wieder im EDT ausgeführt werden, da Swing im allgemeinen nicht thread-safe ist. Wie geht beides: Einen neuen Thread starten: new Thread(calculation, "CalculationThread").start(); Etwas im EDT ausführen: SwingUtilites.invokeLater(guiChanges); Weitere Informationen dazu und alternative Lösungsansätze: 1) Artikel-Serie "Threads and Swing" http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html 2) Foxtrott, ein Rahmenwerk um anders zu schreiben, was der Swing-Worker aus 1) kann: http://foxtrot.sourceforge.net/ 3) Spin, eine weitere Threading-Bibliothek für Swing http://spin.sourceforge.net/ Hier ein konkretes, lauffähiges Beispiel: Während einer längeren Berechnung soll eine ProgressBar den Fortschritt anzeigen. import java.awt.FlowLayout; import java.awt.event.*; import javax.swing.*; public class Beispiel implements ActionListener{ JProgressBar progressBar = new JProgressBar(0, 10); public void actionPerformed(ActionEvent e) { Runnable calculation = new Runnable() { public void run() { bigCalculation(); } }; // Neuen Thread starten: new Thread(calculation, "CalculationThread").start(); } /** * Hier ist die große Berechnung, die gleichzeitig die * ProgressBar updaten soll. */ private void bigCalculation() { for (int i = 0; i <= 10; i++) { calculateNextStep(); setProgress(i); } } void calculateNextStep() { try { Thread.sleep(500); } catch (InterruptedException e) { } } void setProgress(final int newProgress) { // Wert der ProgressBar im EDT ändern: SwingUtilities.invokeLater(new Runnable() { public void run() { progressBar.setValue(newProgress); } }); } public static void main(String[] args) { Beispiel bsp = new Beispiel(); JButton button = new JButton("Rechne..."); button.addActionListener(bsp); JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().setLayout(new FlowLayout()); frame.getContentPane().add(button); frame.getContentPane().add(bsp.progressBar); frame.pack(); frame.setVisible(true); } } Es sei hier auch noch auf das Tutorial von Sun zur Verwendung von JProgessBars hingewiesen, da dort anders vorgegangen wird als hier: http://java.sun.com/docs/books/tutorial/uiswing/components/progress.html Dort wird die JProgressBar über einen Timer angesteuert und nicht über einen expliziten Aufruf von setProgress. Allerdings war es ja hier gerade die Absicht, zu zeigen, wie man aus einem extra Thread heraus GUI-Methoden aufruft. 3.7. [APPLET] - Frage zu Java-Applets und ihre Zusammenarbeit mit Browsern. -------------------------------------------------------------- 3.7.1. Welche JDK-Version sollte ich für Applets verwenden, die möglichst allgemein lauffähig sein sollen? Autor: Stefan Menzel ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Am besten nicht über 1.1.8. 3.7.2. Wie bekomme ich den Internet Explorer dazu, das Plugin anstelle der integrierten JVM zu benutzen. Autor: Stefan Menzel, Aljoscha Rittner, Joachim Sauer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Indem auf der aufrufenden Webseite nicht das <APPLET>, sondern das <OBJECT>-Tag verwendet wird. Es gibt auf <URL:http://java.sun.com/products/plugin/1.3/docs/html_converter.html> einen automatischen Konverter einer Seite zur Benutzung des Plugins. Mit 1.4 kann auch das APPLET-Tag zusammen mit dem Plugin verwendet werden. Dazu findet man nähere Informationenen unter: <URL:http://java.sun.com/j2se/1.4/docs/guide/plugin/developer_guide/ html_converter_more.html> Anmerkung: Mit 1.4 kann auch das APPLET-Tag zusammen mit dem Plugin verwendet werden. Aber alle, die für JDKs größer 1.2 coden sollten ohnehin OBJECT verwenden, da sie mit APPLET (auch mit den neuen Plugins) auf Browser stoßen können, die zwar glauben es darstellen zu können, dann aber kläglich scheitern. Wer für 1.1 programmiert sollte sich wohl wirklich noch das APPLET-Tag überlegen. 3.7.3. Was dürfen unsignierte Applets nicht aus Sicherheitsgründen? Autor: Martin Erren ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Applets wurden generell stark in ihren Rechten eingeschränkt. Die genauen Einschränkungen sind von der Virtuellen Maschine, also Browser / Plugin oder Appletviewer abhängig. Einem signierten Applet kann man explizit mehr Rechte geben als das normalerweise der Fall ist. Auch gibt es Unterschiede, ob das Applet von remote oder vom Filesystem geladen wurde. Ein von remote geladenes Applet darf in der Regel nicht: * auf das lokale Filesystem zugreifen * auf die (System-)Zwischenablage zugreifen * Socketverbindungen zum lokalen Host aufbauen. * Socketverbindungen zu dritt-Hosts aufbauen. * System-properties ändern/setzen * System-properties lesen ausser: java.version, java.vendor,java.vendor.url, java.class.version os.name, os.arch, os.version, file.separator, path.separator, line.separator * lokal Programme starten * lokale Bibliotheken laden * System.exit(int) aufrufen * Fenster ohne Warnung öffnen Signieren heisst, das Applet mit einer verschlüsselten Unterschrift (Signatur) zu versehen, um diesen mehr Rechte zuzuweisen. Signieren bedarf einer "vertrauenswürdigen" Person bzw. Institution, unterschiedlich, je ob das Applet nur in einem Intra- oder im gesamten Internet betrieben werden soll. Der Prozess des Signieren-Lassens wurde leider noch zusätzlich dadurch erschwert, dass man jede Signatur praktisch für jede Browser-VM extra durchführen mus. Genaueres siehe: <URL:http://java.sun.com/sfaq> (Dort auch böse Testapplets.) Zur Java-Security und Codesignierung allgemein siehe: <URL:http://www.securingjava.com/toc.html> <URL:http://home.iSTAR.ca/~neutron/java.html> und spezieller: <URL:http://www.abim.net/jsw/index.htm> <URL:http://www.iw.uni-hannover.de/~ruemper/> <URL:http://www.suitable.com/Doc_CodeSigning.shtml> <URL:http://developer.netscape.com/docs/manuals/signedobj/signtool/ index.htm> (umgebrochen) <URL:http://java.sun.com/products/plugin/1.2/docs/nsobjsigning.html> <URL:http://developer.java.sun.com/developer/onlineTraining/ Programming/JDCBook/signed.html> (umgebrochen) 3.8. [SERVER] - Frage zu Servlets und anderen Server- Implementierungen in Java. ----------------------------------------------------- 3.9. [NONCORE] - Fragen zu Klassen/Packages, die über den Kern der Sprache hinausgehen, also Java3D etc. ------------------------------------------------------------------ 3.10. [OOP] - Frage bezüglich OOP-Konzepten und Patterns in Java. ---------------------------------------------------------------- 3.10.1. Was bedeutet Vererbung im OO-Kontext? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Vererbung ist in Java, und natürlich auch in anderen Programmiersprachen, nur eine Möglichkeit, um das konkrete Problem in den Computer zu übertragen. Das Prinzip der Vererbung ist dann anwendbar, wenn zwei Objekte in einer ist-ein oder ist eine Art von Beziehung zueinander stehen. Eine abgeleitete Klasse ist ein Subtyp der zugehörigen Oberklasse, besitzt also deren Eigenschaften (Daten & Methoden) und erweitert diese bei Bedarf um neue. LKW und PKW sind zum Beispiel Subtypen der Oberklasse Automobil, wobei beim LKW zum Beispiel die maximal zulässige Anhängerlast oder die Achsenanzahl hinzukommt. Wendet man das Prinzip der Vererbung an, so gilt das sogenannte Liskov'sche Substitutionsprinzip: Objekte der abgeleiteten Klasse können stets an die Stelle von Objekten der Oberklasse treten. 3.10.2. Was bedeutet Aggregation im OO-Kontext? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das Prinzip der Aggregation besagt, daß ein Objekt aus mehreren Teilen (=Objekten) besteht, die wiederrum aus Teilen bestehen können usw. Die Klasse Computer könnte z.B. aus den Klassen Speicher, Festplatte etc. bestehen. In einem Pseudo-Java-Code etwa so formuliert: public class Computer { private Speicher rom; private Speicher ram; private Speicher festPlatte; (...) } 3.10.3. Was bedeutet Assoziation im OO-Kontext? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Mit der Assoziation wird die Verbindung des Objektes zu einem oder mehreren anderen Objekten beschrieben. Assoziationen können kurzfristig sein, zum Beispiel dann, wenn ein anderes Objekt an das aktuelle Objekt als Parameter der Objektmethode übergeben wird. Sie können aber auch langfristig sein, wenn das Objekt Referenzen auf die assoziierten Objekte speichert (Registrierung). 3.10.4. Was bedeutet Benutzung im OO-Kontext? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das aktuelle Objekt benutzt eines oder mehrere andere Objekte um die anstehende Aufgabe erfüllen zu können. Das Objekt Backofen benutzt zum Beispiel das Objekt Thermostat, um die Aufgabe backen zu erfüllen, damit der Inhalt des Backofens nicht verkohlt. Es muß angemerkt werden, daß man häufig aus dem Programmcode keine direkte Entscheidung treffen kann, ob Aggregation, Assoziation oder Benutzung vorliegt. Diese drei Beziehungen sind prinzipiell Designprinzipien, die in der späteren Implementierungsphase, zum Beispiel in Java, in relativ ähnliche oder sogar identische Konstrukte umgesetzt werden. Es ist deshalb wichtig, daß man das jeweils zugrunde gelegte Prinzip dokumentiert, damit die Funktionsweise besser und einfacher nachvollzogen werden kann. 3.10.5. Worin liegen die Unterschiede zwischen abstrakten Klassen und Interfaces? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Java fügt dem schon von C++ her bekannten Konzept der abstrakten Klasse noch ein weiteres, aus der Sprache Objective-C entliehenes Feature hinzu: Interfaces. Ein Interface ist zuersteinmal nichts anderes als eine Auflistung von Methoden. Eine Klasse implementiert ein Interface, wenn sie alle in der Interface-Deklaration angegebenen Methoden besitzt. Wird mindestens eine der Methoden des Interfaces nicht implementiert, so wird die Klasse zu einer abstrakten Klasse. Mit dem Interface-Prinzip lassen sich in Java einfach Benutzt-Beziehungen modellieren. Eine Klasse benutzt eine andere Klasse in dem Sinn, daß es ganz spezielle Methoden dieser Klasse verwendet, um seine eigene Funktionalität zu realisieren - auf Daten der Hilfsklasse wird ja wegen dem Prinzip der Datenkapselung nicht direkt zugegriffen. Um ein Objekt zu benutzen, ist es nur wichtig, daß dieses Objekt die gewünschten Funktionen auch besitzt. Man definiert sich daher ein Interface, welches die benötigten Funktionen auflistet. Alle Objekte, die diese Funktionen benötigen, sprich, die dieses Interface verlangen, können nun all die Klassen verwenden, die dieses Interface implementieren. Im Sinne eines guten Klassendesigns ist es daher wichtig, solche Benutzt-Beziehungen zu lokalisieren, um die Klassen flexibler zu machen und unnötigerweise angewendete Vererbung zu eliminieren. Abstrakte Klassen sind im Gegensatz dazu Modellierungen des Ist ein-Prinzips und unterscheiden sich in dieser Hinsicht von Interfaces. 3.10.6. Was ist eine anonyme innere Klasse? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Eine anonyme innere Klasse trägt keinen Namen und wird vor allem bei der GUI-Programmierung für Adapterklassen verwendet. Wird eine solche Klasse in ein Class-File übersetzt, so werden alle anonymen Klassen, die in der umgebenden Klasse definiert wurden, von Null beginnend durhnummeriert. Testklasse$0.class ist also das Class-File der ersten in der Klasse Testklasse auftauchenden anonymen inneren Klasse. 3.10.7. Was ist ein immutable Objekt? Autor: Gerhard Bloch ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Ein immutable (unveraenderliches) Objekt ist ein Objekt, das nach seiner Instanzierung nicht mehr veraendert werden kann. Bekannte Beispiele hierfuer sind saemtliche Wrapper-Klassen (Integer, Boolean,... ) sowie die Klasse String. Dieses Design-Pattern bietet folgende Vorteile: + Instanzen koennen gefahrlos mehrfach referenziert werden + keine Synchronisation noetig + sehr gut geeignet als Schluessel fuer HashMap Immutable Objekte muessen folgende Forderungen erfuellen: + alle Attribute sind final + alle nicht selbst immutable Attribute sind zusaetzlich private + kein Schreibzugriff auf Attribute moeglich + direkter Lesezugriff ist nur auf selbst immutable Attribute erlaubt + im Konstruktor uebergebene mutable Objekte muessen geklont(*) werden (*) Klonen ist hier in der Bedeutung "tief genug kopieren" gemeint: Es muessen rekursiv alle mutable Attribute kopiert werden. Dies hoert sich komplizierter an, als es in der Praxis ist, da die verwendeten Objekte i.d.R. nicht verschachtelt sind, reicht meist eine flache Kopie aus. Beispiel: public class MyImmutable { public final int i; private final String s; private final int[] a; private final Point p; public MyImmutable(int i, String s, int[] a, Point p) { this.i = i; // primitiver Typ ist immutable this.s = s; // String ist immutable this.a = new int[a.length]; // Arrays sind mutable! System.arraycopy(a, 0, this.a, 0, a.length); this.p = new Point(p); // Point ist mutable! } public String getS() { return s; } // Array ist mutable, also Klon zurueckgeben public int[] getA() { return (int[]) a.clone(); } // alternativ: Elementzugriff, die int-Elemente sind immutable public int getAAt(int pos) { return a[pos]; } // Point ist mutable, also Klon zurueckgeben public Point getP() { return new Point(p); } } Zusaetzlich ist folgendes zu beachten: Subklassen von Immutables muessen selbst nicht immutable sein und koennen im schlimmsten Fall sogar das Konzept unterlaufen! Beispiel: public class AntiImmutable extends MyImmutable { public String s; public AntiImmutable(int i, String s, int[] a, Point p) { super(i, "", a, p); this.s = s; } public String getS() { return s; } } MyImmutable mi = new AntiImmutable(0, "A", anIntArray, new Point(0, 0)); ((AntiImmutable)mi).s = "B"; System.out.println(mi.getS()); --> Ergibt "B"!!! Daher ist es meist angebracht, Immutables final zu deklarieren! Zudem sollte man Attribute, die zwar immutable, aber nicht final sind, wie Mutables behandeln (also im Konstruktor bzw. bei der Rueckgabe klonen). 3.11. [JDK] - Frage zu virtuelle Maschinen, alles über JDKs und deren Installation und Verwendung. --------------------------------------------------------------------- 3.11.1. Was ist ein Java Development Kit (JDK) Autor: Hubert Partl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das JDK ist eine Software, die für die Erstellung, Übersetzung und Ausführung von Java-Programmen notwendig ist; enthält unter anderem den Java-Compiler, das Java Runtime Environment JRE und diverse Hilfsprogramme. Der Name bedeutet übersetzt Java-Entwicklungs-Werkzeug. 3.11.2. Was ist ein Java Runtime Environment (JRE) Autor: Hubert Partl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das JRE ist eine Software, die für die Ausführung von Java-Programmen notwendig ist; enthält unter anderem die Java Virtual Machine JVM und die Klassenbibliothek. Der Name bedeutet übersetzt Java-Laufzeit- Umgebung. 3.11.3. Was ist eine Java Virtual Machine (JVM) Autor: Hubert Partl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Die JVM ist eine die Software, die notwendig ist, um ein Java- Binärprogramm (Bytecode) auf einem Computer auszuführen. Der Name bedeutet virtuelle Java-Maschine und kommt daher, dass der Computer, der direkt nur Windows- oder Macintosh- oder Unix-Binärprogramme ausführen kann, mit Hilfe der JVM so wirkt, als ob er Java-Bytecode ausführen könnte, also als ob er eine Java-Maschine wäre. 3.11.4. Wie konfiguriere ich JDK1.3/1.4 unter Linux oder Unix? Autor: Martin Erren, Michael Paap ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Frage: Bei meinem Linux ist per default kaffe (jdk1.1.8) eingestellt, obwohl ich das Sun jdk installiert habe. Wie kann ich 1.3 unter Linux benutzen? Antwort: Die default-Einstellungen sind gut für jene, die nur GNU-Lizenzen akzeptieren, aber schlecht für solche, die ernsthaft in Java entwickln oder anspruchsvolle Java-Programme benutzen wollen. I. SuSE Linux SuSE macht die Umstellung auf das aktuelle Sun-jdk wenigstens einfach. Sie muss jedoch per Hand erfolgen. Ein normaler Benutzer hat /usr/lib/java/bin im $PATH. (Unter root braucht/soll Java nicht gestartet werden) /usr/lib/java ist ein symbolic link auf /usr/lib/jdk1.1.8, eine Variante vom jdk 1.1.8 namens kaffe. Man vergewissere sich dessen mit # file /usr/lib/java Falls tatsächlich ein symbolic link, kann man diesen getrost (unter root) mit # rm /usr/lib/java löschen und mit # ln -s /usr/java/jdk1.3.1_01 /usr/lib/java auf das Sun jdk neu verlinken. Weiterhin sollte man in /etc/rc.config die Variable CREATE_JAVALINK auf "no" setzen, sonst setzt SuSEConfig den Link wieder in den Urzustand, wenn es das nächste Mal läuft. Diese Angaben gelten für SuSE 7.3 und können pro Distributions-Version geringfügig von der beschriebenen Struktur abweichen. (Falls Dir symbolic links, file, rm, $PATH nichts sagen, solltest Du Dir eine kleine Einführung in UN*X besorgen, zum Beispiel [...]) II. Unix/Linux Allgemein Da andere Linux Distributionen nicht so verbreitet sind wie SuSE, können hier kaum alle Originalkonfigurationen durchgegangen werden. Bei allen UN*X Varianten kann man aber immer so analysieren: * Mit "java -version" die installierte JRE überprüfen. * Mit "type java" erkennen ob "java" ein alias, oder eine ausführbare Datei ist, bzw. wo sie im Filesystem liegt. * Mit "file .../java" erkennen, ob es sich um ein script oder eine binary handelt. Und so zu einem installierten Sun-JDK umlenken ($JAVA_HOME sei das installierte Sun JDK): * Den $PATH ändern, Eintrag auf $JAVA_HOME/bin *vor* der Original- Binary * "alias" setzen (hat immer Vorrang). * Etwaige links umbiegen wie oben beschrieben. Die Variable $JAVA_HOME sollte man ebenfalls setzen, da einige Programme so das installierte JDK finden (z.B. Tomcat). 3.11.5. Wie installiere und konfiguriere ich das jdk unter Windows 9x/Me/NT/2000 richtig? Autor: Wolfgang Schirmer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Als Referenz-jdk nehmen wir das jdk1.3.1 ! Nach dem Download des jdk 1.3.1 wird die Installationsroutine über den klick auf die jdk1_3_1-win.exe gestartet. Nach der Begrüssung und den Auszug aus den Lizenzbedingungen erfolgt die Auswahl des Installations- verzeichnisses. Standardmässig wird das Verzeichnis C:\jdk1.3.1 vorgeschlagen. Dies kann aber über Browse geändert werden. Nach der Festlegung des Installationsumfanges werden die Dateien auf die Festplatte kopiert. Durch die Eingabe des Kommandos: java -version in der DOS-Box kann festgestellt werden welche Version auf dem System installiert wurde. Für dieses Beispiel sollte folgende Nachricht als Reaktion auf das Kommando erscheinen: java version "1.3.1" Als nächstes sind die PATH-Einstellungen zu überprüfen. Hierbei ist es wichtig zu wissen, dass die PATH-Anweisung die Pfade festlegt, in denen das Betriebssystem nach Programmen sucht. Unter Windows 9x/Me wird die Pfadeinstellung in der autoexec.bat durchgeführt. Hier sollte die PATH-Zeile folgendermassen aussehen: PATH=C:\WINDOWS;C:\WINDOWS\COMMAND;C:\jdk1.3.1\bin Nach dieser Änderung in der autoexec.bat muss das System neu gestartet werden, damit die Änderungen auch wirksam werden. Unter WindowsNT/2000 ist die Pfadeinstellung in der Systemsteuerung|System|Umgebung innerhalb der Systemvariablen vorzunehmen. Hier ist der Eintrag in der Variablen PATH durch die Zuweisung: ;C:\jdk1.3.1\bin zu ergänzen. Die Änderungen werden erst in den nach der Änderung geöffneten DOS-Box wirksam. Achtung! Um unter WinNT/2000 diese Einstellungen vornehmen zu können muss man die Administrator- Berechtigung besitzen. 3.12. [TOOLS] - Frage zu einem Java-Zusatz-Tool, zum Beispiel IDEs, Build-Tools, Profiler, etc. ------------------------------------------------------------------- 3.12.1. Welche IDE muss ich verwenden? Autor: Martin Erren ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <URL:http://groups.google.com/groups?q=beste+%2B+IDE+%2B+Java ++-emacs+-vi+-vim+-notepad+group%3Ade.comp.lang.java&hl=de&btnG =Google-Suche> BTW: Der Link muss aus den 3 Zeilen zusammengesetzt werden ! Bietet eigentlich jederzeit einen guten Überblick. 3.12.2. Wie kann man eine Java-Anwendung in eine EXE-Datei umwandeln? Autor: Marco Schmidt ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Ein nativer Compiler ist in der Lage, Quelltext oder Bytecode in eine native Anwendung (statt wie üblich bei Java in Bytecode, also .class-Dateien) umzuwandeln, etwa eine EXE-Datei unter Windows. Auf <URL:http://www.geocities.com/marcoschmidt.geo/java-native-compilers.html #products> sind u. a. auch einige native Compiler und weiterführende Links zum Thema "native compilation" aufgelistet. BTW: Der Link muss aus 2 Zeilen zusammengesetzt werden ! Bei der Verwendung gibt es allerdings ein paar Punkte zu beachten. * Gute native Compiler sind recht teuer und somit nur im professionellen Umfeld sinnvoll einsetzbar. Der freie native Compiler gcj unterstützt z. B. nur Java 1.1, und auch das nur teilweise (kein AWT etc.). * Obwohl das vom nativen Compiler erzeugte Programm aus nativem Code besteht, muß oft trotzdem noch ein Java Runtime Environment installiert werden, so daß der Vorteil der einfachen Verteilung des Programms wegfällt - der Benutzer könnte genauso gut direkt das JRE installieren und die Bytecode-Version der Anwendung starten. * Der große Geschwindigkeitsvorteil durch nativen Code existiert heutzutage nicht mehr, da moderne JVMs durch Just-in-time-Compiler in den meisten Fällen sehr nah an nativen Code herankommen. In Einzelfällen mag es aber durchaus Vorteile bei nativem Code geben. * Da es native Compiler nicht für all diejenigen Plattformen gibt, für die auch JREs existieren, schränkt man die Anzahl potentieller Nutzer ein, wenn man auf nativen Code besteht. Allerdings dürften mit nativen Compilern für Windows und ein paar der verbreiteteren Unix-Varianten absolut gesehen der größte Teil aller Computerbenutzer abgedeckt sein. * Wer nativen Code verwendet, muß eventuell mehrere Versionen des Programms für verschiedene Plattformen pflegen. Durch die Verwendung von Bytecode (.class-Dateien) hat man eine Version, die überall ausgeführt werden kann ("write once, run anywhere"). Zum Schluß noch ein paar Vorteile durch die Verwendung eines nativen Compilers. * Native Programme starten meist schneller - dies ist für lang laufende Server-Anwendungen nicht so wichtig, für häufig aufgerufene Kommandozeilenprogramme allerdings schon eher. * Es ist schwerer, nativen Code als Bytecode zu dekompilieren (also wieder den Quelltext zu erhalten). Wer also Reverse-engineering seines Programms fürchtet, hat bei Bytecode mehr Anlaß zur Sorge. * Einige native Compiler ermöglichen es, daß gleichzeitig laufende Instanzen der erzeugten Programme sich gewisse Ressourcen teilen und so weniger Arbeitsspeicher verbrauchen. Aktuelle JVMs laufen stets völlig unabhängig voneinander ab (dies wird sich vielleicht nach Java 1.4 ändern). Man kann mit nativen Anwendungen also mehr Instanzen eines Programms auf demselben System laufen lassen. Native Compiler haben in einigen Nischen also durchaus Daseinsberechtigung. Allerdings sollte, wer sich um einfache Verteilung seines Programms Gedanken macht, eine der folgenden Möglichkeiten in Betracht ziehen: * Suns Java Webstart <URL:http://java.sun.com/products/javawebstart/> * Ausführbare JAR-Dateien, die sich mit einem Doppelklick auf das entsprechende Icon starten lassen <URL:http://java.sun.com/products/jdk/1.2/docs/guide/extensions/ spec.html#executable> * Einen Installer wie InstallAnywhere <URL:http://www.zerog.com/>, JExpress <URL:http://www.denova.com/> oder eines der Produkte aus dem entsprechenden Abschnitt des Open Directory: <URL:http://dmoz.org/Computers/Programming/Languages/Java/ Development_Tools/Deployment/> 3.13. [MATH] - Mathematik, Arithmetik, Gleitpunktzahlen, Funktionen. -------------------------------------------------------------------- 3.13.1. Warum rechnet Java falsch? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Problem: public class Test { public static void main(String[] args) { float zahl = 0.0F; for (int i = 0; i <= 100; i++) { System.out.println(zahl); zahl += 0.1F; } } } Läßt man obiges Programm laufen so ergibt sich folgende Ausgabe: 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.70000005 0.8000001 0.9000001 ... Scheinbar rechnet Java an einigen Stellen falsch und ist nicht fähig, zu einer Zahl den Wert 0.1 korrekt zu addieren. Dies sieht jedoch nur so aus und ist auch keineswegs typisch für die Programmiersprache Java, sondern ein allgemeines Problem. Zahlendarstellung im Computer: Um dies zu verstehen, muß man sich klar machen, daß ganze Zahlen intern in Form von Binärzahlen und Fließkommazahlen mit Hilfe von Binärbrüchen dargestellt werden. Nun kann der (nicht gerade seltene) Fall eintreten, daß sich eine Zahl im Dezimalsystem zwar darstellen läßt, in Binärdarstellung jedoch zu einem unendlichen, nicht abbrechenden, Bruch wird. Für die Speicherung einer Zahl steht aber nur ein beschränkter Speicherplatz zur Verfügung, d.h. die unendliche Binärdarstellung wird nur bis zu einer gewissen Stelle gespeichert. Das Resultat sind Ungenauigkeiten. Addiert man jetzt solche Zahlen (und 0.1 ist ein Beispiel für so eine Zahl) mehrfach auf, so addieren sich die unvermeidbaren Ungenauigkeiten immer mehr auf und führen zu dem obigen Verhalten. Dies ist keineswegs charakteristisch für Java, sondern auch in jeder anderen Sprache, die Fließkommazahlen verwendet, reproduzierbar. Eine Abmilderung des Problems besteht in einer schlauen Rundung von Zwischenergebnissen an geeigneten Stellen der Berechnung, so daß Fehler kompensiert oder zumindest abgeschwächt werden. 3.13.2. Wie runde ich eine Gleitkommazahl? Wie formatiere ich eine Gleitkommazahl? Autor: Peter Luschny; Datum: 2004-02-04 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Runden und Formatieren von Gleitkommazahlen ::: 1. Einführung :::::::::::::::::::::::::::::::::::::::::::::::::::::::: Die Dezimaldarstellung einer Zahl a notieren wir (die Schreibweise a_{i} bedeutet dabei "a mit einem tiefgestellten Index i") a = ± a_n a_{n-1} ... a_0 , a_{-1} a_{-2} ... a{-k} wobei die a_i Ziffern '0','1,','2',...,'9' sind und a_n ungleich 0. Die rechts vom Vorzeichen stehenden Ziffern heißen die 'tragenden Stellen' von a, die rechts vom Komma stehenden Ziffern 'Nachkommastellen' von a. Jede Zahl a ungleich 0 kann eindeutig in der Form dargestellt werden a = m * 10^q, wobei 1 <= |m| < 10 und q eine ganze Zahl ist. Diese Darstellung nennen wir die Gleitkommadarstellung von a. Beispiel: Die Dezimalzahl 123,45678 hat die Gleitkommadarstellung 1,2345678 * 102. Will man auf 2 /Nachkommastellen/ runden, so möchte man die Dezimaldarstellung 123,46 erhalten, will man auf 2 /tragende Stellen/ runden, so möchte man die Gleitkommadarstellung 1,2 * 102 erhalten. Die Java-Dokumentation wählt noch eine andere Umsetzung. Hier wird die Zahl aus dem Beispiel als [12345678, 5] dargestellt und allgemein die Notation [m,s] verwendet, wobei a = m*10^(-s). Hier geht man also von der Anzahl der Nachkommastellen aus (deren Endlichkeit vorausgesetzt wird), und es wird von rechts nach links abgezählt (man beachte das Minus- Zeichen des Exponenten). Wir haben also die 3 Darstellungen 123,45678 = 1,2345678 * 102 = [12345678, 5] In einem Computer sind natürlich nur endlich viele Gleitkommazahlen darstellbar. In der Mathematik gibt es aber unendlich viele. Will man also das mathematische Rechnen auf einem Computer 'simulieren', muss man ständig versuchen, trotz dieses Umstandes möglichst nahe an den 'wahren' Werten zu bleiben. Dieses Auswählen eines geeigneten Stellvertreters unter den im Rechner darstellbaren Zahlen ist es, was man, allgemein gesprochen, 'Runden' nennt, und diese Operation ist im Grunde vor und nach jeder arithmetischen Operationen notwendig, wenn man sicherstellen will, dass die Gesetze der Mathematik nicht verletzt werden. Die bekanntesten Arten zum Runden sind: den 'oberen' Nachbarn, den 'unteren' Nachbarn oder die 'nächstliegende' Zahl im System der darstellbaren Gleitkommazahlen auszuwählen. Neben dem Runden einer Gleitkommazahl gehört zu den Standardaufgaben sie geeignet zu 'formatieren'. Hier entscheidet man über die graphische Darstellung einer Ziffernfolge beim Anzeigen oder Ausdrucken. Beide Operationen sind begrifflich unabhängig voneinander, werden aber manchmal verwechselt - dies ist einer der Gründen, warum wir sie hier gemeinsam besprechen. Das Ergebnis einer Rundung ist ein 'double', das Ergebnis einer Formatierung ein 'String'. Für beide Operationen stellt die Java-Bibliothek Klassen zur Verfügung. Für das Rechnen und Runden: 'java.math.BigDecimal', Für das Formatieren: 'java.text.DecimalFormat'. Die folgenden Vorschläge setzen eine Version des JDK >= 1.5 voraus. Wie komplex die Dinge sind, zeigt, dass die Dokumentation von BigDecimal in der Version 1.5 allein 41 Seiten, die von 'DecimalFormat' 22 Seiten lang ist. Es wird daher dringend empfohlen, diese Dokumentation zu lesen. ::: 2. Das Runden einer Gleitkommazahl :::::::::::::::::::::::::::::::: Eine wichtige Art, Gleitkommazahlen in Java in den Griff zu bekommen, ist es, die Klasse BigDecimal zu verwenden. Dies bietet Vorteile gegenüber dem direkten Arbeiten mit 'doubles': In den Feinheiten und Sonderfällen der Gleitkomma-Arithmetik sind viele Fußangeln versteckt, ihnen durch den Gebrauch der Bibliotheksfunktionen aus dem Weg zu gehen, ist ein guter Rat nicht nur für Anfänger. Folgende einfache Funktion zum Runden einer Gleitkommazahl zeigt, wie man dabei vorgehen kann. ////////////////////////////////////////////////////////////////////////// public static double round (double d, int scale, RoundingMode mode, FormatType type) { if (Double.isNaN(d) || Double.isInfinite(d)) return d; scale = Math.max(scale,0); // Verhindert negative scale-Werte BigDecimal bd = BigDecimal.valueOf(d); if(type == FormatType.exp) { BigDecimal bc = new BigDecimal(bd.unscaledValue(),bd.precision()-1); return ((bc.setScale(scale, mode)). scaleByPowerOfTen(bc.scale()-bd.scale())).doubleValue(); } return (bd.setScale(scale, mode)).doubleValue(); } ////////////////////////////////////////////////////////////////////////// Kurzbeschreibung der Funktion: /** * @param d der zu rundende Gleitkommawert. * @param scale die Anzahl der Nachkommastellen, falls type = fix, * die Anzahl der tragenden Stellen - 1, falls type = exp. * scale sollte >= 0 sein (negative Werte werden auf 0 gesetzt). * @param mode die Rundungsart: einer der Rundungsarten von BigDecimal, * seit 1.5 in java.math.RoundingMode. * @param type ein Element von "enum FormatType {fix, exp}" gibt an, * auf welche Stellen sich die Rundung beziehen soll. * FormatType.exp ('Exponential') steht für tragende Stellen, * FormatType.fix ('Fixkomma') steht für Nachkommastellen. * @return der gerundete Gleitkommawert. * <p>Anmerkung: Für die Werte <tt>double</tt> NaN und ±Infinity * liefert round den Eingabewert unverändert zurück. */ Beispiel: d = -Math.exp(702); for (int scale = 0; scale < 6; scale++) System.out.println(round(d,scale,RoundingMode.HALF_EVEN,FormatType.exp)); 0 -> -7.0E304 1 -> -7.5E304 2 -> -7.49E304 3 -> -7.494E304 4 -> -7.4942E304 5 -> -7.49422E304 Beispiel: d = Math.PI*10000.0; for (int scale = 0; scale < 6; scale++) System.out.println(round(d,scale,RoundingMode.HALF_EVEN,FormatType.fix)); 0 -> 31416.0 1 -> 31415.9 2 -> 31415.93 3 -> 31415.927 4 -> 31415.9265 5 -> 31415.92654 Die Enumeration "enum FormatType {fix, exp};" muss dabei selber definiert werden. Man beachte auch die Verwendung der Factory-Methode valueOf(). Diese Form ist in der Regel dem Konstruktor 'BigDecimal(double val)' vorzuziehen. Man lese dazu die Erläuterungen in der Dokumentation (Version >= 1.5) von BigDecimal. "This is generally the preferred way to convert a float or double into a BigDecimal.." Ein einfaches Beispiel veranschaulicht den Unterschied: System.out.println(BigDecimal.valueOf(1.005)); System.out.println(new BigDecimal(1.005)); >>>> 1.005 >>>> 1.00499999999999989341858963598497211933135986328125 In Java-Versionen vor 1.5 läßt sich BigDecimal.valueOf(d) simulieren durch new BigDecimal(Double.toString(d))). Der 'if'-Zweig der Funktion 'round' ist allerdings in älteren Versionen nicht (so einfach) zu erhalten: Die Funktionen precision() und scaleByPowerOfTen() sind erst ab 1.5 im API enthalten und müssten bei älteren Versionen 'nachgebaut' werden. Entweder eine nette Übungsaufgabe oder ein guter Grund, auf eine Version >= 1.5 umzusteigen. BigDecimal stellt 8 Möglichkeiten zur Rundung zur Verfügung. Will man auf 2 Stellen im 'kaufmännischen Sinn' runden, so wähle man HALF_UP. Der IEEE-Standard sieht diese Rundung allerdings nicht als den Normalfall an, und bei numerischen Rechnungen wählt man besser HALF_EVEN. Gebrauchsfertig in handliche Makros gepackt: public static double roundUpFix2 (double d) { return round (d, 2, RoundingMode.HALF_UP, FormatType.fix); } public static double roundEvenExp2 (double d) { return round (d, 2, RoundingMode.HALF_EVEN, FormatType.exp); } ::: 3. Das Formatieren einer Gleitkommazahl ::::::::::::::::::::::::::: Für das Formatieren ist 'java.text.DecimalFormat' das angebotene Werkzeug. Eine kleine Utility-Funktion 'format' mit einer ähnlichen Aufrufstruktur wie die Funktion 'round', zeigt hier einen Ansatz: ////////////////////////////////////////////////////////////////////////// public static String format (double d, int scale, FormatType type) { if (Double.isNaN(d) || Double.isInfinite(d)) return Double.toString(d); scale = Math.max(scale,0); // Verhindert negative scale-Werte DecimalFormat df = new DecimalFormat(); df.setMaximumFractionDigits(scale); df.setMinimumFractionDigits(scale); if( type == FormatType.exp ) { StringBuilder sb = new StringBuilder("0E0"); if(scale > 0) sb.append(".000000000000000000",0,scale+1); df.applyPattern(sb.toString()); } else { df.setGroupingUsed(false); df.setMinimumIntegerDigits(1); } return df.format( d ); } ////////////////////////////////////////////////////////////////////////// Kurzbeschreibung der Funktion: /** * @param d der zu formatierende Gleitkommawert. * @param scale die Anzahl der Nachkommastellen, falls type = fix, * die Anzahl der tragenden Stellen - 1, falls type = exp. * scale sollte >= 0 sein (negative Werte werden auf 0 gesetzt). * @param type ein Element von "enum FormatType {fix, exp}". * FormatType.exp fordert eine eine wissenschaftliche Exponential- * darstellung an, FormatType.fix fordert ein Fixkommaformat an. * @return eine Zeichenkette, die den Gleitkommawert darstellt und entsprechend den gewünschten Parametern formatiert ist. * <p>Anmerkung: Für die Werte <tt>double</tt> NaN und ±Infinity * liefert diese Methode {@link Double#toString} zurück. */ Auch hier muss die Enumeration "enum FormatType {fix, exp};" selber definiert werden. Man beachte bei den folgenden Beispielen die Verwendung von ',' anstelle von '.' bei der Ausgabe. Wir bekommen also in der Tat Gleitkommazahlen und nicht Gleitpunktzahlen geliefert, wie sich das auch gehört, wenn die 'lokalen Einstellungen' auf Deutschland gesetzt sind. Wer arabische oder indischen Ziffern bevorzugt, kann auch dies erreichen - zur Verwendung von 'Locales' im Zusammenhang mit DecimalFormat verweisen wir auf die Dokumentation. Die beiden Beispiele von oben nehmen, mit den Voreinstellungen der deutschen Locale, folgende Gestalt an: Beispiel: d = -Math.exp(702); for (int scale = 0; scale < 6; scale++) System.out.println(scale+" -> "+format(d, scale, FormatType.exp)); 0 -> -7E304 1 -> -7,5E304 2 -> -7,49E304 3 -> -7,494E304 4 -> -7,4942E304 5 -> -7,49422E304 Beispiel: d = Math.PI*10000.0; for (int scale = 0; scale < 6; scale++) System.out.println(scale+" -> "+format(d, scale, FormatType.fix)); 0 -> 31416 1 -> 31415,9 2 -> 31415,93 3 -> 31415,927 4 -> 31415,9265 5 -> 31415,92654 Bequeme Makros machen die Funktion jetzt gebrauchsfertig: public static String formatFix2 (double d) { return format (d, 2, FormatType.fix); } public static String formatExp2 (double d) { return format (d, 2, FormatType.exp); } In vielen Anwendungen sind die Anforderungen an die Formatierung jedoch wesentlich komplexer, weil dabei auch der für die Anzeige zur Verfügung stehende Platz berücksichtigt werden muss. Eine Basisklasse dafür, die sich auch für eigene Experimente eignet, kann man hier finden: <URL:http://www.javajungle.de/OpenSource/DecimalFormat/> ::: 4. Runden plus Formatieren ::::::::::::::::::::::::::::::::::::::::: Als letztes eine Warnung: DecimalFormat verwendet /intern/ auch eine Rundung, und zwar - fest verdrahtet - RoundingMode.HALF_EVEN. Das ist schwerlich etwas anderes als ein Design-Bug, denn damit wird die Verwendung der 7 anderen Rundungsarten von BigDecimal in Verbindung mit dieser Formatierungsklasse problematisch. Insbesondere sollte jeder, der DecimalFormat verwendet, sich klar darüber sein, dass hier nicht kaufmännisch gerundet wird! Dazu noch ein Beispiel: Das einfache dform2 = new java.text.DecimalFormat("0.00") beschert folgende 'Überraschung' beim Ausdruck einer Rechnung: dform2( 10.495 ) -> 10,50 dform2( 10.505 ) -> 10,50 dform2( 10.515 ) -> 10,52 Zum Glück beißt sich die interne Rundung von DecimalFormat nicht mit einer vorgeschalteten Aufrundung, sofern es nur um das Formatieren geht, so dass sich dieses Problem so lösen läßt: public static String formatFixUp2 (double d) { return formatFix2(roundUpFix2(d)); } formatFixUp2( 10.495 ) -> 10,50 formatFixUp2( 10.505 ) -> 10,51 formatFixUp2( 11.515 ) -> 11,52 Aber dieses Beispiel ist in erster Linie zur Illustration des Gesagten gedacht. Denn bei kaufmännischen Rechnungen gilt es die Maxime von Paul Ebermann zu befolgen: "Beim Rechnen mit Geld verwende man NIE 'double'." Zu diesem Thema lese man auch den zweiten Link, der unten angegeben ist. ::: 5. Zusammenfassung :::::::::::::::::::::::::::::::::::::::::::::::::: ---------------------------------------------------- RUNDEN | FORMATIEREN ---------------------------------------------------- d = PI*10000.0; Nachkomma-Stellen | Fixpunkt-Format roundUpFix2(d) | formatFix2(d) Wert: double 31415.93 | Wert: String "31415,93" ---------------------------------------------------- d = -exp(702); Tragende Stellen | Exponential-Format roundEvenExp2(d) | formatExp2(d) Wert: double -7.49E304 | Wert: String "-7,49E304" ---------------------------------------------------- In dieser Gegenüberstellung wurde der Einfachheit willen ein Spezialfall gewählt, für die allgemeine Darstellung gilt Analoges. ::: 6. Links und Literatur ::::::::::::::::::::::::::::::::::::::::::::::: *** Zur Verwendung der API: <URL:http://java.sun.com/docs/books/tutorial/i18n/format/ decimalFormat.html> <URL:http://www.javaworld.com/javaworld/jw-06-2001/jw-0601-cents_p.html> *** Grundsätzliches zur (dezimalen) Gleitkomma-Arithmetik: <URL:http://www2.hursley.ibm.com/decimal/decimal.html> <URL:http://www2.hursley.ibm.com/decimal/decifaq.html> 3.13.3. Wie kann ich in Java Zufallszahlen im Bereich 0..n erzeugen? Autor: Markus Reitz, Uwe Günther ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Zur Erzeugung von Zufallszahlen bietet Java das Random-Objekt. Bevor Zufallszahlen erzeugt werden können, muß ein solches Objekt erzeugt werden, wobei ein Random-Seed genannter Zahlenwert übergeben wird, der Anteil an der Berechnung von Zufallszahlen hat. Generell muß man sagen, daß es sich bei auf dem Computer erzeugten Zufallszahlen um keine echten zufälligen Zahlenfolgen handelt, der Begriff Pseudo-Zufallszahlen trifft das Ganze besser. Grundlage dieser Zahlen sind Generator-Funktionen, die eine mehr oder minder zufällig erscheinende Folge von Zahlen erzeugen. Für die Berechnung der Zahlen spielt der Random-Seed (quasi der Samen für die Berechnung) eine große Rolle. Wird dieser Random-Seed möglichst zufällig gewählt, so sind die resultieren-den Zahlenfolgen beinahe echte Zufallszahlen. In der Praxis hat es sich als brauchbar erwiesen, als Random-Seed die jeweils aktuelle Systemzeit zu benutzen, damit gewährleistet ist, daß sich der Seed möglichst häufig ändert. Damit erzeugt man ein Random-Objekt am Besten wie folgt: Random zufall = new Random(System.currentTimeMillis()); oder Random zufall = new Random(); Wobei der Default-Kostruktor intern auch nur this(System.currentTimeMillis()); aufruft, wie die Firma Sun in ihrer Online Dokumentation schreibt. Mit Hilfe der Member-Funktion nextInt() wird die nächste erzeugte Integerzahl geliefert. In einem Großteil der Fälle möchte man jedoch nicht Integerzahlen haben, die auch negative Werte annehmen können, sondern Zahlen im Bereich von 0 bis n. Hier kommt eine Variation der nextInt() Methode ins Spiel, deren Anwendung die folgende Zeile demonstriert: zahl = zufall.nextInt(n+1); Ist einem die Saat von Random nicht zufällig genug, dann sollte man die Klasse SecureRandom aus dem Package java.security verwenden. Hier werden unter anderem je nach Plattform Betriebsystem spezifische Mechanismen gewählt, wie zum Beispiel unter UNIX /dev/urandom. 3.14. [MISC] - Alles, was nicht in eine der anderen Rubriken paßt. ------------------------------------------------------------------ 3.14.1. Ich komme mit dem import-Statement nicht klar, was mache ich falsch? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Problem: In einem Programm kommen folgende beide import-Statements vor: import java.awt.*; import java.awt.event.*; Warum reicht nicht das erste Statement aus, um die nötigen Klassen und Interfaces zu importieren? Intuitiv würde man sagen, daß mit Hilfe des * alle Klassen und Interfaces importiert werden, die unterhalb des Pfades java.awt stehen, doch leider ist dies nicht so. Mit import java.awt.*; werden nur alle Klassen importiert, die im Verzeichnis java.awt stehen, nicht jedoch Klassen, die noch tiefer verschachtelt abgespeichert sind. Deshalb ist das zweite import-Statement nötig, welches alle Klassen in java.awt.event importiert. 3.14.2. Warum gibt es Probleme bei final Werten in Verbindung mit elementaren Typen? Autor: Markus Reitz, Paul Ebermann ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Angenommen, eine Klasse A ist wie folgt definiert: public class A { final static boolean test = true; } Aus irgendeinem Grund wird die Definition der Klasse A auf Folgendes geändert: public class A { final static boolean test = false; } Die Klasse A wird neu compiliert und eine neue Klassendatei erzeugt. Alle anderen Klassen, die das in Klasse A definierte Feld verwenden, müßten nun mit dem korrigierten Wert false arbeiten. Probiert man dies in der Praxis aus, so stellt man fest, daß dies nicht der Fall ist. Der Grund liegt in der Sprachdefinition der Sprache Java. Ein primitiver (elementarer) Datentyp, der das Modifiziererpaar final static trägt, wird vom Compiler als Konstante behandelt und kann überall, wo er im Programm auftritt, vom Compiler direkt durch den Wert ersetzt werden. Um also zu erreichen, daß sich die in der Klasse A gemachte Änderung auf alle sie benutzenden Klassen auswirkt, müssen alle betroffenen Klassen neu übersetzt werden. Dies gilt aber nur, wenn der Wert tatsächlich mit einer Compile-Zeit-Konstante initialisiert wird. Ein Ausweg aus diesem Dilemma stellt hier die Verwendung des Typ sicheren enum (type safe enum) dar, siehe weiter unten. Eine weitere Typ sichere Variante findet sich in folgendem Beispiel: public class A { final static boolean test = new Boolean(true).booleanValue(); } Geändert dann: public class A { final static boolean test = new Boolean(false).booleanValue(); } 3.14.3. Was bedeuten "$" im Namen von Class-Files? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das Dollarzeichen innerhalb eines Klassennamens taucht auf, wenn innere Klassen, also Klassen, die innerhalb einer anderen Klasse definiert sind, verwendet werden. Somit ist Testklasse$InnereKlasse.class die Class-Datei der inneren Klasse "InnereKlasse", die innerhalb der Klasse Testklasse definiert worden ist. 3.14.4. Wie lassen sich Bilder im Dateiformat XYZ laden oder speichern? Autor: Marco Schmidt ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Seit Java 1.0 lassen sich mit java.awt.Toolkit GIF- und JPEG-Dateien laden, seit Version 1.3 auch PNG (genaugenommen nicht nur aus Dateien, sondern von beliebigen URLs und sogar aus Byte-Arrays). Das Speichern von Bildern in diesen oder anderen Formaten wird nicht unterstützt. Da weder java.awt.Image noch java.awt.image.BufferedImage die Schnittstelle Serializable implementieren, lassen sich auch die eingebauten Routinen zum Objekt-I/O nicht verwenden. Eine Notlösung stellt eventuell die Verwendung von PixelGrabber bzw. MemoryImageSource aus java.awt.image dar. Mit ersterem lassen sich Pixel als RGBA-int-Werte aus einem beliebigen java.awt.Image-Objekt extrahieren, mit letzterem erzeugt man ein Image-Objekt aus solchen "int-Pixeln". Lesen und Schreiben von int-Arrays unterstützen u. a. DataIn/OutputStream bzw. ObjectIn/OutputStream. Dabei ist zu bedenken, daß dieser Ansatz vier Bytes pro Pixel verbraucht. Eventuell führt die zusätzliche Verwendung von java.util.zip.DeflaterOut/InflaterInputStream zu Einsparungen. Für gängige Dateiformate sind zahlreiche externe Lösungen verfügbar, sowohl frei als auch kommerziell, in stark unterschiedlicher Qualität. Unter <URL:http://www.geocities.com/marcoschmidt.geo/java-image-coding.html> befindet sich eine Liste. Die Bibliothek JIMI <URL:http://java.sun.com/products/jimi/> ist ein guter Einstiegspunkt, sie ist (seit Sun sie aufgekauft hat) kostenlos erhältlich und deckt eine größere Anzahl Formate ab. Das Package com.sun.image.codec.jpeg - zum Laden und Speichern von Bildern im JPEG-Format - ist in neueren Sun-JREs und JDKs enthalten, man kann sich jedoch nicht darauf verlassen, es auch in anderen Java-Distributionen zu finden. Bei Verwendung des LZW-Algorithmus' (z. B. in GIF bzw. TIFF/LZW) ist darauf zu achten, daß sowohl kommerzielle als auch Freeware-Produkte in bestimmten Ländern (inkl. USA und Deutschland) eine Lizenz beim Patentbesitzer Unisys erwerben müssen (siehe auch <URL:http://dmoz.org/Computers/Data_Formats/Graphics/2D/GIF/>. Als Alternative zu GIF ist PNG gut geeignet, da es GIFs Einsatzgebiet - mit Ausnahme von Animationen - abdeckt und in modernen Browsern unterstützt wird. Mit Java 1.4 wurde ein eigenständiges Package zum Laden und Speichern eingeführt: javax.imageio. Dieses unterstützt zunächst nur das Lesen von GIF, JPEG und PNG sowie das Schreiben von JPEG und PNG. Allerdings plant Suns JAI-Team (Java Advanced Imaging, eine Java-Bibliothek für Bildverarbeitung), fast alle Codecs aus JAI zu portieren, so daß sie der Spezifikation von javax.imageio folgen. Das schließt BMP, PNM (Portable Anymap) und TIFF ein. In Zukunft soll JAI für Codecs komplett auf die ImageIO-API aufbauen. Eine Einführung befindet sich auf <URL:http://java.sun.com/j2se/1.4/docs/guide/imageio/> oder bei installiertem JDK unterhalb des Installationsverzeichnisses in /docs/guide/imageio/index.html. Auf <URL:http://www.jalice.net/myJava.htm> finden sich zahlreiche Code-Beispiele und weiterführende Informationen zum Umgang mit Bildern, Java2D und der neuen ImageIO-API. 3.14.5. Was geht nicht mit Java? Autor: Martin Erren ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Einige Features sind sehr systemnah und können wegen der Plattformunabhängigkeit von Java nicht direkt gelöst werden. Oft ist liegt dabei das eigentliche Problem eher im Ansatz, weil zu plattformspezifisch gedacht wird, statt gängige Java-Paradigmen umzusetzen. So sind z.B. Properties jederzeit Environment Variablen vorzuziehen. Ansonsten schafft ein entsprechendes System.exec(...) Abhilfe oder ein Bibliotheksaufruf, z.B. über JNI (Java Native Interface). Ein für jede Plattform einzeln zu lösendes Problem. Einige Bibliotheken wie JavaComm sind bereits für die verschiedensten Plattformen implementiert. CPU: Daten wie Taktfrequenz auslesen. Speicher: Gesamtgrösse ermitteln, direkt manipulieren. Laufwerke: Genauen Laufwerkstyp erkennen (CDRom,...) CD auswerfen Filesystem: Partitionsgrösse bestimmen. Linux: genauen file type feststellen (block/stream devices,...) einzelne Berechtigungen auslesen Windows: Verknüpfungen folgen Native Lösung: <URL:http://www.tolstoy.com/samizdat/jconfig.html> Textconsole: Bildschirm löschen cursor position setzen Farben Native Lösung: <URL:http://www.bmsi.com/tuipeer/> Pure java (über ANSI): <URL:http://purl.org/NET/ePaul/#pps> Desktop: Nicht reckeckige Fenster Native Lösung: <URL:http://www.l2fprod.com/software/skinlf/> Tray Icon Native Lösung beschrieben bei: <URL:http://www.nevaobject.com/_docs/_coroutine/coroutine.htm#example> Fenster immer im Vordergrund halten Native Lösung: <URL:http://www.mysrc.net/lib/java/MySRC-AlwaysOnTop-090.zip> Netzwerk, Schnittstellen: ICMP Serielle + Parralle Schnittstelle ansteuern Native Lösung: <URL:http://java.sun.com/products/javacomm/> Tastatur: SHIFT, CNTRL, ALT alleine gedrückt Windows Taste Standardeingabe ungepuffert lesen (statt flush bei \n) Maus: Scrollrad (möglich ab JDK 1.4) Native Lösung <URL:http://www.codeproject.com/java/mousewheel.asp> Betriebssystem: Environment-Variablen lesen(unmöglich seit 1.2)/schreiben. System-Shutdown. Windows: Registry lesen/schreiben (eingeschr. möglich ab JDK 1.4) Native Lösung: <URL:http://www.trustice.com/java/jnireg/> 3.14.6. Wie kann ich in meinem Java-Programm ein HTML-Dokument anzeigen lassen? Author: Marco Schmidt ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Es gibt zwei Lösungsansätze, das Einbinden von Java-Code, der dies leistet, oder das Aufrufen eines externen Programms (Browser). * Man kann das Dokument selbst in einer GUI-Komponente anzeigen lassen. Das hat den Vorteil, daß man unabhängig von externen Programmen ist. In der Standardbibliothek gibt es seit Java 1.2 in der Hierarchie javax.swing.text.html entsprechenden Code. Der ist allerdings nur für Swing-Oberflächen geeignet, nicht für AWT. Darüber hinaus wird nur HTML 3.2 unterstützt. * Eine Mischlösung ist die Verwendung eines reinen Java-Browsers wie HotJava <URL:http://java.sun.com/products/hotjava/3.0/>. Dieser Browser könnte - nach Klärung der rechtlichen Details - mit dem Programm ausgeliefert werden und müßte überall dort funktionieren, wo auch die eigene Java-Applikation ausgeführt wird. HotJava unterstützt auch nur HTML 3.2, benötigt aber nur das AWT und Java 1.1. * Eine ähnliche Lösung ist der ebenfalls in reinem Java geschriebene kommerzielle ICEbrowser: <URL:http://www.icesoft.no/ps_browser_overview.html>. * BrowserLauncher ist eine Java-Klasse (Freeware) zum Starten des Standard-Browsers: <URL:http://browserlauncher.sourceforge.net/> * Ein JavaWorld-Artikel versucht ebenfalls, den Standardbrowser zu finden und zu starten: "Java Tip 66: Control browsers from your Java application" <URL:http://www.javaworld.com/javaworld/javatips/jw-javatip66.html> * Die kommerzielle Bibliothek JConfig bietet Ähnliches, unter Verwendung von nativem Code: <URL:http://www.tolstoy.com/samizdat/jcdocs/overview.html#WBLaunch> 3.14.7. Unter Windows werden in der Konsole (DOS-Eingabeaufforderung) die Umlaute falsch ausgegeben. Wie kann ich das korrigieren? Author: Peter Karp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Das Problem liegt in der verwendeten Codepage. Standardmäßig geht ein Java-Programm von der Codepage Latin1 aus, während in der Windows-Konsole eine andere Codepage (hier meist CP850) verwendet wird. Intern werden bei Java Zeichensätze in Unicode codiert. Zur Ausgabe muss das Java-Programm die Unicode-Kodierung in die passende Kodierung der aktuellen Codepage konvertieren, welche für ein Zeichen -- im Unterschied zu Unicode -- nur ein Byte verwendet. Die Umlaute liegen bei verschiedenen Kodierungen oft nicht an der gleichen Stelle. Die aktive Codepage kann unter Windows 2000/XP mit dem Befehl chcp bzw. unter Windowx 9x mit dem Befehl mode con:cp angezeigt werden. Standardmäßig ist bei einem deutschen Windows Codepage 850 (manchmal auch 437) in der Konsole aktiv. In den "normalen" Windows-Programmen wird immer der MS-ANSI-Zeichensatz (Codepage 1252) verwendet. MS-ANSI ist weitgehend mit Latin1, welches z.B. unter Linux verwendet wird, identisch. Die 7-Bit-ASCII-Zeichen und die deutschen Umlaute sind an den gleichen Stellen. Somit gibt es zwei grundlegend verschiedene Lösungen: 1) Man ändert die verwendete Codepage in Windows auf Codepage 1252 oder Latin1. a) Die Unix-Umgebung "Cygwin" für Windows benutzt Latin1 als Codepage. Cygwin findet man unter www.cygwin.com und stellt unter anderem die üblichen Unix-Tools und eine Shell zur Verfügung. b) Unter Windows 2000/XP kann mit dem Befehl chcp die aktive Codepage gewechselt werden. Dies alleine stellt aber noch nicht die korrekte Ausgabe der Umlaute sicher, da auch der verwendete Font die richtige Kodierung verwenden muss. Sinnvollerweise erstellt man sich eine Verknüpfung zu einem neuen Konsolenfenster, dass Codepage 1252 und einen dazu passenden Font verwendet, da man in den anderen Konsolenfenstern in der Regel wahrscheinlich weiterhin Codepage 850/437 verwenden will, um zu den DOS-Programmen kompatibel zu bleiben: 1. neue Verküpfung auf Desktop erstellen 2. Speicherort angeben: %SystemRoot%\system32\cmd.exe /k chcp 1252 3. Name angeben: Konsole1252 4. fertigstellen 5. Eigenschaften der Verknüpfung ändern und als Schriftart "Lucida Console" definieren. Optional gibt man noch den Pfad zu dem Verzeichnis mit den Java-Programmen bei "Ausführen in:" an. 6. fertig, das wars - wird der Shortcut nun für Java-Programme verwendet, gibts keine Probleme mehr mit Umlauten Der Font "Lucida Console" ist ein TrueType-Font im OpenType-Format, der die Unicode-Codierung unterstützt und daher die Zeichen für alle verfügbaren Codepages beinhaltet. Die sog. "Rasterschriftart" hingegen liegt immer in der OEM-Kodierung vor. Dabei ist die OEM-Kodierung, die Kodierung, die im jeweiligen Land für "DOS-Programme" üblich ist. Für Deutschland ist das Codepage 850, für Amerika Codepage 437 usw. Falls man das Aussehen der "Lucida Console" nicht mag, kann man einen neuen Font installieren, der das OEM-Flag gesetzt hat, aber trotz dieses Flags die Kodierung für Codepage 1252 verwendet. Diesen Font New1252.FON, und eine Erklärung wie dieser zu nutzen ist, findet sich auf der Homepage von <URL:http://www.uwe-sieber.de>. 2) Alternativ kann im Java-Programm selbst sichergestellt werden, dass die aktive Codepage zur Ausgabe berücksichtigt wird. Erstelle eine neue Klasse für dein Tool-Paket und rufe sie mit new KonsolenUmlaut(); auf, oder packe das folgende Code-Beispiel in die Klasse, die das Problem betrifft. Diese Möglichkeit sollte aber nicht fest codiert werden, sondern optional über ein Argument beim Start des Programmes mitgegeben werden können. Eine weitere Möglichkeit wäre die Ausgabe des Befehls 'mode con:cp' auszuwerten und dann die Codepage entsprechend zu übergeben. Code-Beispiel ============= /** * Gibt die Umlaute unter Windows in Codepage 850 aus. Eingabe von * der Konsole oder Dateioperationen sind davon unberührt und haben * auch die Umlaute korrekt nach Latin1. */ package myToolsPackage; import java.io.*; public final class KonsolenUmlaut { public KonsolenUmlaut() { String sys = System.getProperty("os.name"); if (sys.startsWith("Windows")) { try { System.setOut( new PrintStream( new FileOutputStream(FileDescriptor.out), false, "cp850")); System.setErr( new PrintStream( new FileOutputStream(FileDescriptor.err), true, "cp850")); } catch (IOException e) { e.printStackTrace(); } } } } 3.15. [ERROR] - Fehlermeldungen ------------------------------- 3.15.1. Warum findet Java den Konstruktor nicht? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Problem: public class Test { private int a; private int b; public void Test (int a , int b) { this.a = a; this.b = b; } public static void main (String[] args) { //Und hier eine Fehlermeldung. Test myTest = new Test (12, 13); } } Versucht man die obige Klasse zu übersetzen, so meckert der Compiler mit der Meldung Wrong number of arguments in constructor ... . Die Lösung des Problems ist einfach: Konstruktoren besitzen keinen Rückgabetyp, auch nicht void. In obigem Beispiel hat man nämlich nicht den Konstruktor für die Klasse Test definiert, sondern eine Funktion, die den selben Namen wie die Klasse hat. Weiter unten versucht man nun, den vermeintlich definierten Konstruktor aufzurufen und bekommt die beschriebene Fehlermeldung. Lösung: public class Test { private int a; private int b; public Test (int a , int b) { this.a = a; this.b = b; } public static void main (String[] args) { //Hier kommt jetzt keine Fehlermeldung mehr! Test myTest = new Test (12 , 13); } } 3.15.2. Warum bekomme ich eine "NoClassDefFoundError" Fehlermeldung beim Starten von java? Autor: Markus Reitz, Sascha Raabe ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Der Grund ist einfach: Man muß der Java-Laufzeitumgebung mitteilen, wo sich die *.class-Dateien des Programms befinden, damit die Ausführung erfolgen kann. Dies erreicht man bei der SUN-Implementierung mit dem Aufrufparameter -cp für Classpath oder man setzt die Umgebungsvariable CLASSPATH auf das Verzeichnis, in dem sich die *.class-Dateien befinden. Ausserdem muss beim Aufruf des Programms der vollständige Klassenname angegeben werden. Dieser besteht aus Packagenamen und Name der Java Klasse. Beispiel: Die Java Klasse "MyApp" befindet sich im Package "de.foo.bar". Der Aufruf der Klasse erfolgt somit über: java de.foo.bar.MyApp Dabei ist auf Gross- und Kleinschreibung zu achten. 3.15.3. Warum bekomme ich eine "Couldn't read <Name>" Fehlermeldung beim Kompilieren mit javac? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In Java gilt die einfache Regel, daß der Name der Datei, in der die Klasse gespeichert ist, identisch mit dem Klassennamen sein muß, wobei auf die korrekte Groß- und Kleinschreibung zu achten ist. 3.15.4. Warum bekomme ich eine "class <Name> must be defined in a file called <Name>" Fehlermeldung beim Kompilieren von javac? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Hier gilt analog das unter dem vorherigen Punkt angeführte. Eine Datei, in der der Quelltext der Klasse gespeichert ist, muß den selben Namen besitzen wie die Klasse im Quelltext. 3.15.5. Warum wird beim Zugriff auf ein korrekt initialisiertes Objekt- Array eine NullPointerException geworfen? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Problem: public class A { public String text; } public class Test { public static void main (String[] args) { A array = new A[10]; for(int i = 0; i < 10; i++) { //wirft NullPointerException array[i].text = "Test"; } } } Führt man das Programm aus, so erhält man die im Programmtext erwähnte NullPointerException, obwohl doch alles eigentlich ganz in Ordnung aussieht. Der Teufel steckt aber im Detail. Mit A array[] = new A[10] wird eben nicht ein Array von zehn Objekten vom Typ A erzeugt, sondern nur ein Array, welches zehn Referenzen auf Objekte vom Typ A enthält, die eigentlichen Objekte werden damit nicht erzeugt. Lösung: public class A { public String text; } public class Test { public static void main (String[] args) { A array = new A[10]; for(int i = 0; i < 10; i++) { array[i] = new A(); //wirft keine NullPointerException mehr array[i].text = "Test"; } } } 3.15.6. Warum bekomme ich eine NullPointerException, wenn ich versuche, auf Methoden oder Attribute von in einem Array gespeicherten Objekten zuzugreifen? Autor: Michael Paap ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Vermutlich hast Du zwar den Array erzeugt, nicht jedoch die Objekte. Dann sind die Fields Deines Array mit null initialisiert, d.h. die Referenzen existieren zwar, nicht jedoch die referenzierten Objekte. Abhilfe: Um z.B. einen Array von sechs Buttons zu erzeugen, geht man so vor: Button[] myButtons = new Button[6]; for (int i = 0; i < myButtons.length; i++) { myButtons[i] = new Button(); } 3.15.7. Warum meckert der Compiler bei nicht initialisierten final Variablen Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Ab Java 1.1 ist folgender Code gültig: public class Test { final int i; public Test(int i) { //finale Variable this.i wird mit dem Wert //von i initialisiert. this.i = i; } public Test() { //Fehler die finale Variable this.i muss auch in diesem //Konstruktor initialisiert werden. } } Es ist also möglich, Variablen mit finalen Werten zu erzeugen, deren finaler Wert beim ersten initialisieren der Variablen einmalig festgelegt werden kann. Allerdings kann dies nur innerhalb eines Konstruktors geschehen und es muß auf jeden Fall eine Initialisierung der Variablen erfolgen, denn ansonsten meldet der Compiler Fehler. 3.15.8. Was hat die Compilerfehlermeldung "... is deprecated" zu bedeuten? Autor: Markus Reitz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Diese Meldung erhält man normalerweise dann, wenn ältere Java-Programme mit einer neuen Version des Compilers übersetzt werden sollen. Die Meldung ist nur eine Warnung, also kein richtiger Fehler, die Funktionalität des Programms wird nicht beeinträchtigt. Die Methode, die bei der Meldung "... is deprecated" genannt wird, sollte aber nach Möglichkeit nicht mehr im Programm verwendet werden, da das Konzept der Klasse verändert wurde und deshalb die Methode unter Umständen in neueren Versionen nicht mehr implementiert sein wird. Treten also solche Meldungen beim Compilieren auf, so sollte man die entsprechenden Methoden durch die empfohlenen Varianten ersetzen, damit gewährleistet ist, daß das Programm auch mit späteren Versionen von Java einwandfrei übersetzt werden kann. 3.16. [ClassLoader] - Alles über Classloader -------------------------------------------- 3.16.1 Wie funktionieren Classloader? Autor: Ortwin Glück, Ulf Jaehrig, Patrick Roemer, Jan Schulz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Für das Laden von Klassendefinitionen sind in Java Exemplare der Klasse java.lang.ClassLoader zuständig.[1] Jede Klasse ist assoziiert mit dem Classloader (kurz: CL), über den sie geladen wurde und kann über diesen im Zusammenhang mit ihrem voll qualifizierten Klassennamen eindeutig identifiziert werden. Klassen mit gleichem Namen aber unterschiedlichen CL gelten als verschieden; Casts zwischen den beiden Klassen funktionieren nicht. Da die Klasse ueblicherweise auch bei der equals()-Operation herangezogen wird, werden auch Objekte mit gleicher Struktur und gleichen Membern, deren Klassen gleiche Namen aber unterschiedliche CL haben, als unterschiedlich betrachtet. Die im System vorhandenen CL bilden eine baumartige Hierarchie, in der Anfragen nach oben weitergereicht werden können (und sollen), und die in einem 'obersten' CL (dem sog. Bootstrap-CL) wurzelt. Der Ladevorgang teilt sich üblicherweise auf in folgende Schritte: 1. Es wird eine Anfrage an den CL gestellt, entweder explizit über ClassLoader#loadClass(), indirekt über Class#forName() oder implizit durch Verwendung eines Klassennamens im Sourcecode. In den letzten beiden Fällen wird der CL der Klasse, auf der forName() aufgerufen wird bzw. derjenigen, aus welcher der gerade ausgeführte Code stammt, verwendet. 2. Der CL überprüft, ob die Klasse bereits von ihm geladen wurde. Falls ja, sollte er das entsprechende Klassenexemplar gecached haben[2] - dieses wird zurückgeliefert, fertig. 3. Der CL delegiert die Anfrage zunächst an den ihm übergeordneten CL, der wiederum bei Punkt 2 mit der Bearbeitung beginnt. Kann dieser ein Klassenexemplar liefern, wird dieses zurückgegeben - fertig. 4. Der CL holt den Bytecode - je nach CL kann dieser aus unterschiedlichen Quellen stammen: .class-datei, .jar-datei, URL, programmatisch generiertes Byte-Array, usw. 5. Der Bytecode wird per ClassLoader#defineClass() in ein Exemplar von java.lang.Class umgewandelt. 6. Gegebenenfalls werden referenzierte Klassen und Interfaces rekursiv geladen ('resolving'). (Wohlgemerkt: 'Der' CL einer Klasse ist derjenige in der Delegationskette, der sie tatsächlich geladen hat (Schritt 2 oder 5), nicht unbedingt derjenige, auf dem ursprünglich loadClass() aufgerufen wurde.) Diese Vorgehensreihenfolge ist zwar erwünscht, kann aber teilweise nicht erzwungen werden. Insbesondere der Vorrang der Delegation kann in seltenen Fällen ignoriert werden, so z.B. beim Webapp-CL von Tomcat[9]. Bei Vergleichen von Klassen oder Objekten, beim Laden von Ressourcen (s.u.), bei Singletons[7] und bei 'Typesafe Enumerations'[8] kann man unter unerwarteten CL-Bedingungen schweren Schiffbruch erleiden. Näheres zum CL-Mechanismus findet man z.B. bei [4], [5] und [6]. 3.16.2 Warum macht der Classloader im Servlet-Container Probleme? Warum funktioniert das Einlesen von Ressourcen ueber den Classloader bei mir nicht? Autor: Ortwin Glück, Ulf Jaehrig, Patrick Roemer, Jan Schulz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Üblicherweise muss man sich nicht besonders oft mit CL befassen. Ausnahmen sind Probleme beim Laden von Ressourcen aus dem Classpath (=> dclj-FAQ 3.3.6.) und das Schreiben von Bibliotheken oder Klassen, die im Kontext CL-technisch komplizierterer Umgebungen wie J2EE- oder Servlet-Containern zur Anwendung kommen können oder sollen. Es gibt mehrere Möglichkeiten, direkt oder indirekt an unterschiedliche CL zu geraten: Über die Klasse, die den gerade ausgeführten Code enthält, über eine beliebige andere Klasse oder über Thread.currentThread().getContextClassLoader(). Üblicherweise will man sich möglichst tief in der CL-Hierarchie einklinken. In Servlet-Containern fährt man meist recht gut mit dem Context-CL des aktuellen Threads, da der Container sich drum kuemmert, dass dieser passend gesetzt ist, ebenso im Beispielcode. Anderswo ist das eine recht wacklige Angelegenheit, da dieser Context-CL praktisch beliebig gesetzt und wieder neu gesetzt werden oder auch null sein kann. Das Laden über eine bestimmte Klasse bringt hingegen Probleme, wenn deren CL kein Blatt der CL-Hierarchie darstellt und es möglich ist, dass dynamisch auf tieferliegende Elemente zurückgegriffen werden soll. Beispiel: Eine Klasse in einem shared- oder common-Verzeichnis eines Servlet-Containers will zur Laufzeit eine Webapp-spezifische Klasse laden. Ein weiteres Problem mit dem Einlesen von Ressourcen ueber den CL ist, dass man ggfs. die entsprechenden Security-Privilegien benoetigt. Diese Einschraenkung ist bei Verwendung von Class#GetResource[AsStream]() nicht gegeben. Aufgrund der vielen sich ergebenden Fallstricke ist ein Einlesen von Ressourcen ueber Class/Classloader in komplexeren Umgebungen nur sehr bedingt zu empfehlen. Bei Problemen damit oder mit dem Laden von Klassen ist es hilfreich, erst einmal die Struktur der CL-Hierarchie zu klaeren. 3.16.3 Wie lade ich eine Klasse neu? Autor: Ortwin Glück, Ulf Jaehrig, Patrick Roemer, Jan Schulz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Im Wortsinn: Eigentlich gar nicht. Von 'guten' CLs wird gefordert, dass sie auf Anfragen nach demselben Klassennamen auch stets dasselbe Class-Exemplar liefern.[2] Wenn ein CL sich nicht daran hält, könnte es im System zwei Klassen geben, die per Definition identisch sind, aber eine ganz andere Struktur oder zumindest unterschiedliche Methodendefinitionen haben - ein sicheres Rezept für Ärger. Daher gilt seit Java 1.2: Eine Klasse kann nur entladen werden, wenn der zugehörige CL nicht mehr reachable und damit reif für den GC ist.[3] Demgemäss können Klassen, die durch den Bootstraploader geladen wurden, überhaupt nicht entladen werden. Vorsicht ist im Umgang mit älteren VMs geboten: In Java 1.0 gab es überhaupt kein Class-Unloading; in Java 1.1 konnten Klassen geradezu beliebig entladen werden, mit entsprechend verwirrenden Konsequenzen, z.B. bei Verwendung des Singleton-Patterns.[7] Wie erzeugt man nun trotz alldem den Effekt des 'Neuladens' von Klassen? Man lädt die geänderte Klassendefinition über einen neuen CL. Damit sind Exemplare der alten Klasse inkompatibel zu Exemplaren der neuen Version. Üblicherweise 'vergisst' man beim Neuladen den alten CL, so dass Exemplare der alten Klasse (so sie nicht anderweitig referenziert werden) fällig für den GC werden. Um dieses Auswechseln der Klasse für den Rest der Anwendung transparent zu halten, empfiehlt es sich, diese Klasse hinter einem Interface zu verstecken. Das Interface ist dem übergeordneten CL bekannt und Anfragen danach werden vom Reloading-CL an diesen delegiert, so dass überall in der Anwendung dasselbe Interface bekannt ist. Die 'änderbare' Klasse muss dieses Interface implementieren. Ihre Definition wird dann über einen Reloading-CL geladen und ein Exemplar erzeugt, das dem Rest der Applikation ausschliesslich als Ausprägung des Interface zur Verfügung gestellt wird. Ein Beispiel dieses Vorgehens findet sich in [4] und im Beispielcode. 3.16.4 Wie baue ich einen Plugin-Mechanismus? Autor: Ortwin Glück, Ulf Jaehrig, Patrick Roemer, Jan Schulz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unter einem Plugin-Mechanismus versteht man gemeinhin eine Möglichkeit, zur Compilezeit der Applikation noch unbekannte Klassen, die üblicherweise dasselbe Interface implementieren, zum Applikationsstart (oder irgendwann sonst zur Laufzeit) zu laden. Die Plugin-Klassen (oder entsprechende Jars/Unterverzeichnisse) liegen hierbei oft in einem besonderen Plugin-Verzeichnis, oder die Klassennamen und Positionen im Dateisystem werden der Applikation über eine Konfigurationsdatei mitgeteilt. Zum Applikationsstart werden die Klassen über einen eigenen CL (das kann ein selbstgeschriebener CL oder schlicht ein URLClassLoader[10] sein) geladen und dem Rest der Applikation als Exemplar des übergeordneten Interfacetyps bekanntgemacht. Beispiele für Plugin-Mechanismen findet man in IDEs, Servlet-Containern und Webbrowsern. Beim Entwickeln eines Plugin-Frameworks oder eines Plugins koennen einem schnell fiese CL-Probleme begegnen - [12] und [13] aus dem Eclipse-Wiki liefern etwas Anschauungsmaterial. 3.16.5 Gibt's dazu auch Beispielcode? Autor: Ortwin Glück, Ulf Jaehrig, Patrick Roemer, Jan Schulz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // $HOME/player/PluginPlayer.java import java.io.*; import java.net.*; public class PluginPlayer { public static void main(String[] argv) { try { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); // Plugin-Verzeichnis setzen final File plugindir = new File(argv[0]).getCanonicalFile(); do { // Suche alle .class-Files im Plugin-Verzeichnis File[] pluginfiles = plugindir.listFiles( new FilenameFilter() { public boolean accept(File dir, String name) { return plugindir.equals(dir) && name.endsWith(".class") && name.indexOf("Plugin") > -1; } } ); // Erzeuge einen Loader fuer dieses Verzeichnis URLClassLoader loader = new URLClassLoader(new URL[]{plugindir.toURL()}); // Iteriere ueber alle .class-Files... System.out.println("Running plugins from: " + plugindir.toURL()); int numplugins = pluginfiles.length; for (int plugincnt = 0; plugincnt < numplugins; plugincnt++) { // ...extrahiere den Klassennamen... String filename = pluginfiles[plugincnt].getName(); String pluginname = filename.substring(0, filename.lastIndexOf(".")); System.out.println("Running plugin: " + pluginname); // ...lade die Klasse, erzeuge eine Instanz und caste... Plugin plugin = (Plugin) (loader.loadClass(pluginname).newInstance()); // ...setze den Context-Loader des ausfuehrenden Threads... Thread.currentThread().setContextClassLoader(loader); // ...rufe die entsprechende Methode auf... plugin.doIt(); // ...und setze den Context-Loader zurueck. ClassLoader parentloader = loader.getParent(); Thread.currentThread().setContextClassLoader(parentloader); } System.out.println("'q'[RET] to quit, [RET] to continue"); } while (!"q".equals(in.readLine())); } catch (Exception exc) { exc.printStackTrace(); } } } // $HOME/player/Plugin.java public interface Plugin { void doIt() throws Exception; } // $HOME/plugins/CLTestPlugin.java public class CLTestPlugin implements Plugin { // Laedt Klassen auf unterschiedliche Arten public void doIt() throws ClassNotFoundException { ClassLoader contextcl = Thread.currentThread().getContextClassLoader(); Class ac1 = contextcl.loadClass("AnotherClass"); Class ac2 = Class.forName("AnotherClass"); String firstresult = (ac1 == ac2 ? "fine" :"inconsistent"); System.out.println("Check #1: " + firstresult); ac1 = contextcl.loadClass("Plugin"); ac2 = AnotherClass.class.forName("Plugin"); Class ac3 = Plugin.class; String secondresult = (ac1 == ac2 && ac1 == ac3 ? "fine" :"inconsistent"); System.out.println("Check #2: " + secondresult); } } // $HOME/plugins/AnotherClass.java public class AnotherClass { } // $HOME/plugins/CLResourcePlugin.java import java.io.*; public class CLResourcePlugin implements Plugin { // Laedt einen Text als Ressource und gibt ihn aus public void doIt() throws IOException { InputStream instream = getClass().getResourceAsStream("msg.txt"); BufferedReader inreader = new BufferedReader(new InputStreamReader(instream)); String curline = null; while ((curline = inreader.readLine()) != null) { System.out.println(curline); } inreader.close(); } } // $HOME/plugins/msg.txt Hello, world! // $HOME/plugins/CLListPlugin.java public class CLListPlugin implements Plugin { // Listet alle beteiligten CL public void doIt() { ClassLoader contextcl= Thread.currentThread().getContextClassLoader(); System.out.println("Context CL: " + contextcl); ClassLoader curcl = getClass().getClassLoader(); while (curcl != null) { System.out.println(curcl); curcl = curcl.getParent(); } } } 3.16.6 ClassLoader Ressourcen zu 3.16.1 bis 3.16.5 Autor: Ortwin Glück, Ulf Jaehrig, Patrick Roemer, Jan Schulz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [1] java.lang.ClassLoader API-Doc <URL:http://java.sun.com/j2se/1.4.1/docs/api/java/lang/ClassLoader.html> [2] JLS 12.2 <URL:http://java.sun.com/docs/books/jls/second_edition/html/ execution.doc.html#44459> [3] JLS 12.7 <URL:http://java.sun.com/docs/books/jls/second_edition/html/ execution.doc.html#74294> [4] JavaWorld-Artikel Classloader Java 1.0 Style <URL:http://www.javaworld.com/javaworld/jw-10-1996/jw-10-indepth.html> [5] JavaWorld-Artikel Classloader Java 1.1 Style <URL:http://www.javaworld.com/javaworld/jw-03-2000/jw-03-classload.html> [6] Sun-Tutorial Classloader Java 1.1 Style <URL:http://developer.java.sun.com/developer/onlineTraining/Security/ Fundamentals/magercises/ClassLoader/index.html> [7] JavaWorld-Artikel Classloader und Singletons <URL:http://www.javaworld.com/javatips/jw-javatip52_p.html> [8] JavaWorld-Artikel Typesafe Enumerations <URL:http://www.javaworld.com/javatips/jw-javatip122_p.html> [9] Tomcat Classloader-Howto <URL:http://jakarta.apache.org/tomcat/tomcat-4.1-doc/ class-loader-howto.html> [10] java.net.URLClassLoader API-Doc <URL:http://java.sun.com/j2se/1.4.1/docs/api/java/net/URLClassLoader.html> [11] SPI-Kapitel im Jar-Doc <URL:http://java.sun.com/j2se/1.4.1/docs/guide/jar/ jar.html#Service%20Provider> [12] Eclipse-Wiki Plugin-Development <URL:http://eclipsewiki.swiki.net/114> [13] Eclipse-Wiki Classloader Tricks <URL:http://eclipsewiki.swiki.net/123> 4. Bücher zum Thema Java ======================== 4.1. Kann mir jemand gute Literatur zum Thema Java empfehlen? ------------------------------------------------------------- <URL:http://www.dclj.de/links.html> bietet eine kleine Übersicht über Bücher, die zum freien Download zur Verfügung stehen. Weiterhin bietet die Seite <URL:http://www.accu.org/bookreviews> viele aufschlussreiche Besprechungen von Java-Büchern. Hier nochmal in aller Kürze: * The Java Tutorial - <URL:http://java.sun.com/docs/books/tutorial/> * Java ist auch eine Insel - <URL:http://www.java-tutor.com/> * Go To Java 2 - <URL:http://www.javabuch.de/> * Thinking in Java - <URL:http://www.mindview.net/Books/TIJ/> In Buchform sind die folgenden Werke interessant: * Go To Java 2 - Handbuch der Java-Programmierung Guido Krüger Addison-Wesley ISBN: 382731710X 15. September 2000 1224 Seiten <URL:http://www.javabuch.de/> - Eines der Standardwerke für angehende Javaprogrammierer und Kaffetrinker. Zumindest als Onlineversion sollte es sich jeder mal angeschaut haben, denn es bietet einen umfassenden und dennoch nicht zu sehr ins Detail gehenden Überblick ueber die verschiedenen Komponenten der Sprache Java. * Java ist auch eine Insel - Programmieren für die Java 2-Plattform in der Version 1.4 Christian Ullenboom Galileo Press ISBN: 3898421740 Dezember 2001 1239 Seiten <URL:http://www.java-tutor.com/javabuch/download.htm> * Java. Programmierhandbuch und Referenz für die Java-2-Plattform - Einführung und Kernpakete. Mit CD-ROM. Stefan Middendorf, Reiner Singer dpunkt-Verlag, Heidelberg ISBN 3920993829 1999 1256 Seiten * Java2 Designmuster und Zertifizierungswissen Friedrich Esser Galileo Press ISBN 3934358667 2001 654 Seiten * Einführung in die objektorientierte Programmierung mit Java Ernst-Erich Doberkat, Stefan Dißmann Oldenbourg ISBN 3486247867 2000 315 Seiten * Java Gently Judy M. Bishop Addison-Wesley ISBN 0201342979 1998 508 Seiten * Die Java2 Fibel Ralf Künel Addison-Welsey ISBN 3827314100 1999 442 Seiten * Datenbanken und Java. JDBC, SQLJ und ODMG Gunter Saake, Kai-Uwe Sattler dpunkt-Verlag, Heidelberg 2000 ISBN: 3932588541 * Java Servlet Programming Jason Hunter, William Crawford O'Reilly ISBN 156592391X 1988 510 Seiten * Komponenten in Java: Einsatz und Entwicklung von JavaBeans mit VisualAge for Java Claudia Piemont dpunkt-Verlag ISBN 3932588215 1999 347 Seiten * Design mit Java. Bessere Applets und Anwendungen Peter Coad, Mark Mayfield Markt und Technik ISBN: 3827295866 1999 301 Seiten englischsprachig, aber auch gut: * Effective Java, Programming Language Guide Joshua Bloch The Java Series Addison-Wesley ISBN 0201310058 * Image Processing in Java Douglas A. Lyon Prentice Hall ISBN 0-13-97457707 1999 532 Seiten 5. Themenverwandte Internet Ressourcen ====================================== 5.1. WWW-Sites -------------- Deutschsprachige: Java: + Immer aktuelle archivierte Version dieser Text FAQ <URL:ftp://rtfm.mit.edu/pub/usenet-by-group/ de.answers/de/comp-lang-java/faq> + HTML Version dieser FAQ von Uwe Plonus <URL:http://de.geocities.com/uweplonus/faq/> + deutsche Java-FAQ von Markus Reitz (de.comp.lang.java) <URL:http://www.dclj.de/> <URL:http://www.geocities.com/SiliconValley/Foothills/5270/> + Java-Einfuehrung von Hubert Partl (BOKU Wien) <URL:http://www.boku.ac.at/javaeinf/> + Go to Java 2 von Guido Krueger (Addison Wesley Verlag) <URL:http://www.javabuch.de/> <URL:http://www.gkrueger.com/> + Java ist auch eine Insel von Christian Ullenboom <URL:http://java-tutor.com/> + Java Dokumentation von Brit Schroeter und Johann Plank <URL:http://www.selfjava.de/> + Liste weiterer Links, zusammengestellt von Ralf Geschke (Uni Koeln) <URL:http://infosoc.uni-koeln.de/akademie/java/> HTML, XHTML: <URL:http://www.teamone.de/selfhtml/> <URL:http://www.boku.ac.at/htmleinf/> <URL:http://art2.ph-freiburg.de/HTML-Tutor/> <URL:http://www.netandmore.de/faq/> XML: <URL:http://www.mintert.com/xml/> <URL:http://www.boku.ac.at/htmleinf/xmlkurz.html> WAP und WML: <URL:http://www.boku.ac.at/htmleinf/wein.html> <URL:http://allnetdevices.com/faq/> Englischsprachige: Java: + Java Online-Doku der Firma Sun <URL:http://java.sun.com/docs/> + Java Tutorial der Firma Sun <URL:http://java.sun.com/docs/books/tutorial/index.html> + Java FAQ von Eliotte Rusty Harold <URL:http://sunsite.unc.edu/javafaq/javafaq.html> + Java Programmers FAQ von Peter van der Linden <URL:http://www.afu.com/javafaq.html> + Java Glossary von Roedy Green <URL:http://mindprod.com/gloss.html> + Thinking in Java von Bruce Eckel <URL:http://www.BruceEckel.com/javabook.html> + Java Tutorial von Prof. Baldwin <URL:http://www.phrantic.com/scoop/toc.htm> + Swing FAQ von Linda Radecke <URL:http://www.jalice.net/textfaq.htm> <URL:http://www.jalice.net/tablefaq.htm> <URL:http://www.jalice.net/componentfaq.htm> + Tips zu Java von Marco Schmidt <URL:http://jiu.sourceforge.net/javatips.html> HTML, XML, XHTML u.a.: <URL:http://www.w3.org/> WAP, WML: <URL:http://www.wapforum.org/> Anmerkung: Diese Liste ist meine subjektive Auswahl und stellt keinen Anspruch auf Vollstaendigkeit oder Objektivitaet. 5.2. Newsgroups --------------- <news:comp.lang.java.3d> <news:comp.lang.java.advocacy> <news:comp.lang.java.beans> <news:comp.lang.java.corba> <news:comp.lang.java.databases> <news:comp.lang.java.gui> <news:comp.lang.java.help> <news:comp.lang.java.machine> <news:comp.lang.java.programmer> <news:comp.lang.java.security> <news:comp.lang.java.softwaretools> 5.3. Mailinglisten ------------------ <mailto:java-linux@java.blackdown.org> <mailto:nbusers@netbeans.org> 6. JavaScript Internet Ressourcen. ================================== 6.1 WWW-Sites <URL:http://www.teamone.de/selfhtml/> <URL:http://www.dcljs.de/> 6.2. Newsgroups <news:de.comp.lang.javascript> 7. Credits ========== Folgende Personen waren an der Erstellung der FAQ beteiligt: Werner Baumann, Gerhard Bloch, Frank Buss, Paul Ebermann, Alexander Elsholz, Martin Erren, Uwe Günther, Erwin Hoffmann, Ingo R. Homann, Ulf Jährig, Christian Kaufhold, Georg Lipitsch, Peter Luschny, Stephan Menzel, Alexander Merkelbach, Michael Paap, Hubert Partl, Achim Peters, Uwe Plonus, Sascha Raabe, Markus Reitz, Aljoscha Rittner, Wolfram Rühaak, Joachim Sauer, Wolfgang Schirmer, Marco Schmidt, Michael Schmidt, Karsten Schulz, Roger Schuster, Jochen Theodorou, Tobias Vogele, Christian Wederhake (in alphabetischer Reihenfolge). Die Version 1.0 dieser FAQ baut auf den FAQs von Hubert Partl, Markus Reitz und Michael Schmidt auf. Kritik und Verbesserungsvorschläge an der FAQ bitte direkt an den Autor oder in die Newsgroup mit dem Tag [FAQ] im Subject. User Contributions:
[ Usenet FAQs | Web FAQs | Documents | RFC Index ]
Send corrections/additions to the FAQ Maintainer: uwe@cscc.de (Uwe Guenther)
Last Update March 27 2014 @ 02:11 PM
|
Comment about this article, ask questions, or add new information about this topic: