Methoden Serialisieren

Bitte aktiviere JavaScript!
Hallo zusammen,
Ich habe mich eben etwas mit der Serialisierung befasst.

Jetzt würde gerne wissen ob man sozusagen "Java Anweisungen" Serialisieren und danach wieder ausführen kann.

Hier ein paar Beispiele:

Meine Klasse:

Java:
import java.io.Serializable;

public class MyTestClass implements Serializable {
    private static final long serialVersionUID = -3529335292173062912L;
    private MyInterface myInterface;
    public MyTestClass(MyInterface myInterface){
        this.myInterface = myInterface;
    }

    public MyInterface getMyInterface() {
        return myInterface;
    }
}
Wie Ich mein "SerializerString" bekomme:

Java:
        System.out.println(ObjectBuilder.getStringOf(new MyTestClass(new MyInterface() {
            @Override
            public void doSomething() {
                System.out.println("Hello world!");
            }
        })));
Ergebnis: "rO0ABXNyAAtNeVRlc3RDbGFzc88FSEBAxm0AAgABTAALbXlJbnRlcmZhY2V0AA1MTXlJbnRlcmZhY2U7eHBzcgAGTWFpbiQxfcgSFMpUyiwCAAB4cA=="


Nun möchte Ich meine "doSomething" wieder ausführen, das würde Ich theoretisch so machen:
Java:
MyTestClass myTestClass = (MyTestClass) ObjectBuilder.getObjectOf("rO0ABXNy...B4cA==");
myTestClass.getMyInterface().doSomething();
Dabei bekomme Ich aber eine NullPointerException.

Ich habe mir zwar schon gedacht dass das nicht geht, aber Ich finde die Vorstellung von sowas ziemlich cool und es kann ja sein dass es da Möglichkeiten gibt.

Ich hoffe Ich konnte das halbwegs verständlich erklären.
Fragen beantworte Ich sehr gerne.
 
Jetzt würde gerne wissen ob man sozusagen "Java Anweisungen" Serialisieren und danach wieder ausführen kann.
Klar, was glaubst Du, was eine .class-Datei ist?

Nun möchte Ich meine "doSomething" wieder ausführen, das würde Ich theoretisch so machen:
Wenn Du das theoretisch so machen würdest, hättest Du die Klassen ja bereits zur Verfügung. Dann reicht es, die Objekte zu serialisieren:

Java:
import java.io.Serializable;

public interface MyInterface extends Serializable {
    void doSomething();
}
Java:
import java.io.Serializable;
public class MyClass implements Serializable {
    private MyInterface iface;
    public MyClass(MyInterface iface) {
        this.iface = iface;
    }
    public MyInterface getIface() { return iface; }
}
Java:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Test {
    
    public static void main(String[] args) throws Exception {
        MyClass mc = new MyClass(new MyInterface() {
            @Override
            public void doSomething() {
                System.out.println("Hello world!");
            }
        });

        try(ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("test.dat"))) {
            oos.writeObject(mc);
        }

        try(ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("test.dat"))) {
            MyClass mcRead = (MyClass) ois.readObject();
            mcRead.getIface().doSomething();
        }
    }
}
 
Dazu sei gesagt, dass ein ObjectOutputStream nicht den ausführbaren Code oder die Klasse selbst wegschreibt, sodass ein Empfänger genau diesen Code bekommt und ausführen könnte, wenn er ihn deserialisiert. Stattdessen schreibt der ObjectOutputStream lediglich eine Referenz des Laufzeittyps des serialisierten Objektes als Namen in den Ausgabestrom. Ein Empfänger muss diese Klasse also bereits bei sich im ClassPath haben und es wird eben die Klasse instanziiert, die sich bereits beim Empfänger im ClassPath befindet (so diese denn gemäß der SerialVersionUID kompatibel sind).
 
Hey, mihe7,
deine Methode scheint auf den ersten blick zu funktionieren,

Hier der Code:
Java:
    public static void main(String[] args) throws Exception {

        MyTestClass mc = (MyTestClass) new MyTestClass(new MyInterface() {
            @Override
            public void doSomething() {
                System.out.println("Hello world!");
            }
        });

        try(ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("test.dat"))) {
            oos.writeObject(mc);
        }


        try(ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("test.dat"))) {
            MyTestClass mcRead = (MyTestClass) ois.readObject();
            mcRead.getMyInterface().doSomething();
        }
    }
Als Ergebnis kommt dann: Hello world!, scheint also zu funktionieren.

wenn Ich aber den oberen teil weglasse, und die (bestehende) datei nur lese kommt ein Error
Java:
public static void main(String[] args) throws Exception {
        try(ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("test.dat"))) {
            MyTestClass mcRead = (MyTestClass) ois.readObject();
            mcRead.getMyInterface().doSomething();
        }
    }
Code:
Exception in thread "main" java.lang.ClassNotFoundException: Main$1
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:686)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1868)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2287)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2211)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2069)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
    at Main.main(Main.java:8)
dasselbe Problem kam auch bei meinem Beispiel.

Noch was verwirrendes:
wenn Ich die obere Instanz meiner Klasse beibehalte und verändere, wird diese ausgeführt!
Beispiel:
Java:
public static void main(String[] args) throws Exception {

        MyTestClass mc = (MyTestClass) new MyTestClass(new MyInterface() {
            @Override
            public void doSomething() {
                System.out.println("Differend Text!");
            }
        });

        try(ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("test.dat"))) {
            MyTestClass mcRead = (MyTestClass) ois.readObject();
            mcRead.getMyInterface().doSomething();
        }
    }
^ Als Ausgabe kommt dann "Differend Text!" obwohl in der test.dat "Hello world!" gespeichert ist.

Ich selber verstehe das nicht, vielleicht hat ja jemand eine Erklärung.
 
deine Methode scheint auf den ersten blick zu funktionieren,
Die Methode funktioniert nicht nur auf den ersten Blick, Sie löst nur ein anderes Problem.

Bei der Objektserialisierung werden die Daten zusammen mit ein paar Metadaten (welche Typen etc) weggeschrieben. Das reicht normalerweise aus, denn die zugehörigen Klassen (= Code) befindet sich üblicherweise bereits im Classpath. Die Klassen musst Du nicht serialisieren, denn sie liegt als .class-File bereits in serialisierter Form vor. Willst Du den Code weitergeben, gibst Du das .class-File weiter.
 
httpdigest,
tut mir leid, Ich hab mir deine Antwort nicht wirklich aufmerksam durchgelesen.

Das würde es schon erklären.

mihe7
Ich will nicht wirklich "code weitergeben",
Ich mache zurzeit ein anderes Projekt, dort habe Ich ein Klasse mit einem Interface, und in dem Interface ist eine Methods.
Und diese genannte Klasse würde Ich gerne Serialisieren, aber anscheinend geht das nicht wirklich.

Danke euch.
 
Und diese genannte Klasse würde Ich gerne Serialisieren, aber anscheinend geht das nicht wirklich.
Die Klasse ist serialisiert.

Pass auf. Wenn Du ohne IDE übersetzt, kannst Du mal folgendes machen.

1. Nimm die 3 Klassen von mir und übersetze sie:
javac -cp . Test.java

Du findest dann unter anderem die Datei Test$1.class in Deinem Verzeichnis - das ist die serialisierte Version der anonymen Implementierung des Interfaces.

2. Führ das Programm aus
java -cp . Test

3. Ändere den Code von Test.java wie folgt ab:
Java:
    public static void main(String[] args) throws Exception {
        try(ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("test.dat"))) {
            MyTestClass mcRead = (MyTestClass) ois.readObject();
            mcRead.getMyInterface().doSomething();
        }
    }
4. Übersetze die Datei
javac -cp . Test.java

5. Führe das Programm aus
java -cp . Test

6. Staune
 
Ich verstehe Irgendwie nicht ganz.

Folgendes:
Ich weiß nicht genau was du mit "übersetzen" meinst.

und den Java-Code so wie du Ihn geschrieben hast funktioniert bei nicht bzw. es kommt folgender Fehler:
Code:
Exception in thread "main" java.lang.ClassNotFoundException: Main$1
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:686)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1868)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2287)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2211)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2069)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
    at Main.main(Main.java:8)
Und diese genannte Klasse würde Ich gerne Serialisieren, aber anscheinend geht das nicht wirklich.
^ Das habe Ich etwas falsch ausgedrückt.
^ Die Klasse ist Serialisiert. nur das doSomething(); funktionert nicht. <- Ich schätze das kann auch nicht funktionieren.

Ich versuche es mal mit einem Beispiel
Ich hoffe so kann man das besser verstehen.

beispiel.png
 
Ich weiß nicht genau was du mit "übersetzen" meinst.
Java-Quelltext wird mit Hilfe des Java-Compilers (javac) in Bytecode übersetzt und dieser wird als .class-File abgespeichert.

und den Java-Code so wie du Ihn geschrieben hast funktioniert bei nicht bzw. es kommt folgender Fehler:
Das liegt vermutlich an Deiner IDE, die die .class-Dateien vorher löscht. Darum habe ich geschrieben, Du sollst das mal außerhalb der IDE mit den angegebenen Befehlen machen.

Mal anders:
Java:
public class MyImplementation implements MyInterface {
    @Override
    public void doSomething() {
        System.out.println("Hello world!");
    }
}
Das verwendest Du in main:
Java:
public class Test {
   
    public static void main(String[] args) throws Exception {
        MyClass mc = new MyClass(new MyImplementation());

        try(ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("test.dat"))) {
            oos.writeObject(mc);
        }

        try(ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("test.dat"))) {
            MyClass mcRead = (MyClass) ois.readObject();
            mcRead.getIface().doSomething();
        }
    }
}
Jetzt noch ausführen.

Danach testen mit

Java:
public class Test {
   
    public static void main(String[] args) throws Exception {
        try(ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("test.dat"))) {
            MyClass mcRead = (MyClass) ois.readObject();
            mcRead.getIface().doSomething();
        }
    }
}
Nachtrag: wenn Du den Spaß in einer zweiten Anwendung verwenden willst, musst Du die Dateien MyImplementation.class, MyInterface.class und MyClass.class der zweiten Anwendung zur Verfügung stellen.
 
Also ich weiß nicht, was du erreichen willst. Kannst du das ursprüngliche Problem nicht etwas erläutern, denn ich fürchte, dass wir hier etwas ein xy Problem diskutieren.

Klassen sind erst einmal Code und sollten vor der Ausführung bekannt sein. Serialisiert werden nur Daten, d.h. Instanzen der Klassen, aber eben kein Code, der ausgeführt wird. Somit liegt die Methode bereits vor,

Dies hat Sicherheitsgründe und den Grund erkennst du z.B. mit den großen Problemen, die Applikationen haben, wenn sie Dokumente laden und ausführen (Malware über Office Dokumente und so. Oder in Deinem Fall: ich gebe Dir ein .class File, dass dann nicht nur den Text ausgibt sondern parallel noch etwas startet, dass Deine Dateien zu mir hoch lädt, einen Keylogger installiert, ....)

Somit brauchst Du eine Trennung zwischen Code und Daten: In Deinem Beispiel hast du lediglich eine Ausgabe, Daher reicht eine einfache Klasse ‚Ausgabe‘, die ein Feld text hast mit Getter, Setter, SerializationUID ... und von der kannst du dann Instanzen serialisieren und dann weitergeben .... aber dann auch laden und mit text etwas machen ....

Wenn Du aber doch auch Code weiter geben willst, dann geht das auch. Aber aus Sicherheitsgründen würde ich davon abraten! Die Idee dabei ist, dass Du dann halt z.B. Code in Form von jar Dateien weiter gibst. Dein eigener Classloader lädt das Jar File dann und ist verantwortlich für das Erstellen von Instanzen.
Dafür gibt es dann auch Libraries wie z.B. JCL: https://github.com/kamranzafar/JCL

(Sowas kann halt auch manchmal sinnvoll sein ... ich denke z.B, an IDEs. Aber den Sicherheitsaspekt muss man immer im Auge behalten!)
 
Er will seine Methode in eine Datei schreiben und in einem anderen Programm dann einladen und ausführen. So verstehe ich das zumindest.
 
Den Part habe ich bereits verstanden, aber wie schon gesagt: ich fürchte ein xy Problem. Er will x und denkt, dass y eine gute Lösung wäre. Nur y kriegt er dann nicht hin und fragt.

Daher auch meine Ausführung, dass eben Daten gespeichert werden und normalerweise keine Methoden oder Funktionen. Und dass dies gute Gründe hat (Security).

Auch wenn ich Hinweise gebracht habe, wie man dynamisch Code nachladen kann (Classloader, JCL) ist dies meist keine gute Idee. Ich habe mal einiges diesbezüglich von 20 Jahren gebastelt. Da ging es sogar so weit, dass dynamisch Codefiles übergeben werden konnten und der Code dann übersetzt wurde. Incl. dem erneuten Laden, wenn eine bestehende Klasse erneuert wurde. (Da ging es damals um eine Entwicklung eines graphischen MUDs, in dem High Level Spieler das Spiel auch weiter entwickeln könnten und so ....)
 
Also im Grunde eine Art Plugin-System?

Klassen (=„Code“) kann man natürlich problemlos zur Laufzeit nachladen und ausführen, auch wenn die zur Laufzeit des restlichen Programms noch nicht bekannt sind.

Wenn das ganze zur Laufzeit passieren muss, kann man eines der von @kneitzel genannten Dinge nutzen, wenn zur Startzeit reicht, geht das zB mit Bordmitteln mit ServiceLoadern.
 
Ja, mit nem ClassLoader komme Ich gut weiter!
Vielen Dank an alle hier die sich die mühe gemacht haben mir zu helfen!
 
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben