Albrecht Weinert Labor für Medien und verteilte Anwendungen (MEVA-Lab) Fachbereich Et. & Informatik (FB3) der Hochschule Bochum Java --- Tipps und Tricks =============================== V0.01 (20.06.2001 17:43) : Aktualisierungen V0.02 (20.06.2001 17:43) : JDK - Installation nach java-install.txt ausgegliedert V0.03 (03.04.2002 09:54) : Kapitel 7 V1.02 (05.12.2006 10:52) : RXTX-Hinweise, Straffungen. V1.04 (20.12.2006 10:30) : Kapitel sortiert, native dazu V1.05 (16.01.2007 12:01) : SerNimpl dazu; JNI auf BCC5 umgestellt V1.02 (05.06.2006 14:43) : kl. Korr und cvs-Fehler (V-Sprung) V.22 (02.12.2008 17:22) : Revisionssprung wg. Subversion-Umstellung V.37+ (08.12.2008 15:50) : Kleinigkeiten V.05+ (17.12.2009 12:16) : Revisionssprung SVN-bedingt, Aktualisierung Version V.1 Zuletzt geändert von A. Weinert am 23.09.2016 Copyright (c) 2008 Albrecht Weinert All rights reserved. a-weinert.de Inhalt 1. Welches JDK, JDK-Installation 2. Editor, IDE 3. StringBuffer Editing 4. Rezept für equals() 5. Komplizierte Konstantenberechnung in Interfaces - Kein Problem 6. Ein-/Ausgabe auf Ports 6a Java-COMM-Extensions installieren 6b RXTX installieren 6c Javax, RXTX oder .. 7. Java - Performance 8. Java native - Einstieg für Win32 Hinweis: Dieser Text ist wie ähnliche in diesem Verzeichnis eine etwas lose Sammlung von Tipps und Hinweisen. Sie erwiesen sich in der täglichen (eigenen) Arbeit und in der Beratung als nützlich. Manchmal wird ja drei Tage nach Antworten für etwas gesucht, das in zehn Minuten gehen müsste. Diese "losen Sammlungen" sind auch nie wirklich "fertig". Fragen und Anregungen sind willkommen. 1. Welches JDK, JDK-Installation ================================= Dieses Kapitel finden Sie in der Datei java-install.txt (hier im selben Verzeichnis; Einstieg: http://a-weinert.de/pub . 2. Editor, IDE =============== Zum Arbeiten mit Java benötigen Sie neben den JDK-Werkzeugen, also dem JDK bzw, JSDK von SUN) nur noch * einen Browser (für die Dokumentation und evtl. auch Applets etc.) und * einen (ASCII-) Editor. Von Microsofts Notepad ist hier ausdrücklich abzuraten. Es gibt Versionen, die Java-Quellen heimlich derart verderben, dass sie nur noch mit Microsoft- Werkzeugen zu lesen sind. Hier kann man nun schon seit 10 Jahren EditPad empfehlen, der auch ein vollwertiger NotePad-Ersatz ist. Von Kleinstprojekten abgesehen wird man aber mit einer Integrierte Java- Entwicklungsumgebung (IDE) arbeiten -- und auch mit einer Versionsverwaltung (cvsnt-tipp.txt im selben Verzeichnis wie dieses Dokument ist etwas alt, mag aber nützliche Hinweise geben). Viele IDEs kosten viel und nicht alle sind ihr Geld wert. Mit einigen bekommt man sogar Lizenzprobleme beim Ausliefern eigener Software an Kunden -- und zwar genau von der Sorte, die man durch den Einsatz von Java vermeidet. Und die Hoffnung "Java programmieren ohne Java-Kenntnisse" mag mancher versprechen, aber keine IDE kann sie letztlich halten: With or without a tool -- a fool is still a fool. Dies gesagt, kann man eine IDE -- von Profis (ursprünglich IBM) für Profis gemacht -- uneingeschränkt empfehlen: Eclipse 3.5.0 oder ggf. höher und am besten gleich gepackt mit den Web-Applikationserweiterungen. Eine Eclipse-Alternative wäre NetBeans von Sun oder gleich darauf aufsetzend J2EE-Studio 8. 3. StringBuffer Editing ======================= Jedem der objektorientiert arbeitet, sollte der wichtige Unterschied von Klassen mit unveränderbaren Objekten, wie java.lang.String und de.frame4j.math.Comlex, und solchen mit veränderbaren Objekten, wie java.lang.StringBuffer bzw. StringBuilder, in allen Konsequenzen klar sein. String und StringBuffer bzw. StringBuilder sind ein hierfür gut geeignetes Beispielpaar (und CharSequence der zugehörige gemeinsame Typ). Entscheidend ist, dass jede Änderung an Strings neue String- (Wegwerf-) Objekte erzeugt. Daher sollte man kompliziertere "Stringbasteleien" immer mit einem StringBuffer oder StringBuilder machen, den man dann ganz zuletzt in einen String wandelt. Diese Wandlung ist praktisch umsonst -- aber nur, falls man den StringBuffer oder StringBuilder fürderhin NICHT MEHR anfasst. Kleines Beispiel (aus Framework Frame4J: de.frame4j.time.TimeHelper): public static String formatDIN(final ZonedDateTime zoDtTi){ StringBuilder bastel = new StringBuilder(20); /// *) Anmerkung 1 bastel.append(TextHelper.twoDigit(zoDtTi.getDayOfMonth())).append('.'); bastel.append(TextHelper.twoDigit(zoDtTi.getMonthValue())).append('.'); bastel.append(zoDtTi.getYear()).append(' '); return formHH_MMSS(zoDtTi, bastel).toString(); } // formatDIN(ZonedDateTime) Außer dem im Beispiel verwendeten append() haben StringBuffer und StringBuilder noch zahlreiche nützliche Änderungsmethoden, die, wie gesagt, im Gegensatz zu String keine neuen Objekte erzeugen. Anmerkung *1): Falls man -- hoffentlich -- JDK >= 1.5.x (Java >= 5) einsetzt, sollte man die Leistung einfach durch Verwendung von StringBuilder statt StringBuffer weiter steigern (es sei denn man benötigt die Thread-Sicherheit von StrinBuffer wirklich). 4. Rezept für equals() ======================= Jede "anständige" Klasse (fast jede), die die Methode equals() direkt oder indirekt von java.lang.Object erbt, sollte diese überschreiben. Hierbei sind zwei Fälle zu unterscheiden: A) Dieses equals() ist ausdrücklich oder durch die Klasse endgültig. B) Dieses equals() kann durch weitere Vererbung überschrieben werden und dabei kann man auch (super.equals()) verwenden. Fall A): class MeineKlasse extends Object { //..... public final boolean equals(Object other) { if (!(other instanceof MeineKlasse)) return false; if (other == this) return true; // Standardanfang / Klassenspezifische Inhaltsvergleiche return ((MeineKlasse)other).fields == fields; } // equals() //...... } // class MeineKlasse Fall B): class MeineKlasse extends Object { //..... public boolean equals(Object other) { if (other == null || other.getClass() != this.getClass()) return false; if (other == this) return true; // Standardanfang / Klassenspezifische Inhaltsvergleiche return ((MeineKlasse)other).fields == fields; } // equals() //...... } // class MeineKlasse Bei weiterer Vererbung geht man am besten so vor: class MeineZweiteKlasse extends MeineKlasse { //..... public boolean equals(Object other) { if (! super.equals(other)) return false; // erledigt null, // Typ und geerbte Felder // Standardanfang / Klassenspezifische Inhaltsvergleiche return ((MeineZweiteKlasse)other).extraFields == extraFields; } // equals() //...... } // class MeineZweiteKlasse 5. Komplizierte Konstantenberechnung in Interfaces -- Kein Problem =================================================================== Gelegentlich steht man vor der Aufgabe, in Interfaces zur Laufzeit sogenannte Konstanten (sprich in Java final Variablen) zu berechnen, deren Wert nicht in einfachen Ausdrücken darzustellen ist. Dies gilt insbesondere dann, wenn bei der Berechnung Ausnahmen abzufangen und zu behandeln sind. Zur Erinnerung: a) Alle Variablen in einem Interface sind public static final, auch wenn man es nicht ausdrücklich hinschreibt. b) Statische Blöcke (in Klassen i.A. das Mittel der Wahl in solchen Fällen) sind in Interfaces verboten. Das folgende Beispiel ist sinngemäß ein kleiner Auszug aus entsprechenden Teilen der Interfaces des Frameworks Frame4J (de.frame4j...). Wie man sieht, hilft hier eine kleine eingebettete statische Hilfsklasse mit statischen Methoden zur Konstantenberechnung. Diese kann nun beliebig kompliziert ausfallen, einschließlich, wie im Beispiel, einer Ausnahmebehandlung. public interface ComVar { /** Die Systemeigenschaft "file.separator".
*
* Es ist entweder "\" für DOS-Windows oder "/" * für alle anderen Systeme (Unix).
*
* Default: "\" * Der Defaultwert wird genommen, falls Zugriff auf System-Property * (wie gelegentlich bei Applets, SecurityException) nicht möglich ist.
*/ public static final String FS = Helperle.givFS(); /** Interne Hilfsklasse. */ static class Helperle { // public by default (in interfaces) private Helperle(){} // no Helperle-Objects static String givFS() { // kann ebenso gut private sein String fs = null; try { fs = System.getProperty("file.separator"); } catch (Exception e) { fs = "\\"; } return fs; } // String givFS() } // class Helperle } // interface GemConst Die in ein Interface eingebettete Klasse, also Helperle im Beispiel, ist wie alles in Interfaces zwangsweise public. Man kann aber das (hier unsinnige und unerwünschte) Erzeugen eines Objekts, durch einen privaten Konstruktor verhindern. (Damit "erschlägt" man auch gleich die unsinnige, leere javadoc-Dokumentation des sonst gegebenen default-Konstruktors.) Ebenso ließen sich hier, vom Beispiel abweichend, auch die Methoden private machen. Im obigen Falle hätte man die Methode givFS() (als harmlos) auch public machen können, womit dann ein javadoc-Kommentar fällig geworden wäre. Hier liefert ComVar.Helperle.givFS() aber nichts, was man mit ComVar.Helperle.FS nicht "billiger" bekäme. Hinweis zu Java8: Java8 erlaubt statische Methoden in interfaces. Insofern ist die obige eingebettete Beispielklasse Helperle nicht mehr nötig -- es sei denn man mag diese Hilfsmethoden nicht public haben. 6. Ein-/Ausgabe auf Ports ========================== Nicht nur die Plattformunabhängigkeit verbietet die direkte hardwarenahe Ansteuerung von Ein-/Ausgaberegistern (E/A, I/O) etwa nach Art etwa des port-arrays einiger Pascal-Versionen. Hierzu muss man die Plattformunabhängigkeit verlassen und mit native methods (JNI) arbeiten oder auch ein in einer maschinennäheren Sprache (C, ASM) geschriebenes Mini-E/A- Programm mit System.exec() "an der Leine führen". Der Wunsch nach Nutzung vielfach vorhandener und von den unterlagerten Betriebssystemen (per Treiber und API) gestützter Standardschnittstellen, wie Ethernet, USB, parallel, seriell etc., plattformunabhängig mit Java ist hingegen legitim. Leider hat SUN dies ordentlich nur für Ethernet (TCP, IP etc. mit dem Paket java.net) verwirklicht. Aus dem Spektrum der anderen Schnittstellen fehlt -- auch heute noch -- für viele industrielle (HART-Protokoll) und andere Steuerungsaufgaben vor allem die serielle Schnittstelle (V.24, RS232 etc.; COM1, COM2 unter Windows). Die Ein-/Ausgabe auf Standard-Ports (bei Windows COM1, COM2, LPT1 usw. genannt) wurde von SUN mit den sogenannten COMM-Extensions (Paket javax.comm) recht früh (ca. 1998?) aber in Java möglich. Der Ansatz ist objektorientiert und auf der Ebene auswechselbarer JNI-Implementierungen und Properties- Dateien plattformunabhängig. Nachteilig ist, dass - die Objektorientierung handwerklich schlecht gemacht (keine Generalisierungen durch Interfaces u.A.) und - die Java-Comm-Extensions nie (wie z.B. XML und vieles Andere) JDK-/JRE-Standard wurden, sondern eher schlecht als recht unterstützt bzw. bis 2006 nie gepflegt wurden. Diese Situation hat zu vielen alternativen Ansätzen geführt von denen RXTX (Paket gnu.io) javaoberflächlich zu javax.comm gleich ist. Kopierte Methodensignaturen und Semantik nützen nur bedingt. Man muss alle Anwendungsquellen anfassen und beim Umschalten zumindest javax.comm mit gnu.io tauschen. Der Fehler liegt aber bei SUNs oben erwähnter fehlender OO- Generalisierung. Die auf den ersten Blick umständlich erscheinende Handhabung von javax.comm und wie gesagt fast gleich RXTX kann man am Beispiel der Anwendung ShowPorts sehen. Diese Anwendung listet alle für die COMM-extensions und / oder RXTX erreichbare Ports und gibt ein paar Zustandsinfos zu ihnen. javax.comm und / oder RXTX müssen natürlich installiert sein; siehe weiter unten unter 6a und 6b. Wenn Sie das Framework Frame4J (de.frame4j...), siehe http://www.frame4j.de/, installiert haben, haben Sie auch diese Testanwendung. Die Quelle ShowPorts.java steht zur Verfügung, Frame4J open source ist. 6a. Java-IO-Extensions installieren =================================== Die COMM-Extensions sind, wie der Name schon andeutet, Erweiterungen, die (leider) nicht zum Standard-Lieferumfang des JDK gehören. Man muss sie und ihre Dokumentation aus den Dateien javacomm20-win32.zip (Code und Doku zusammen, ebenfalls in meva-lab/tools) entpacken und installieren. Da die mitgelieferte Anleitung in PlatformSpecific.html (mitgeliefert) etwas kryptisch und teilweise auch irreführend ist, hier die Schritte, die für jedes installierte JDK bei der Ergänzung um die COMM-Extensions zu tun sind: 1.) Die Datei wincom32.dll kommt in das JRE-bin-Verzeichnis also beispielsweise nach ...\jdk\jre\bin . 2.) Die Datei comm.jar kommt in das JRE\lib\ext-Verzeichnis also beispielsweise nach C:\Programme\jdk\jre\lib\ext . Diese Datei darf dort nicht entpackt werden. Durch diese Installation verwenden die Werkzeuge java, javac usw. ab 1.2.2 die comm-Extensions als sogenannte "installed extensions" automatisch. Eclipse bekommt so was auch mit, evtl. aber erst nach Neusetzen (auf den selben) JRE (preferences/java/...). N.b.: Ab Java 2 sollte man den Klassenpfad in Ruhe lassen. 3.) Die Datei javax.comm.properties kommt in das JRE-lib-Verzeichnis also beispielsweise nach C:\Programme\jdk\jre\lib . 4.) Die Comm-Dokumentation kann man als neues Unterverzeichnis zum JDK-docs-Verzeichnis hinzufügen. Der Vorgang ist in java-install.txt (aus der selben Quelle wie diese Datei) mit allen Tipps und Hinweisen auf Fallen und Fehlermöglichkeiten genau beschrieben. 6b. RXTX-IO-Extensions installieren =================================== Zu den von Sun gelieferten und uralt (aber immerhin) javadoc-dokumentierten comm-extensions gibt es eine RXTX genannte undokumentierte Alternative. RXTX hat sich aus bescheidenen (und durchaus unbrauchbaren nur LINUX-) Anfängen über die Jahre zu einem vollwertigen und in Teilaspekten besseren Ersatz für javax.comm entwickelt. Für RXTX unter Windows braucht diese Dateien oder neuere: 30.01.2006 00:23 59.464 RXTXcomm.jar 30.01.2006 00:23 47.421 rxtxParallel.dll 01.03.2006 12:01 77.759 rxtxSerial.dll (Die javadoc ist praktisch nicht vorhandenen und kann ignoriert werden. 80% der Spezies open source-Programmierer dokumentieren nicht oder schlecht.) 1.) Die Datei RXTXcomm.jar kommt in das JRE\lib\ext-Verzeichnis also beispielsweise nach C:\Programme\jdk\jre\lib\ext . 2.) Die beiden .dll-Dateien kommen in das JRE-bin-Verzeichnis also beispielsweise nach ...\jdk\jre\bin . RXTX (open source) hat eine lange Geschichte, die auch absolut ungeeignete und unprofessionelle Ansätze kannte. Unter Windows ist die Version 2.1.6 einsetzbar und sie stellt eine gute Arbeit (von Trent Jarvi) dar. Zu den nach wie vor bestehenden (leider bei open source verbreiteten) Mängeln bleibt leider: - Missachtung aller vernünftigen Java-Namens- (Beans-) und Stil-Konventionen, - Copy und Paste statt Vererbung / Generalisierung (auch innerhalb von gnu.io), - unnötige Mehrfachausführungen von Bibliotheksladen, Betriebssystemklärung u.A., - unklares (chaotisches bzw. gar kein) Logging-Konzept -- mal System.out mal eine komplexe Klasse Zystem und vor allem - das Fehlen jeglicher Dokumentation. Ein java-mäßig verbesserte, gestraffte, performantere und sogar teilweise dokumentierte Weiterentwicklung der Java-Quellen ist vielfach gemacht bzw. versucht worden -- mit sehr unterschiedlichen Erfolgen. Solange as um die serielle Schnittstelle unter Windows (für industrielle Steuerungsaufgaben z.B.) ist das Nachfolgende der viel leistungsfähigere, stabilere und flexiblere Weg. 6c. RXTX oder javax.comm oder ... ================================= Eigenschaften, Performance und Problemlast der beiden hier erwähnten wesentlichen IO-Extensions sind unterschiedlich. Es gibt leider kein eindeutiges "besser oder schlechter". Je nach Anwendung, Chip-Satz der Schnittstellen kann es vorkommen, dass einer der beiden Ansätze nicht oder unbefriedigend läuft und der andere OK ist. Ein Grund von javax zu RXTX oder sogar zu ganz anderen Ansätzen zu gehen, liegt darin, dass SUN im Jahr 2006 zwar endlich(!) seine Comm-Extensions weiterpflegte, dabei aber -- gelinde gesagt absolut unverständlich -- die Unterstützung für Windows total über Bord warf. So gibt es vielleicht was Besseres -- aber es ist de facto weg. ------------------------I---- | de.a_weinert.io.SerialDefs | ----------------------------- | ------------------------------------ | | ------------------------ --------------------------- | de.a_weinert.io.SerIO | | de.a_weinert.io.SerNimpl | ------------------------ --------------------------- Ein OO-naher Ansatz, der sich in diesem Sinne nun vom javax.comm und damit auch von RXTX verabschiedet, ist die Klasse SerNimpl, die das Interface SerialDefs implementiert. Die das gleiche Interface implementierende Klasse SerIO stützt sich auf javax.comm. Eine RXTX-gestützte Variante von SerIO ist praktisch mit Copy und Paste erstellbar, wobei die meiste Mühe dann in den javadoc- Kommentaren steckt. --------------------------- ---------------------------- | de.a_weinert.io.SerNimpl |o---| de.a_weinert.io.WinDoesIt | --------------------------- |------------------- | | JNI-Lib: bsDoesItNative | ---------------------------- Die Klasse SerNimpl, hingegen verlässt teilweise die javax.comm/RXTX- Gewohnheiten und bietet nur die serielle Schnittstelle. Sie bildet weitgehend (und oft 1zu1) die weitergehenden Möglichkeiten der Betriebssystem-API mit JNI ab. Für Windows ist dies mit 08.01.2007 10:38 51.712 bsDoesItNative.dll 13.01.2007 11:17 17.132 WinDoesIt.cpp implementiert. Die dll muss erreichbar in einem Java-bin-Verzeichnis oder auch in einem entsprechenden Windows-Verzeichnis ("im PATH") installiert werden. Mit den neueren Framework-Versionen (de.a_weinert.. ab E2006; erg.zip und nun Frame4J) bekommen sie (automatisch) auch diese Möglichkeit. Weiteres siehe auch unter http://www.frame4j.de/ 7. Java - Performance ====================== Es kommt vor, dass eine Java-Anwendung zu langsam läuft oder dass dieselbe Anwendung auf einem schlechter ausgestatteten Rechner total "stehenbleibt". Man greift zum Debugger, um die vermutete Endlosschleife aufzuspüren, doch im Debugger (jdb) läuft die Anwendung "gemeinerweise" durch. Außerdem gilt ja eh: Wer 100% Java vernünftig macht, braucht nie einen Debugger. Wirklich! Der geschilderte Effekt deutet auf ein Ressourcenproblem hin, das der niederpriore garbage collection thread nicht hinreichend handhabt. Hier helfen oft zwei Maßnahmen (einzeln oder beide zusammen): 1.) Ausdrückliches auf null Setzen von nicht mehr benötigten Referenzen. 2.) Gelegentliches direktes Starten der garbage collection. Im Allgemeinen ist das Grundproblem ein ungeeigneter (schlechter) Algorithmus oft gepaart mit dem großzügigen Erzeugen von Wegwerfobjekten. Performanceprobleme können auch durch ungeeignete Verwendung von Containern und von synchronized kommen. Solche Situationen erfordern fachlich fundierte Analyse. Auf gar keinen Fall nehme man Performance-Probleme leichtfertig als Grund für Java native (nächstes Kapitel). Der bessere Anschluss an Betriebssystem- und Hardware (IO, voriges) Kapitel ist was ganz Anderes. 8. Java native - Einstieg für Win32 ==================================== Bei Problemen mit Performance, Schnittstellen und der bewussten Ausnutzung von Plattformspezifika greift man gerne zu native methods (JNI). Bevor man dies tut, sollte man wirklich alle möglichen (100%) Java-Lösungen ausgelotet bzw. ausgeschlossen haben. Wer dank JNI seit Jahren zum ersten mal wieder C lesen oder gar machen muss, dankt erst mal Java (Sun) aus vollem Herzen: "Wie konnte ich früher mal nur mit so was Furchtbaren ..." Neben dieser C-Hürde treten oft noch dll-Link-Probleme auf, vor Allem mit GNU- / MinGW (open source) C. Und plötzlich liegt die JNI-Einstiegshürde -- wenn man es nun schon nehmen will oder muss -- ganz hoch. Schon der "Lehrbucheinstieg" misslingt -- und die Projektlösung rückt in weite Ferne. Hier nun die nur diese Einstiegsschritte für Windows32 mit dem (ohne IDE) frei erhältlichen Borland-C++, Version 5.5: 06.02.2006 13:41 8.935.988 freecommandLinetools.exe Schritt 0, C-Werkzeuge installieren und einstellen: =========== Wenn C/C++ regelmäßig genutzt wird, mag dies bereits geschehen sein. Benötigt werden diese Vorbereitungen erst für Schritt 6 (unten). Installieren Sie Borland C++ 5.5 unter Windows mit der o.a. Installationsdatei freecommandLinetools.exe. Als Installationspfad können Sie das vorgeschlagene C:\BCC55 lassen oder der guten Ordnung halber C:\Programme\BCC55 wählen. Zum Arbeiten mit den Werkzeugen müssen Sie den Pfad (PATH) um C:\BCC55\bin bzw. C:\Programme\BCC55\bin ergänzen. Wenn Sie viel damit arbeiten wollen, tun Sie das einmalig in den Systemumgebungsvariablen mit der Systemsteuerung, ansonsten jeweils bei Bedarf (vor Schritt 6 unten) mit einem Befehl D:\weinert\javaFactory>set path=%PATH%;C:\[Programme\]BCC5\bin Erstellen Sie (mit EditPad) im BCC55\bin-Verzeichnis zwei kleine TextDateien 03.01.2007 11:32 158 bcc32.cfg 03.01.2007 11:28 60 ilink32.cfg mit folgendem Inhalt: ----bcc32.cfg: -I"c:\Borland\bcc55\include" -I"c:\programme\jdk\include;c:\programme\jdk\include\win32" -L"c:\Borland\bcc55\lib;c:\Borland\bcc55\lib\psdk" ----- -----ilink32.cfg: -L"c:\Borland\Bcc55\lib;c:\Borland\Bcc55\lib\psdk" -E24 ----- Die Pfadangaben C:\.... müssen natürlich zu Ihrer jeweiligen Installation stimmend gemacht werden. Nun ist Borland C++ (V5.5) für Java- bzw. JNI-Zwecke startklar. Schritt 1, der Java-Teil (Auszug): ============= ----- package de.a_weinert.io; public class NimplTest { static { System.loadLibrary("nimplTesteNative"); } public native void setRcvThr(int rcvThr); public native int getRcvThr(); } ----- Schritt 2, Nutzen bzw. dieser native Methods aus Schritt 1, in Java (Auszug): ================================= Der Test erfolgt in der Methode in doIt() eines Erben von de.a_weinert.App. ---Test.java (Auszug): /** Die eigentliche Arbeit des Programms.
*/ public int doIt() { log.println(); log.println(zweiZeilStartMld().append('\n')); NimplTest nimplTest = new NimplTest(); log.println("\n //// nimplTest.setRcvThr(99)"); nimplTest.setRcvThr(99); int back = nimplTest.getRcvThr(); log.println("\n //// nimplTest.getRcvThr() = " + back); return 0; } // doIt() --- Das Ergebnis des Laufs der erfolgreich übersetzten Anwendung ist: --- Exception in thread "main" java.lang.UnsatisfiedLinkError: no nimplTesteNative in java.library.path at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1682) ............. at Test.doIt(Test.java:163) at de.a_weinert.App.go(App.java:2397) at de.a_weinert.App.go(App.java:2248) at Test.main(Test.java:65) --- Nun, das war natürlich zu erwarten, da noch keine .dll hergestellt wurde, sprich also die verwendeten native methods nicht implementiert sind. Schritt 3, Erzeugen der .h-Datei: ===================== Der Befehl D:\weinert\javaFactory>javah de.a_weinert.io.NimplTest erzeugt die Datei 19.12.2006 14:56 658 de_a_weinert_io_NimplTest.h Man beginnt zu ahnen, wie Java-Paketstrukturen in C-Namen umgesetzt werden. Als erstes benennt man die Datei (passend zur reverse URL-Paket-Benennung) in de_a-weinert_io_NimplTest.h um. Schritt 4, das Erzeugen der .c-Datei: ========================= Bis jetzt war es (aus Java-Sicht) Spaß. Nun beginnt die C-Programmierung. Der Einfacheinstieg ist das Umkopieren der vom Java-Werkzeug generierten C-Header-Datei. .h kopieren und umschreiben in de_a-weinert_io_NimplTest.cpp. ---de_a-weinert_io_NimplTest.cpp: #include "de_a-weinert_io_NimplTest.h" /* Aus .h umgeneriert */ jint val; /* * Class: de_a_weinert_io_NimplTest * Method: void setRcvThr(int) * Signature: (I)V */ JNIEXPORT void JNICALL Java_de_a_1weinert_io_NimplTest_setRcvThr (JNIEnv *env, jobject theObj, jint setVal) { val = setVal; } /* * Class: de_a_weinert_io_NimplTest * Method: int getRcvThr() * Signature: ()I */ JNIEXPORT jint JNICALL Java_de_a_1weinert_io_NimplTest_getRcvThr (JNIEnv *env, jobject theObj){ return val; } ---- Nun ist die C-Quelle da. Es ist offensichtlich ein per se unsinniger Einstiegstest, bei dem der Parameterwert der set-Prozedur (in einer C-Variable) gemerkt und von der get-Funktion wieder geliefert wird. Schritt 5, Erzeugen der .def-Datei: ======================= Obgleich nicht jeder Linker darauf angewiesen ist, sollte man die öffentliche Schnittstelle der zu erzeugenden DLL in einer .def darstellen; sie ist leicht aus der .h "zusammenzustreichen" bzw. jeweils zu ergänzen. Mit BCC läuft so vieles robuster und manche Fehler werden schon bei der Erstellung statt im Betrieb aufgedeckt. (Und beim -- auch möglichen -- Einsatz von MinGW-/GNU-Werkzeugen bringt man erfahrungsgemäß ohne eine .def-Datei nur fehlerhafte DLLs zustande.) ---de_a-weinert_io_NimplTest.def: EXPORTS Java_de_a_1weinert_io_NimplTest_setRcvThr Java_de_a_1weinert_io_NimplTest_getRcvThr --- Das ist Alles; also einfach eine vollständige Liste, der C/C++-Funktionen auf die sich die JNI-Implementierung stützt. Schritt 6, Übersetzen und DLL erzeugen: ================== Nach den Vorbereitungen des Schritts 0 (oben) ist die Sache mit BCC55 schon erschreckend einfach: bcc32 -tWM -tWD de_a-weinert_io_NimplTest.cpp del nimplTesteNative.dll ren nimplTesteNative.dll de_a-weinert_io_NimplTest.dll Anmerkung: Dies geht auch ohne den Sinn der Optionsparameter oder der in Schritt 0 gemachten Einstellungen in den .cfg-Dateien verstehen zu müssen. Aber wehe, wann daran irgend etwas falsch ist. Für Tests kann die erzeugte nimplTesteNative.dll i.A. im aktuellen Verzeichnis bleiben, ansonsten wäre sie noch in ein Java-bin-Verzeichnis oder ein Windows-PATH-Verzeichnis zu schieben. Mit dieser .dll läuft nun auch Test.java mit dem offensichtlich C-/C++/.dll-bedingtem Ergebnis: D:\weinert\javaFactory>java Test /// Test V02.58 (19.12.2006, A.Weinert) /// Start: Di, 19.12.2006, 18:10:22 //// nimplTest.setRcvThr(99) //// nimplTest.getRcvThr() = 99 Dies ist der Einstieg in "native methods" (JNI) für Win32 mit BCC55 -- nicht mehr, aber auch nicht weniger.