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.