Java, wie Dateien in andere Dateien einbinden?

3 Antworten

Java-Dateien liegen in einem Verzeichnisbaum.

Die package-Deklaration gibt den Ordner an, in dem die Datei liegt.

Dateiname = (public) Klassenname. Es dürfen aber auch andere (private) Klassen drinstehen.

Java-Dateien werden alle in einem Rutsch compiliert (*.java ⇒ *.class). Der Compiler heißt javac. Mit --classpath oder -cp gibst Du das Wurzelverzeichnis Deiner Quelltexte und ggf. noch jar-Dateien (Bibliotheken) an. Mit -d bestimmst Du das Zielverzeichnis für die class-Dateien. Nimm eine RAM-Disk, um Deine SSD zu schonen. Die Optionen Xlint:xxx aktivieren bessere Warnungen.

Mit java startest Du das Programm. Mit -cp sagst Du, wo die class-Dateien und Bibliotheken liegen. Dahinter kommt die Klasse mit einer main()-Methode. Für Schulaufgaben gibt es die Möglichkeit, eine einzelne (!) Java-Datei anzugeben. Die wird dann schnell noch kompiliert. Die Option -ea aktiviert Assertions.

Das sieht dann etwa so aus:

export JAVA_CLASSES=$XDG_RUNTIME_DIR/java
mkdir $JAVA_CLASSES

javac -d $JAVA_CLASSES -cp src:lib/\* \
    -Xlint:deprecation -Xlint:unchecked -Xlint:path
    src/test/*.java src/test/p1/*.java

java -cp $JAVA_CLASSES:lib/\* -ea \
    test.JavaMain arg1 arg2 ...

1) Ein Package stellt einen Ordner dar. Deine Projektstruktur müsste folglich so aussehen:

/JavaMain.java
/p1
/p1/JavaSecond.java

2) So wie die Hauptklasse in einer Datei (bzw. die explizit sichtbare Klasse in einer Datei) heißt, so muss auch die Java-Datei heißen.

Deine Klasse Test müsstest du demzufolge in JavaSecond umbenennen. Der Konstruktor erhält natürlich denselben Namen wie die Klasse.

3) Um ein Objekt von JavaSecond anzulegen, brauchst du den new-Operator. Es werden nicht so wie in C++ automatisch Instanzen des Objekts kreiert.

JavaSecond test = new JavaSecond();

Valentin1720653 
Fragesteller
 20.05.2022, 23:08

okay vielen Dank dir.

Das ist nicht sonderlich "schwer" nur etwas "ungewohnt" wenn man von c++ kommt.

Eine Frage noch, gibt es in Java eine Alternative zu make, oder kann man make auch mit java verwenden ?

0
regex9  20.05.2022, 23:12
@Valentin1720653

In Java kannst du entweder Maven oder Gradle verwenden (abseits davon gibt es noch weitere Alternativen, die beiden sind aber wohl die bekanntesten), um deine Projektabhängigkeiten zu verwalten. Je nachdem, mit welcher IDE du arbeitest, gibt es Projekttemplates, die dir beim Anlegen eines Projekts auch schon Maven/Gradle vorbereiten.

0
ArchEnema  20.05.2022, 23:14
@Valentin1720653

Niemand hält dich davon ab make mit Java zu verwenden. Allerdings ist es ziemlich sinnlos, weil der Java-Compiler (javac) bereits schlau genug ist nur die aktualisierten Klassen neu zu kompilieren.

Interessanter ist Dependency-Management (Fremdbibliotheken einbinden), z.b. mit Maven.

1

Kleiner Tipp am Rande: Die rot unterkritzelten Programmteile mag der Compiler nicht. Da ist was faul. Wenn du mit der Maus drüberfährst sollte eine Fehlerinfo angezeigt werden.

Compiler-Fehlermeldungen sind zwar oft etwas seltsam formuliert (kennst du von C++, da ist das noch viel schlimmer), aber sie sagen ziemlich genau was falsch ist!


Valentin1720653 
Fragesteller
 20.05.2022, 23:24

Ich habe jetzt die Ordnerstruktur so angelegt wie in dem oberen Kommentar gefordert, bekomme beim Kompillieren aber immernoch einen Fehler dass das package nicht existiere...

Meine Ordnerstruktur ist jetzt folgende:

/Java // Projektordner

/Java/A.java // Main Datei

/Java/p1/B.java // Die einzubindende Klasse

In meiner A.java Datei steht jetzt oben

import p1.B;

in B.java heißt die Datei noch immer genau wie die Klasse und der Konstruktor, gleiches gilt für die Klasse A und und Dateiname von A.

Trotzdem bekomme ich den Fehler des Compilers wenn ich A.java kompillieren will dass er p1.B nicht kenne. Irgendeine Idee woran das liegen kann ?

0
ArchEnema  20.05.2022, 23:28
@Valentin1720653

Nein, muss man nicht. Das passiert dynamisch zur Laufzeit.

Hast du denn die anderen Compilerfehler in Second bzw. B ausgebessert? x und y musst du global definieren, so wie du sie verwendest.

Vielleicht auch ein IDE-Problem. Ich gehe davon aus, dass die IDE (VS Code?) nicht einfach den normalen javac verwendet.

0
Valentin1720653 
Fragesteller
 20.05.2022, 23:34
@ArchEnema

A.java:1: error: package p1 does not exist

import p1.B;

        ^

A.java:7: error: cannot find symbol

       B test = new B();

       ^

 symbol:  class B

 location: class A

A.java:7: error: cannot find symbol

       B test = new B();

                    ^

 symbol:  class B

 location: class A

3 errors

error: compilation failed

-------------------------------------------------------------------------

package p1;

public class B

{

public static int x;

public static int y;

B()

{

x = 15;

y = 20;

}

void Output()

{

System.out.println(x);

System.out.println(y);

}

---------------------------------------------------------------------

import p1.B;

public class A

{

public static void main(String[] args) {

B test = new B();

}

}

0
Valentin1720653 
Fragesteller
 20.05.2022, 23:37
@ArchEnema

Sry sieht ziemlich beschissen aus.

Ja ich nutze VS code aber linux und bin eigentlich ein Fan davon alles was kompillierung angeht über das Terminal zu machen (ist bei c++ finde ich am einfachsten)

zum kompillieren verwende ich den Befehl java "filename". Ich hatte als ich von der Oracle webseite das .deb paket herunterladen wollte probleme mit abhängigkeiten und deswegen habe ich dafür irgendeinen Flatpak-compiler genommen.

Aber er führt den code irgendwie auch direkt aus und erstellt keine Executable wie das zb bei g++ der fall ist. Kann es sein dass der compiler den ich verwende einfach "müll" ist ?

0
ArchEnema  21.05.2022, 00:12
@Valentin1720653

Lad dir OpenJDK von irgendwoher. Oracle selbst verlangt IMO eine Registrierung mit Mailadresse, Ubuntu-Paketquellen sind da nicht so.

Dann mittels "javac" direkt das Verzeichnis kompilieren unter dem alles liegt. So kannst du ausschließen, dass irgendwas komisches passiert wegen IDE oder dergleichen.

Der Compiler (javac) erzeugt keine Binary im gewohnte Sinne, sondern wirft neben die Quelldateien (*.java) (oder in ein Zielverzeichnis) die kompilierten Bytecodedateien (*.class). Wenn du so willst "Objectfiles", die aber nicht gelinkt werden müssen (passiert zur Laufzeit, deshalb ist die Ordnerstruktur wichtig, die muss zur Packagehierarchie passen).

Das Bytecode-Verzeichnis (wenn du eines verwendest) hat die selbe Ordnerstruktur wie die Quelldateien.

Ein einzelnes Binary in dem Sinne gibt es bei Java nicht. Aber ganze Packagehierarchien werden üblicherweise in jar-Archive verpackt (im einfachsten Fall gewöhnliche Zip-Files mit andere Dateiendung *.jar). Dazu hinein kommt meist noch ein Manifest-File und paar andere Eigenheiten.

So ein jar kann dann mit "java -jar xyz.jar" direkt aufgerufen werden oder als Bibliothek verteilt/gezogen werden.

0
ArchEnema  21.05.2022, 00:32
@ArchEnema

Hab das grad mal getestet. Ordnerstruktur (in A.java fehlte hinten ein } ):

bash-5.0$ find
.
./A.java
./p1
./p1/B.java

Compiler-Aufruf:

bash-5.0$ find . -name "*.java" -exec javac \{\} \;
./A.java:9: error: B() is not public in B; cannot be accessed from outside package
B test = new B();
         ^
1 error

OK, Sichtbarkeitsproblem! Dein Konstruktor B hat keine Sichtbarkeitsauszeichnung und ist deswegen "package-private". D.h. nur Klassen aus p1 dürfen den Aufrufen.

Also ändern in "public B()" !

bash-5.0$ find . -name "*.java" -exec javac \{\} \;
bash-5.0$ find
.
./A.class
./A.java
./p1
./p1/B.java
./p1/B.class

Tut!

Wenn du jetzt test.Output(); wieder in A einfügst wird der Compiler maulen, das B.Output nicht public sei (wie der Konstruktor). Also public machen!

public void Output()

Test:

bash-5.0$ java A
15
20
0
ArchEnema  21.05.2022, 00:46
@ArchEnema

Noch paar Tipps/Verbesserungen/Umformatierungen:

A:

import p1.B;

public class A {

  public static void main(String[] args) {

    B test = new B(5, 7);
    test.output();
  }

}

B:

static (=Klassenvariable) war vermutlich nicht das, was du wolltest.

public sollten x und y auch eher nicht sein (sonst müssten sie final sein, also Konstanten; oder du machst keine OOP-Kapselung :D ).

Und man kann die natürlich per Konstruktor setzen. this.x wird verwendet, weil sonst das x aus der Methodensignatur den Vorrang über das globale x hat.

package p1;

public class B {

  private int x;
  private int y;

  public B(int x, int y) {
    this.x = x;
    this.y = y;
  }

  public void output() {
    System.out.println(x);
    System.out.println(y);
  }
}

Next: x und y aus args[ ] holen (z.B. Integer.parseInt( ) ) und per Kommandozeile übergeben. ;)

1
Valentin1720653 
Fragesteller
 21.05.2022, 10:19
@ArchEnema

Vielen Dank 👍

Uh bin dann gestern schlafen gegangen ich probiere es heute aber direkt aus

0