wake-up-neo.net

Bytecode-Funktionen sind in Java nicht verfügbar

Gibt es derzeit (Java 6) Dinge, die Sie mit Java-Bytecode tun können, die Sie nicht innerhalb der Java-Sprache ausführen können?

Ich weiß, dass beide Turing vollständig sind, so dass Lesen "kann" als "kann wesentlich schneller/besser oder nur auf eine 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 bestimmt ist.

141

Soweit ich weiß, enthalten die von Java 6 unterstützten Bytecodes keine wesentlichen Funktionen, auf die auch nicht von Java-Quellcode aus zugegriffen werden kann. Der Hauptgrund dafür ist offensichtlich, dass der Java-Bytecode für die Java-Sprache entwickelt wurde.

Es gibt jedoch einige Funktionen, die von modernen Java-Compilern nicht erzeugt werden:

  • Das ACC_SUPER-Flag :

    Dies ist ein Flag, das für eine Klasse festgelegt werden kann, und gibt an, 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 recht erinnere) und nur alte Java-Compiler erzeugten Klassendateien, bei denen dies nicht festgelegt war. Dieses Flag existiert nur aus Gründen der Rückwärtskompatibilität. Beachten Sie, dass ACC_SUPER ab Java 7u51 aus Sicherheitsgründen vollständig ignoriert wird.

  • Die jsrret Bytecodes.

    Diese Bytecodes wurden zum Implementieren von Unterprogrammen verwendet (meistens zum Implementieren von finally-Blöcken). Sie sind werden seit Java 6 nicht mehr produziert . Der Grund für ihre Abwertung liegt darin, dass sie die statische Verifizierung für keinen großen Gewinn stark erschweren (d. H. Code, der verwendet wird, kann fast immer mit normalen Sprüngen mit sehr geringem Overhead implementiert werden).

  • Sie haben zwei Methoden in einer Klasse, die sich nur im Rückgabetyp unterscheiden.

    Die Java-Sprachspezifikation erlaubt nicht zwei Methoden in derselben Klasse, wenn sie _/only in ihrem Rückgabetyp unterscheiden (d. H. Gleicher Name, gleiche Argumentliste, ...). Die JVM-Spezifikation hat jedoch keine solche Einschränkung. Daher enthält eine Klassendatei can zwei solcher Methoden. Es gibt keine Möglichkeit, eine solche Klassendatei mit dem normalen Java-Compiler zu erstellen. Es gibt ein schönes Beispiel/eine Erklärung in diese Antwort .

60
Joachim Sauer

Nachdem ich einige Zeit mit Java Bytecode gearbeitet und zusätzliche Nachforschungen zu diesem Thema angestellt habe, folgt eine Zusammenfassung meiner Ergebnisse:

Führen Sie Code in einem Konstruktor aus, bevor Sie einen Superkonstruktor oder Hilfskonstruktor aufrufen

In der Programmiersprache Java (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 Byte-Code ist es absolut legitim, Code vor einem Konstruktor auszuführen, sofern:

  • Ein anderer kompatibler Konstruktor wird zu einem bestimmten Zeitpunkt nach diesem Codeblock aufgerufen.
  • Dieser Aufruf befindet sich nicht in einer bedingten Anweisung.
  • Vor diesem Konstruktoraufruf wird kein Feld der erstellten Instanz gelesen und keine ihrer Methoden aufgerufen. Dies impliziert den nächsten Punkt.

Setzen Sie Instanzfelder, bevor Sie einen Superkonstruktor oder einen 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, mit dem dieses "Feature" in Java Versionen vor 6 ausgenutzt werden kann:

class Foo {
  public String s;
  public Foo() {
    System.out.println(s);
  }
}

class Bar extends Foo {
  public Bar() {
    this(s = "Hello World!");
  }
  private Bar(String helper) {
    super();
  }
}

Auf diese Weise könnte ein Feld gesetzt werden, bevor der Superkonstruktor aufgerufen wird, was jedoch nicht länger möglich ist. In JBC kann dieses Verhalten weiterhin implementiert werden.

Verzweige einen Super-Konstruktor-Aufruf

In Java ist es nicht möglich, einen Konstruktoraufruf wie zu definieren

class Foo {
  Foo() { }
  Foo(Void v) { }
}

class Bar() {
  if(System.currentTimeMillis() % 2 == 0) {
    super();
  } else {
    super(null);
  }
}

Bis Java 7u23 hat der Prüfer 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 nur ein Fehler in dieser Compiler-Version. In neueren Compilerversionen ist dies wieder möglich.

Definiere eine Klasse ohne Konstruktor

Der Compiler Java implementiert immer mindestens einen Konstruktor für eine Klasse. In Java Bytecode ist dies nicht erforderlich. Auf diese Weise können Klassen erstellt werden, die auch bei Verwendung von Reflection nicht erstellt werden können. Die Verwendung von Sun.misc.Unsafe Ermöglicht jedoch weiterhin die Erstellung solcher Instanzen.

Definiere Methoden mit identischer Signatur aber mit unterschiedlichem Rückgabetyp

In der JPL wird eine Methode durch ihren Namen und ihre Rohparametertypen als eindeutig identifiziert. In JBC wird zusätzlich der rohe Rückgabetyp berücksichtigt.

Definiere Felder, die sich nicht nach Namen sondern nur nach Typ unterscheiden

Eine Klassendatei kann mehrere gleichnamige Felder enthalten, sofern sie einen anderen Feldtyp deklarieren. Die JVM bezieht sich immer auf ein Feld als Tupel mit Namen und Typ.

Nicht deklarierte überprüfte Ausnahmen auslösen, ohne sie zu fangen

Die Laufzeit Java und der Bytecode Java kennen das Konzept der überprüften Ausnahmen nicht. Nur der Compiler Java überprüft, ob geprüfte Ausnahmen immer abgefangen oder deklariert werden, wenn sie ausgelöst werden.

Dynamischen Methodenaufruf außerhalb von Lambda-Ausdrücken verwenden

Das sogenannte dynamischer 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 sich auf JBC verbesserte Leistung beschränken, verwenden diese Anweisung. In Java Bytecode können Sie auch Lambda-Ausdrücke in Java 7 emulieren, bei denen der Compiler noch keinen dynamischen Methodenaufruf zugelassen hat, während die JVM die Anweisung bereits verstanden hat.

Verwenden Sie Bezeichner, die normalerweise nicht als legal gelten.

Haben Sie schon einmal Lust gehabt, Leerzeichen und einen Zeilenumbruch im Namen Ihrer Methode zu verwenden? Erstellen Sie Ihr eigenes JBC und viel Glück bei der Codeüberprüfung. Die einzigen unzulässigen Zeichen für Bezeichner sind ., ;, [ Und /. Darüber hinaus können Methoden ohne den Namen <init> Oder <clinit> Nicht < Und > Enthalten.

final Parameter neu zuweisen oder die this Referenz

final -Parameter sind in JBC nicht vorhanden und können daher neu zugewiesen werden. Jeder Parameter, einschließlich der this -Referenz, wird nur in einem einfachen Array innerhalb der JVM gespeichert, wodurch die this -Referenz am Index 0 Innerhalb eines einzelnen Methodenrahmens neu zugewiesen werden kann.

final Felder neu zuweisen

Solange ein letztes Feld in einem Konstruktor zugewiesen ist, ist es zulässig, diesen Wert neu zuzuweisen oder gar keinen Wert zuzuweisen. Daher sind die folgenden beiden Konstruktoren zulässig:

class Foo {
  final int bar;
  Foo() { } // bar == 0
  Foo(Void v) { // bar == 2
    bar = 1;
    bar = 2;
  }
}

Für static final - Felder ist es sogar zulässig, die Felder außerhalb des Klasseninitialisierers neu zuzuweisen.

Behandle 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 Prüfer der JVM stellt sicher, dass Konstruktoren einen anderen legalen Konstruktor aufrufen. Ansonsten handelt es sich lediglich um eine Java - Namenskonvention, nach der Konstruktoren <init> Und der Klasseninitialisierer <clinit> Heißen müssen. Abgesehen von diesem Unterschied ist die Darstellung von Methoden und Konstruktoren identisch. Wie Holger in einem Kommentar ausführte, können Sie sogar Konstruktoren mit anderen Rückgabetypen als void oder einen Klasseninitialisierer mit Argumenten definieren, obwohl es nicht möglich ist, diese Methoden aufzurufen.

Rufe 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 auf einen expliziten Zieltyp verteilt. Dies bedeutet, dass z

class Foo {
  void baz() { System.out.println("Foo"); }
}

class Bar extends Foo {
  @Override
  void baz() { System.out.println("Bar"); }
}

class Qux extends Bar {
  @Override
  void baz() { System.out.println("Qux"); }
}

es war möglich, Qux#baz zu implementieren, um Foo#baz aufzurufen, während Sie über Bar#baz springen. Während es immer noch möglich ist, einen expliziten Aufruf zu definieren, um eine andere Super-Methoden-Implementierung als die der direkten Super-Klasse aufzurufen, hat dies in Java-Versionen nach 1.1 keine Auswirkungen mehr. In Java 1.1 wurde dieses Verhalten durch Setzen des Flags ACC_SUPER Gesteuert, das dasselbe Verhalten ermöglicht, das nur die Implementierung der direkten Superklasse aufruft.

Definiert einen nicht virtuellen Aufruf einer Methode, die in derselben Klasse deklariert ist

In Java ist es nicht möglich, eine Klasse zu definieren

class Foo {
  void foo() {
    bar();
  }
  void bar() { }
}

class Bar extends Foo {
  @Override void bar() {
    throw new RuntimeException();
  }
}

Der obige Code führt immer zu einem RuntimeException, wenn foo für eine Instanz von Bar aufgerufen wird. Es ist nicht möglich, die Methode Foo::foo Zu definieren, um seine eigenebar aufzurufen, die in Foo definiert ist. Da bar eine nicht private Instanzmethode ist, ist der Aufruf immer virtuell. Mit Bytecode kann man jedoch den Aufruf definieren, um den INVOKESPECIAL -Opcode zu verwenden, der den bar -Methodenaufruf in Foo::foo Direkt mit der Version von Foo verknüpft. Dieser Opcode wird normalerweise verwendet, um Supermethodenaufrufe zu implementieren, aber Sie können den Opcode wiederverwenden, um das beschriebene Verhalten zu implementieren.

Feinkörnige Anmerkungen

In Java werden Anmerkungen entsprechend ihrem @Target Angewendet, den die Anmerkungen deklarieren. Mit der Bytecode-Manipulation ist es möglich, Anmerkungen unabhängig von diesem Steuerelement zu definieren. Es ist beispielsweise auch möglich, einen Parametertyp zu kommentieren, ohne den Parameter zu kommentieren, selbst wenn die Annotation @Target Für beide Elemente gilt.

Definiere ein beliebiges Attribut für einen Typ oder seine Mitglieder

Innerhalb der Sprache Java können nur Annotationen für Felder, Methoden oder Klassen definiert werden. In JBC können Sie grundsätzlich alle Informationen in die Klassen Java einbetten. Um diese Informationen nutzen zu können, können Sie sich jedoch nicht mehr auf den Klassenlademechanismus Java verlassen, sondern müssen die Metainformationen selbst extrahieren.

Überlauf und implizite Zuweisung von byte-, short-, char- und boolean-Werten

Die 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-Befehlen belegen alle genannten Typen das 32-Bit-Leerzeichen, mit dem sie als int dargestellt werden können. Offiziell gibt es nur die Typen int, float, long und double im Bytecode, die alle explizit nach der Regel des Verifizierers der JVM konvertiert werden müssen.

Einen Monitor nicht 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üge mehr als eine return Anweisung zu einem Typinitialisierer hinzu

In Java kann sogar ein trivialer Initialisierer wie z

class Foo {
  static {
    return;
  }
}

ist illegal. Im Bytecode wird der Typinitialisierer wie jede andere Methode behandelt, d. H. Rückgabeanweisungen können überall definiert werden.

Erstelle irreduzible Schleifen

Der Compiler Java konvertiert Schleifen in goto-Anweisungen im Byte-Code Java. Solche Anweisungen können verwendet werden, um irreduzible Schleifen zu erzeugen, was der Compiler Java niemals tut.

Definiere einen rekursiven catch Block

In Java Bytecode können Sie einen Block definieren:

try {
  throw new Exception();
} catch (Exception e) {
  <goto on exception>
  throw Exception();
}

Eine ähnliche Anweisung wird implizit erstellt, wenn ein synchronized -Block in Java verwendet wird, bei dem eine Ausnahme beim Freigeben eines Monitors zur Anweisung zum Freigeben dieses Monitors zurückkehrt. Normalerweise sollte bei einem solchen Befehl keine Ausnahme auftreten, aber wenn dies der Fall wäre (z. B. der veraltete ThreadDeath), würde der Monitor dennoch freigegeben.

Rufe eine Standardmethode auf

Der Compiler Java erfordert, dass mehrere Bedingungen erfüllt sind, um den Aufruf einer Standardmethode zuzulassen:

  1. Die Methode muss die spezifischste sein (darf nicht von einer Subschnittstelle überschrieben werden, die von einem beliebigen Typ, einschließlich Supertypen, implementiert wird).
  2. Der Schnittstellentyp der Standardmethode muss direkt von der Klasse implementiert werden, die die Standardmethode aufruft. Wenn jedoch die Schnittstelle B die Schnittstelle A erweitert, aber eine Methode in A nicht überschreibt, kann die Methode weiterhin aufgerufen werden.

Für Java Bytecode zählt nur die zweite Bedingung. Der erste ist jedoch irrelevant.

Rufe eine Supermethode für eine Instanz auf, die nicht this ist.

Der Compiler Java ermöglicht 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:

class Foo {
  void m(Foo f) {
    f.super.toString(); // calls Object::toString
  }
  public String toString() {
    return "foo";
  }
}

Zugriff auf synthetische Mitglieder

Im Byte-Code Java kann direkt auf synthetische Member zugegriffen werden. Betrachten Sie beispielsweise, wie im folgenden Beispiel auf die äußere Instanz einer anderen Instanz von Bar zugegriffen wird:

class Foo {
  class Bar { 
    void bar(Bar bar) {
      Foo foo = bar.Foo.this;
    }
  }
}

Dies gilt im Allgemeinen für alle synthetischen Felder, Klassen oder Methoden.

Definiere nicht synchronisierte generische Typinformationen

Während die Laufzeit von Java keine generischen Typen verarbeitet (nachdem der Compiler Java die Typlöschung angewendet hat), werden diese Informationen einer kompilierten Klasse weiterhin als Metainformationen zugeordnet und über die Reflection-API zugänglich gemacht .

Der Prüfer überprüft nicht die Konsistenz dieser mit Metadaten String codierten 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 Aussagen wahr sein:

Method method = ...
assertTrue(method.getParameterTypes() != method.getGenericParameterTypes());

Field field = ...
assertTrue(field.getFieldType() == String.class);
assertTrue(field.getGenericFieldType() == Integer.class);

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 sie träge ausgewertet werden. (Ähnlich wie Annotationswerte mit einem Fehler.)

Parameter-Metainformationen nur für bestimmte Methoden anhängen

Der Compiler Java ermöglicht das Einbetten von Parameternamen und Modifikatorinformationen, wenn eine Klasse mit aktiviertem Flag parameter kompiliert wird. Im Klassen-Dateiformat Java werden diese Informationen jedoch pro Methode gespeichert, wodurch es möglich ist, solche Methodeninformationen nur für bestimmte Methoden einzubetten.

Verwirren Sie Ihre JVM und bringen Sie sie zum Absturz

Beispielsweise können Sie in Java Bytecode definieren, dass eine beliebige Methode für einen beliebigen Typ aufgerufen wird. Normalerweise beschwert sich der Prüfer, wenn ein Typ von einer solchen 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 der Befehl aufgerufen wird. Dies ist zwar kaum ein Feature, aber es ist technisch etwas, was mit javac kompiliertem Java nicht möglich ist. Java hat eine Art Doppelvalidierung. Die erste Validierung wird vom Compiler Java 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 Verifizierers. Dies ist jedoch eher eine allgemeine Aussage als ein Merkmal.

Kommentiert 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 der höchsten Klassen können ihren Empfängertyp nicht mit Anmerkungen versehen, da sie meistens keinen deklarieren.

class Foo {
  class Bar {
    Bar(@TypeAnnotation Foo Foo.this) { }
  }
  Foo() { } // Must not declare a receiver type
}

Da Foo.class.getDeclaredConstructor().getAnnotatedReceiverType() jedoch ein AnnotatedType zurückgibt, das Foo darstellt, ist es möglich, Typanmerkungen für den Konstruktor von Foo direkt in die Klassendatei aufzunehmen, in der sich diese Anmerkungen befinden wird später von der Reflection-API gelesen.

Verwenden Sie nicht verwendete/ältere Bytecode-Anweisungen

Da es von anderen benannt wurde, werde ich es auch einschließen. Java verwendete früher Unterprogramme für die Anweisungen JSR und RET. Zu diesem Zweck kannte JBC sogar eine eigene Art von Absenderadresse. Die Verwendung von Subroutinen erschwerte jedoch die statische Code-Analyse, weshalb diese Anweisungen nicht mehr verwendet werden. Stattdessen dupliziert der Compiler Java den von ihm kompilierten Code. Dies schafft jedoch im Grunde genommen eine identische Logik, weshalb ich es nicht wirklich für nötig halte, etwas anderes zu erreichen. In ähnlicher Weise könnten Sie beispielsweise die Bytecode-Anweisung NOOP hinzufügen, die auch nicht vom Compiler Java verwendet wird, aber dies würde es Ihnen auch nicht wirklich ermöglichen, etwas Neues zu erreichen. Wie im Zusammenhang erwähnt, werden diese erwähnten "Merkmalsanweisungen" nun aus dem Satz legaler Operationscodes entfernt, wodurch sie noch weniger merkwürdig werden.

393

Hier einige Funktionen, die in Java-Bytecode, nicht aber in Java-Quellcode ausgeführt werden können:

  • Eine geprüfte Ausnahme von einer Methode auslösen, ohne dass die Methode dies auslöst. Die geprüften und ungeprüften Ausnahmen werden nur vom Java-Compiler geprüft, nicht von der JVM. Aus diesem Grund kann Scala beispielsweise geprüfte Ausnahmen von Methoden ausgeben, ohne sie zu deklarieren. Bei Java-Generics gibt es eine Problemumgehung, die als hinterlistiger Wurf bezeichnet wird.

  • Zwei Methoden in einer Klasse, die sich nur im Rückgabetyp unterscheiden,, wie bereits in Joachim's Antwort erwähnt: Die Java-Sprachspezifikation erlaubt nicht zwei Methoden in derselben Klasse, wenn sie sich unterscheiden. Nur in ihrem Rückgabetyp (dh gleicher Name, gleiche Argumentliste, ...). Die JVM-Spezifikation hat jedoch keine solche Einschränkung. Eine Klassendatei can enthält zwei solcher Methoden. Es gibt keine Möglichkeit, eine solche Klassendatei mit dem normalen Java-Compiler zu erstellen. Es gibt ein schönes Beispiel/Erklärung in diese Antwort .

12
Esko Luontola
  • GOTO kann mit Labels verwendet werden, um eigene Kontrollstrukturen zu erstellen (außer forwhile etc)
  • Sie können die lokale Variable this innerhalb einer Methode überschreiben
  • Kombinieren Sie beide, können Sie einen Aufruf-optimierten Bytecode erstellen (ich mache dies in JCompilo ).

Als verwandten Punkt können Sie Parameternamen für Methoden erhalten, wenn sie mit debug kompiliert werden ( Paranamer liest dies durch Lesen des Bytecodes

Vielleicht ist Abschnitt 7A in diesem Dokument von Interesse, obwohl es sich um den Bytecode Fallstricke anstatt um den Bytecode Features handelt.

4
eljenso

In Java muss die erste Anweisung in einem Konstruktor ein Aufruf an den Superklassenkonstruktor sein. Für Bytecode gilt diese Einschränkung nicht. Stattdessen muss der Superklassenkonstruktor oder ein anderer Konstruktor in derselben Klasse für das Objekt aufgerufen werden, bevor auf die Member zugegriffen werden kann. Dies sollte mehr Freiheit ermöglichen, z.

  • Erstellen Sie eine Instanz eines anderen Objekts, speichern Sie es in einer lokalen Variablen (oder einem Stapel) und übergeben Sie es als Parameter an den Superklassenkonstruktor, während der Verweis in dieser Variablen für andere Zwecke beibehalten wird.
  • Rufen Sie verschiedene andere Konstruktoren basierend auf einer Bedingung auf. Dies sollte möglich sein: Wie kann ein anderer Konstruktor in Java bedingt aufgerufen werden?

Ich habe diese nicht getestet, bitte korrigieren Sie mich, falls ich falsch liege.

3
msell

Mit Byte-Code 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 einfacher, Bytecode anstelle von Java-Code zu generieren, bevor er verwendet werden kann.

2
Peter Lawrey

Ich habe ein Bytecode-Optimierungsprogramm geschrieben, als ich ein I-Play war (es wurde entwickelt, um die Codegröße für J2ME-Anwendungen zu reduzieren). Eine hinzugefügte Funktion war die Möglichkeit, Inline-Bytecode zu verwenden (ähnlich wie Inline-Assembler in C++). Ich konnte die Größe einer Funktion, die Teil einer Bibliotheksmethode war, mithilfe des DUP-Befehls reduzieren, da ich den Wert zweimal benötige. Ich hatte auch Zero-Byte-Anweisungen (wenn Sie eine Methode aufrufen, die eine Zeichenfolge benötigt, und Sie ein int übergeben möchten, von dem Sie wissen, dass es nicht erforderlich ist, die Umwandlung durchzuführen, fügte ich int2char (var) hinzu, um char (var) zu ersetzen, und es würde entfernt die i2c-Anweisung zur Reduzierung der Code-Größe, die ich auch für float a = 2.3; float b = 3.4; float c = a + b; dies würde zu einem festen Punkt konvertiert werden (schneller, und J2ME auch nicht Gleitkomma unterstützen).

1
nharding

Wenn Sie in Java versuchen, eine öffentliche Methode mit einer geschützten Methode (oder einer anderen Einschränkung des Zugriffs) zu überschreiben, wird eine 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.

0
Joseph Sible