Gibt es derzeit (Java 6) Dinge, die Sie in Java-Bytecode tun können, die Sie in der Java-Sprache nicht tun können?
Ich weiß, dass beide Turing vollständig sind, also lesen Sie "kann" als "kann wesentlich schneller / besser oder nur auf andere Weise".
Ich denke an zusätzliche Bytecodes wie invokedynamic
, die nicht mit Java generiert werden können, außer dass dieser für eine zukünftige Version vorgesehen ist.
rol
im Assembler, den Sie in C ++ nicht schreiben können.(x<<n)|(x>>(32-n))
zu einerrol
Anweisung kompiliert werden kann .Antworten:
Soweit ich weiß, gibt es in den von Java 6 unterstützten Bytecodes keine wesentlichen Funktionen, auf die nicht auch über Java-Quellcode zugegriffen werden kann. Der Hauptgrund dafür ist offensichtlich, dass der Java-Bytecode unter Berücksichtigung der Java-Sprache entwickelt wurde.
Es gibt jedoch einige Funktionen, die von modernen Java-Compilern nicht erstellt werden:
Die
ACC_SUPER
Flagge :Dies ist ein Flag, das für eine Klasse gesetzt werden kann und angibt, wie ein bestimmter Eckfall des
invokespecial
Bytecodes für diese Klasse behandelt wird. Es wird von allen modernen Java-Compilern festgelegt (wobei "modern"> = Java 1.1 ist, wenn ich mich richtig erinnere), und nur alte Java-Compiler haben Klassendateien erstellt, bei denen dies nicht festgelegt wurde. Dieses Flag existiert nur aus Gründen der Abwärtskompatibilität. Beachten Sie, dass ACC_SUPER ab Java 7u51 aus Sicherheitsgründen vollständig ignoriert wird.Die
jsr
/ret
Bytecodes.Diese Bytecodes wurden verwendet, um Unterroutinen zu implementieren (hauptsächlich zum Implementieren von
finally
Blöcken). Sie werden seit Java 6 nicht mehr produziert . Der Grund für ihre Ablehnung ist, dass sie die statische Überprüfung ohne großen Gewinn erheblich erschweren (dh Code, der verwendet wird, kann fast immer mit normalen Sprüngen mit sehr geringem Overhead neu implementiert werden).Zwei Methoden in einer Klasse, die sich nur im Rückgabetyp unterscheiden.
Die Java-Sprachspezifikation erlaubt nicht zwei Methoden in derselben Klasse, wenn sie sich nur in ihrem Rückgabetyp unterscheiden (dh gleichen Namen, gleiche Argumentliste, ...). Die JVM - Spezifikation hat jedoch keine solche Beschränkung, so dass eine Klassendatei kann zwei solche Methoden enthalten, gibt es einfach keine Möglichkeit , eine solche Klasse - Datei mit dem normalen Java - Compiler zu erzeugen. Diese Antwort enthält ein schönes Beispiel / eine Erklärung .
quelle
a
und eine andere inA
der JAR-Datei umbenannt hat. Es dauerte ungefähr eine halbe Stunde, bis ich auf einem Windows-Computer entpackt hatte , bis mir klar wurde, wo sich die fehlenden Klassen befanden. :)'.'
,';'
,'['
, oder'/'
. Methodennamen sind identisch, können aber auch kein'<'
oder enthalten'>'
. (Mit den bemerkenswerten Ausnahmen von<init>
und<clinit>
zum Beispiel und statischen Konstruktoren.) Ich möchte darauf hinweisen, dass die Klassennamen, wenn Sie die Spezifikation genau befolgen, tatsächlich viel eingeschränkter sind, aber die Einschränkungen nicht erzwungen werden."throws ex1, ex2, ..., exn"
als Teil der Methode Signaturen; Sie können überschriebenen Methoden keine Ausnahmeklauseln hinzufügen. ABER die JVM könnte es nicht weniger interessieren.final
Die JVM garantiert also, dass nur Methoden wirklich ausnahmefrei sind - abgesehen vonRuntimeException
s undError
s natürlich. Soviel zur geprüften Ausnahmebehandlung: DNachdem ich einige Zeit mit Java-Bytecode gearbeitet und einige zusätzliche Untersuchungen zu diesem Thema durchgeführt habe, finden Sie hier eine Zusammenfassung meiner Ergebnisse:
Führen Sie Code in einem Konstruktor aus, bevor Sie einen Superkonstruktor oder Hilfskonstruktor aufrufen
In der Java-Programmiersprache (JPL) muss die erste Anweisung eines Konstruktors ein Aufruf eines Superkonstruktors oder eines anderen Konstruktors derselben Klasse sein. Dies gilt nicht für Java-Bytecode (JBC). Innerhalb von Bytecode ist es absolut legitim, Code vor einem Konstruktor auszuführen, solange:
Legen Sie Instanzfelder fest, bevor Sie einen Superkonstruktor oder Hilfskonstruktor aufrufen
Wie bereits erwähnt, ist es völlig legal, einen Feldwert einer Instanz festzulegen, bevor ein anderer Konstruktor aufgerufen wird. Es gibt sogar einen Legacy-Hack, der es ermöglicht, diese "Funktion" in Java-Versionen vor 6 auszunutzen:
Auf diese Weise könnte ein Feld gesetzt werden, bevor der Superkonstruktor aufgerufen wird, was jedoch nicht mehr möglich ist. In JBC kann dieses Verhalten weiterhin implementiert werden.
Verzweigen Sie einen Super-Konstruktor-Aufruf
In Java ist es nicht möglich, einen Konstruktoraufruf wie zu definieren
Bis Java 7u23 hat der Verifizierer der HotSpot-VM diese Prüfung jedoch verpasst, weshalb dies möglich war. Dies wurde von mehreren Tools zur Codegenerierung als eine Art Hack verwendet, aber es ist nicht mehr legal, eine solche Klasse zu implementieren.Letzteres war lediglich ein Fehler in dieser Compilerversion. In neueren Compilerversionen ist dies wieder möglich.
Definieren Sie eine Klasse ohne Konstruktor
Der Java-Compiler implementiert immer mindestens einen Konstruktor für eine Klasse. Im Java-Bytecode ist dies nicht erforderlich. Dies ermöglicht die Erstellung von Klassen, die auch bei Verwendung von Reflektion nicht erstellt werden können. Die Verwendung
sun.misc.Unsafe
ermöglicht jedoch weiterhin die Erstellung solcher Instanzen.Definieren Sie Methoden mit identischer Signatur, aber unterschiedlichem Rückgabetyp
In der JPL wird eine Methode durch ihren Namen und ihre Rohparametertypen als eindeutig identifiziert. In JBC wird zusätzlich der Rohergabetyp berücksichtigt.
Definieren Sie Felder, die sich nicht nach Namen, sondern nur nach Typ unterscheiden
Eine Klassendatei kann mehrere Felder mit demselben Namen enthalten, sofern sie einen anderen Feldtyp deklarieren. Die JVM bezieht sich immer auf ein Feld als Tupel aus Name und Typ.
Wirf nicht deklarierte geprüfte Ausnahmen, ohne sie zu fangen
Die Java-Laufzeit und der Java-Bytecode kennen das Konzept der geprüften Ausnahmen nicht. Nur der Java-Compiler überprüft, ob geprüfte Ausnahmen immer entweder abgefangen oder deklariert werden, wenn sie ausgelöst werden.
Verwenden Sie den dynamischen Methodenaufruf außerhalb von Lambda-Ausdrücken
Der sogenannte dynamische Methodenaufruf kann für alles verwendet werden, nicht nur für Javas Lambda-Ausdrücke. Mit dieser Funktion können Sie beispielsweise die Ausführungslogik zur Laufzeit ausschalten. Viele dynamische Programmiersprachen, die auf JBC hinauslaufen, haben ihre Leistung mithilfe dieser Anweisung verbessert . In Java-Bytecode können Sie auch Lambda-Ausdrücke in Java 7 emulieren, bei denen der Compiler die Verwendung eines dynamischen Methodenaufrufs noch nicht zugelassen hat, während die JVM die Anweisung bereits verstanden hat.
Verwenden Sie Bezeichner, die normalerweise nicht als legal angesehen werden
Haben Sie schon einmal Lust gehabt, Leerzeichen und einen Zeilenumbruch im Namen Ihrer Methode zu verwenden? Erstellen Sie Ihre eigene JBC und viel Glück bei der Codeüberprüfung. Die einzigen ungültigen Zeichen für Bezeichner sind
.
,;
,[
und/
. Darüber hinaus Methoden, die nicht benannt sind<init>
oder<clinit>
nicht<
und enthalten können>
.Ordnen Sie die
final
Parameter oder diethis
Referenz neu zufinal
Parameter sind in JBC nicht vorhanden und können daher neu zugewiesen werden. Jeder Parameter, einschließlich derthis
Referenz, wird nur in einem einfachen Array innerhalb der JVM gespeichert, wodurch diethis
Referenz am Index0
innerhalb eines einzelnen Methodenrahmens neu zugewiesen werden kann.final
Felder neu zuweisenSolange ein letztes Feld innerhalb eines Konstruktors zugewiesen ist, ist es zulässig, diesen Wert neu zuzuweisen oder gar keinen Wert zuzuweisen. Daher sind die folgenden zwei Konstruktoren zulässig:
Für
static final
Felder ist es sogar zulässig, die Felder außerhalb des Klasseninitialisierers neu zuzuweisen.Behandeln Sie Konstruktoren und den Klasseninitialisierer so, als wären sie Methoden
Dies ist eher ein konzeptionelles Merkmal, aber Konstruktoren werden in JBC nicht anders behandelt als normale Methoden. Nur der Verifizierer der JVM stellt sicher, dass Konstruktoren einen anderen legalen Konstruktor aufrufen. Davon abgesehen ist es lediglich eine Java-Namenskonvention, dass Konstruktoren aufgerufen werden müssen
<init>
und dass der Klasseninitialisierer aufgerufen wird<clinit>
. Abgesehen von diesem Unterschied ist die Darstellung von Methoden und Konstruktoren identisch. Wie Holger in einem Kommentar hervorhob, können Sie sogar Konstruktoren mit anderen Rückgabetypen alsvoid
oder einen Klasseninitialisierer mit Argumenten definieren, obwohl es nicht möglich ist, diese Methoden aufzurufen.Erstellen Sie asymmetrische Datensätze * .
Beim Erstellen eines Datensatzes
javac generiert eine Klassendatei mit einem einzelnen Feld namens
bar
, einer Accessormethode namensbar()
und einem Konstruktor, der ein einzelnes Feld verwendetObject
. Zusätzlich wird ein Datensatzattribut fürbar
hinzugefügt. Durch manuelles Generieren eines Datensatzes ist es möglich, eine andere Konstruktorform zu erstellen, das Feld zu überspringen und den Accessor anders zu implementieren. Gleichzeitig ist es weiterhin möglich, die Reflection-API davon zu überzeugen, dass die Klasse einen tatsächlichen Datensatz darstellt.Rufen Sie eine beliebige Supermethode auf (bis Java 1.1)
Dies ist jedoch nur für Java-Versionen 1 und 1.1 möglich. In JBC werden Methoden immer für einen expliziten Zieltyp gesendet. Dies bedeutet, dass für
Es war möglich zu implementieren
Qux#baz
, umFoo#baz
beim Überspringen aufzurufenBar#baz
. Während es weiterhin möglich ist, einen expliziten Aufruf zum Aufrufen einer anderen Implementierung der Supermethode als der der direkten Superklasse zu definieren, hat dies in Java-Versionen nach 1.1 keine Auswirkungen mehr. In Java 1.1 wurde dieses Verhalten durch Setzen desACC_SUPER
Flags gesteuert, das dasselbe Verhalten ermöglicht, das nur die Implementierung der direkten Superklasse aufruft.Definieren Sie einen nicht virtuellen Aufruf einer Methode, die in derselben Klasse deklariert ist
In Java ist es nicht möglich, eine Klasse zu definieren
Der obige Code führt immer zu einem
RuntimeException
Wann,foo
das für eine Instanz von aufgerufen wirdBar
. Es ist nicht möglich, dieFoo::foo
Methode zu definieren , um eine eigenebar
Methode aufzurufen , die in definiert istFoo
. Wiebar
bei einer nicht privaten Instanzmethode ist der Aufruf immer virtuell. Mit Byte - Code, kann man jedoch den Aufruf definieren die verwendenINVOKESPECIAL
Opcode , die direkt über die Linksbar
Methodenaufruf inFoo::foo
zuFoo
s Version‘. Dieser Opcode wird normalerweise zum Implementieren von Super-Methodenaufrufen verwendet. Sie können den Opcode jedoch wiederverwenden, um das beschriebene Verhalten zu implementieren.Feinkörnige Anmerkungen
In Java werden Anmerkungen entsprechend ihrer
@Target
Deklaration in den Anmerkungen angewendet . Mithilfe der Bytecode-Manipulation können Anmerkungen unabhängig von diesem Steuerelement definiert werden. Es ist beispielsweise auch möglich, einen Parametertyp zu kommentieren, ohne den Parameter zu kommentieren, selbst wenn die@Target
Annotation für beide Elemente gilt.Definieren Sie ein Attribut für einen Typ oder seine Mitglieder
In der Java-Sprache können nur Anmerkungen für Felder, Methoden oder Klassen definiert werden. In JBC können Sie grundsätzlich alle Informationen in die Java-Klassen einbetten. Um diese Informationen nutzen zu können, können Sie sich jedoch nicht mehr auf den Lademechanismus der Java-Klasse verlassen, sondern müssen die Metainformationen selbst extrahieren.
Überlauf und implizit assign
byte
,short
,char
undboolean
WerteDie letzteren primitiven Typen sind in JBC normalerweise nicht bekannt, sondern nur für Array-Typen oder für Feld- und Methodendeskriptoren definiert. Innerhalb von Bytecode-Anweisungen nehmen alle benannten Typen das 32-Bit-Leerzeichen ein, mit dem sie dargestellt werden können
int
. Offiziell nur dieint
,float
,long
unddouble
Typen innerhalb Byte - Code existieren , die alle brauchen explizite Konvertierung durch die Regel des Verifizierer JVM.Kein Monitor freigeben
Ein
synchronized
Block besteht eigentlich aus zwei Anweisungen, eine zum Erfassen und eine zum Freigeben eines Monitors. In JBC können Sie eine erwerben, ohne sie freizugeben.Hinweis : In neueren Implementierungen von HotSpot führt dies stattdessen zu einem
IllegalMonitorStateException
am Ende einer Methode oder zu einer impliziten Freigabe, wenn die Methode durch eine Ausnahme selbst beendet wird.Fügen Sie einem Typinitialisierer mehr als eine
return
Anweisung hinzuIn Java kann sogar ein trivialer Typinitialisierer wie
ist illegal. Im Bytecode wird der Typinitialisierer wie jede andere Methode behandelt, dh Rückgabeanweisungen können überall definiert werden.
Erstellen Sie irreduzible Schleifen
Der Java-Compiler konvertiert Schleifen in goto-Anweisungen im Java-Bytecode. Solche Anweisungen können verwendet werden, um irreduzible Schleifen zu erstellen, was der Java-Compiler niemals tut.
Definieren Sie einen rekursiven Catch-Block
Im Java-Bytecode können Sie einen Block definieren:
Eine ähnliche Anweisung wird implizit erstellt, wenn ein
synchronized
Block in Java verwendet wird, bei dem eine Ausnahme beim Freigeben eines Monitors zu der Anweisung zum Freigeben dieses Monitors zurückkehrt. Normalerweise sollte bei einer solchen Anweisung keine Ausnahme auftreten, aber wenn diesThreadDeath
der Fall wäre (z. B. die veraltete Anweisung ), wird der Monitor trotzdem freigegeben.Rufen Sie eine Standardmethode auf
Der Java-Compiler erfordert, dass mehrere Bedingungen erfüllt sind, um den Aufruf einer Standardmethode zu ermöglichen:
B
erweitert,A
aber eine Methode in nicht überschreibtA
, kann die Methode trotzdem aufgerufen werden.Für Java-Bytecode zählt nur die zweite Bedingung. Der erste ist jedoch irrelevant.
Rufen Sie eine Supermethode für eine Instanz auf, die dies nicht ist
this
Der Java-Compiler erlaubt nur das Aufrufen einer Super- (oder Schnittstellenstandard-) Methode für Instanzen von
this
. Im Bytecode ist es jedoch auch möglich, die Super-Methode für eine Instanz desselben Typs aufzurufen, die der folgenden ähnelt:Zugriff auf synthetische Elemente
Im Java-Bytecode ist es möglich, direkt auf synthetische Mitglieder zuzugreifen. Überlegen Sie beispielsweise, wie im folgenden Beispiel auf die äußere Instanz einer anderen
Bar
Instanz zugegriffen wird:Dies gilt im Allgemeinen für jedes synthetische Feld, jede Klasse oder Methode.
Definieren Sie nicht synchronisierte generische Typinformationen
Während die Java-Laufzeit keine generischen Typen verarbeitet (nachdem der Java-Compiler die Typlöschung angewendet hat), werden diese Informationen weiterhin als Metainformationen an eine kompilierte Klasse weitergeleitet und über die Reflection-API zugänglich gemacht.
Der Prüfer überprüft nicht die Konsistenz dieser mit Metadaten
String
kodierten Werte. Es ist daher möglich, Informationen zu generischen Typen zu definieren, die nicht mit der Löschung übereinstimmen. Als Konsequenz können die folgenden Behauptungen wahr sein:Die Signatur kann auch als ungültig definiert werden, sodass eine Laufzeitausnahme ausgelöst wird. Diese Ausnahme wird ausgelöst, wenn zum ersten Mal auf die Informationen zugegriffen wird, da diese träge ausgewertet werden. (Ähnlich wie Anmerkungswerte mit einem Fehler.)
Fügen Sie Parameter-Metainformationen nur für bestimmte Methoden hinzu
Der Java-Compiler ermöglicht das Einbetten von Parameternamen und Modifikatorinformationen beim Kompilieren einer Klasse mit
parameter
aktiviertem Flag. Im Java-Klassendateiformat werden diese Informationen jedoch pro Methode gespeichert, sodass nur solche Methodeninformationen für bestimmte Methoden eingebettet werden können.Verwirren Sie die Dinge und bringen Sie Ihre JVM zum Absturz
In Java-Bytecode können Sie beispielsweise festlegen, dass eine beliebige Methode für einen beliebigen Typ aufgerufen werden soll. Normalerweise beschwert sich der Prüfer, wenn einem Typ eine solche Methode nicht bekannt ist. Wenn Sie jedoch eine unbekannte Methode für ein Array aufrufen, habe ich in einer JVM-Version einen Fehler gefunden, bei dem der Prüfer dies übersieht und Ihre JVM beendet wird, sobald die Anweisung aufgerufen wird. Dies ist zwar kaum eine Funktion, aber technisch gesehen ist dies mit javac kompiliertem Java nicht möglich . Java hat eine Art doppelte Validierung. Die erste Validierung wird vom Java-Compiler angewendet, die zweite von der JVM, wenn eine Klasse geladen wird. Wenn Sie den Compiler überspringen, finden Sie möglicherweise eine Schwachstelle in der Validierung des Überprüfers. Dies ist jedoch eher eine allgemeine Aussage als ein Merkmal.
Kommentieren Sie den Empfängertyp eines Konstruktors, wenn keine äußere Klasse vorhanden ist
Seit Java 8 können nicht statische Methoden und Konstruktoren innerer Klassen einen Empfängertyp deklarieren und diese Typen mit Anmerkungen versehen. Konstruktoren von Klassen der obersten Ebene können ihren Empfängertyp nicht mit Anmerkungen versehen, da sie meistens keinen deklarieren.
Da
Foo.class.getDeclaredConstructor().getAnnotatedReceiverType()
jedoch eineAnnotatedType
Darstellung zurückgegeben wirdFoo
, ist es möglich, Typanmerkungen fürFoo
den Konstruktor direkt in die Klassendatei aufzunehmen, in der diese Anmerkungen später von der Reflection-API gelesen werden.Verwenden Sie nicht verwendete / Legacy-Bytecode-Anweisungen
Da andere es benannt haben, werde ich es auch aufnehmen. Java verwendete früher Unterprogramme der Anweisungen
JSR
undRET
. JBC kannte zu diesem Zweck sogar eine eigene Art von Absenderadresse. Die Verwendung von Unterprogrammen hat jedoch die statische Code-Analyse zu kompliziert gemacht, weshalb diese Anweisungen nicht mehr verwendet werden. Stattdessen dupliziert der Java-Compiler den von ihm kompilierten Code. Dies schafft jedoch im Grunde eine identische Logik, weshalb ich es nicht wirklich für etwas anderes halte. Ebenso können Sie beispielsweise die hinzufügenNOOP
Bytecode-Anweisung, die auch vom Java-Compiler nicht verwendet wird, aber dies würde es Ihnen auch nicht wirklich ermöglichen, etwas Neues zu erreichen. Wie im Zusammenhang ausgeführt, werden diese erwähnten "Funktionsanweisungen" jetzt aus dem Satz legaler Opcodes entfernt, wodurch sie noch weniger zu einem Merkmal werden.quelle
<clinit>
Methoden verwenden, indem Sie Methoden mit dem Namen definieren,<clinit>
aber Parameter akzeptieren oder einenvoid
Typ ohne Rückgabe haben. Diese Methoden sind jedoch nicht sehr nützlich. Die JVM ignoriert sie und der Bytecode kann sie nicht aufrufen. Die einzige Verwendung wäre, die Leser zu verwirren.IllegalMonitorStateException
wenn Sie diemonitorexit
Anweisung weggelassen haben . Und im Falle eines außergewöhnlichen Methoden-Exits, bei dem a nicht ausgeführt wurdemonitorexit
, wird der Monitor unbeaufsichtigt zurückgesetzt.Hier sind einige Funktionen, die im Java-Bytecode, jedoch nicht im Java-Quellcode ausgeführt werden können:
Auslösen einer aktivierten Ausnahme aus einer Methode, ohne zu deklarieren, dass die Methode sie auslöst. Die aktivierten und nicht aktivierten Ausnahmen werden nur vom Java-Compiler und nicht von der JVM überprüft. Aus diesem Grund kann Scala beispielsweise geprüfte Ausnahmen von Methoden auslösen, ohne sie zu deklarieren. Bei Java-Generika gibt es jedoch eine Problemumgehung, die als hinterhältiger Wurf bezeichnet wird .
Zwei Methoden in einer Klasse, die sich nur im Rückgabetyp unterscheiden, wie bereits in Joachims Antwort erwähnt : Die Java-Sprachspezifikation erlaubt nicht zwei Methoden in derselben Klasse, wenn sie sich nur in ihrem Rückgabetyp unterscheiden (dh gleicher Name, gleiche Argumentliste, ...). Die JVM - Spezifikation hat jedoch keine solche Beschränkung, so dass eine Klassendatei kann zwei solche Methoden enthalten, gibt es einfach keine Möglichkeit , eine solche Klasse - Datei mit dem normalen Java - Compiler zu erzeugen. Diese Antwort enthält ein schönes Beispiel / eine Erklärung .
quelle
Thread.stop(Throwable)
für einen hinterhältigen Wurf verwenden. Ich gehe davon aus, dass der bereits verknüpfte schneller ist.GOTO
kann mit Beschriftungen verwendet werden, um eigene Kontrollstrukturen zu erstellen (außerfor
while
usw.)this
lokale Variable innerhalb einer Methode überschreibenAls verwandten Punkt können Sie den Parameternamen für Methoden erhalten, wenn diese mit Debug kompiliert wurden ( Paranamer tut dies durch Lesen des Bytecodes
quelle
override
diese lokale Variable?this
Variable hat den Index Null, ist jedoch nicht nur bei derthis
Eingabe einer Instanzmethode mit der Referenz vorinitialisiert, sondern nur eine lokale Variable. Sie können also einen anderen Wert schreiben, der sich je nach Verwendung wie das Beendenthis
des Gültigkeitsbereichs oder das Ändern derthis
Variablen verhält.this
es neu zugewiesen werden kann? Ich denke, es war nur das Wort Override, das mich fragte, was es genau bedeutete.Vielleicht ist Abschnitt 7A in diesem Dokument von Interesse, obwohl es eher um Bytecode- Fallstricke als um Bytecode- Funktionen geht .
quelle
In der Java-Sprache muss die erste Anweisung in einem Konstruktor ein Aufruf des Superklassenkonstruktors sein. Bytecode hat diese Einschränkung nicht. Stattdessen lautet die Regel, dass der Superklassenkonstruktor oder ein anderer Konstruktor in derselben Klasse für das Objekt aufgerufen werden muss, bevor auf die Mitglieder zugegriffen werden kann. Dies sollte mehr Freiheit ermöglichen wie:
Ich habe diese nicht getestet. Bitte korrigieren Sie mich, wenn ich falsch liege.
quelle
Mit Bytecode können Sie anstelle von einfachem Java-Code Code generieren, der ohne Compiler geladen und ausgeführt werden kann. Viele Systeme haben JRE anstelle von JDK. Wenn Sie Code dynamisch generieren möchten, ist es möglicherweise besser, wenn nicht sogar einfacher, Bytecode zu generieren, anstatt Java-Code zu kompilieren, bevor er verwendet werden kann.
quelle
Ich habe als I-Play einen Bytecode-Optimierer geschrieben (er wurde entwickelt, um die Codegröße für J2ME-Anwendungen zu reduzieren). Eine Funktion, die ich hinzugefügt habe, war die Möglichkeit, Inline-Bytecode zu verwenden (ähnlich der Inline-Assemblersprache in C ++). Ich habe es geschafft, die Größe einer Funktion, die Teil einer Bibliotheksmethode war, mithilfe der DUP-Anweisung zu reduzieren, da ich den Wert zweimal benötige. Ich hatte auch Null-Byte-Anweisungen (wenn Sie eine Methode aufrufen, die ein Zeichen akzeptiert und ein int übergeben möchte, von dem Sie wissen, dass es nicht umgewandelt werden muss, habe ich int2char (var) hinzugefügt, um char (var) zu ersetzen, und es würde entfernt die i2c-Anweisung, um die Größe des Codes zu reduzieren. Ich habe ihn auch dazu gebracht, float a = 2,3; float b = 3,4; float c = a + b; und das würde in einen festen Punkt konvertiert (schneller, und auch einige J2ME nicht Gleitkomma unterstützen).
quelle
Wenn Sie in Java versuchen, eine öffentliche Methode mit einer geschützten Methode (oder einer anderen Einschränkung des Zugriffs) zu überschreiben, wird die folgende Fehlermeldung angezeigt: "Versuch, schwächere Zugriffsrechte zuzuweisen". Wenn Sie dies mit JVM-Bytecode tun, ist der Verifizierer damit einverstanden, und Sie können diese Methoden über die übergeordnete Klasse aufrufen, als wären sie öffentlich.
quelle