Heim >Java >javaLernprogramm >Zusammenfassung der Java-Kenntnisse: So lesen Sie Lambda-Quellcode

Zusammenfassung der Java-Kenntnisse: So lesen Sie Lambda-Quellcode

WBOY
WBOYnach vorne
2022-04-18 18:13:501562Durchsuche

Dieser Artikel vermittelt Ihnen relevantes Wissen über Java, in dem hauptsächlich verwandte Probleme zum Anzeigen von Lambda-Quellcode vorgestellt werden. Mit Lambda-Ausdrücken können Sie viele Optimierungen am Code durchführen, und Sie können mit nur wenigen Zeilen viel erreichen Schauen wir uns die folgenden Dinge an. Ich hoffe, dass sie für alle hilfreich sind.

Zusammenfassung der Java-Kenntnisse: So lesen Sie Lambda-Quellcode

Empfohlene Studie: „Java-Video-Tutorial

Jeder weiß, dass die Verwendung von Lambda-Ausdrücken in Java8 viele Codeoptimierungen ermöglichen kann und Sie mit nur wenigen Dingen viel erreichen können In diesem Kapitel wird Lambda als Beispiel verwendet. Der erste Abschnitt erläutert das zugrunde liegende Ausführungsprinzip und der zweite Abschnitt erläutert die allgemeinen Positionen des Lambda-Flusses.

1. Demo

Schauen wir uns zunächst eine Demo des Lambda-Ausdrucks an, wie unten gezeigt:

Der Code ist relativ einfach, das heißt, einen neuen Thread zu starten, um einen Satz zu drucken, aber for () im Bild -> System.out.println („Lambda wird ausgeführt“) ist ein Code, den viele Schüler möglicherweise verwirrend finden. Wie erkennt Java diesen Code?

Wenn wir die Schreibweise in anonyme innere Klasse ändern, wird es sehr klar und jeder kann es verstehen, wie unten gezeigt:

Bedeutet das () -> System.out.println ( " Lambda wird ausgeführt? " ) Diese Codeform erstellt tatsächlich eine interne Klasse? Tatsächlich ist dies der einfachste Lambda-Ausdruck. Wir können den Quellcode und seine zugrunde liegende Struktur nicht über IDEA sehen. Hier stellen wir verschiedene Möglichkeiten vor, um seine zugrunde liegende Implementierung anzuzeigen.

2. Ausnahmebeurteilungsmethode

Wir können während der Codeausführung aktiv Ausnahmen auslösen und den Stapel ausdrucken. Im Allgemeinen ist diese Methode einfach und effizient, und Sie können den versteckten Code in vielen Fällen sehen Versuchen wir es wie folgt:

Aus dem Ausnahmestapel können wir ersehen, dass die JVM automatisch eine interne Klasse für die aktuelle Klasse erstellt hat (das mehrfache Erscheinen von $ im Fehlerstapel zeigt an, dass es eine interne gibt). Klasse) und die interne Klasse Während der Ausführung des Codes wurde eine Ausnahme ausgelöst, aber der hier angezeigte Code ist eine unbekannte Quelle, sodass wir ihn unter normalen Umständen nicht debuggen können Haltepunkte setzen und erneut ausführen, aber für Lambda-Ausdrücke wissen wir durch die Ausnahmebeurteilungsmethode nur, dass es eine innere Klasse gibt, aber wir können den Quellcode in der inneren Klasse nicht sehen.

3. Javap-Befehlsmethode

Javap ist ein mit Java geliefertes Tool, das Klassenbytecodedateien anzeigen kann. Computer, auf denen die Java-Basisumgebung installiert ist, können den Javap-Befehl direkt ausführen, wie unten gezeigt:

Befehlsoptionen Wir verwenden hauptsächlich den Befehl -v -verbose, um den Inhalt der Bytecode-Datei vollständig auszugeben.

Als nächstes verwenden wir den Befehl javap, um die Datei Lambda.class anzuzeigen. Während der Erklärung bringen wir einige Kenntnisse über Klassendateien mit.

Wir finden den Speicherort von Lambda.class im Befehlsfenster, führen den Befehl aus: javap -verbose Lambda.class, und dann sehen Sie eine lange Liste von Dingen, diese werden als Montageanweisungen bezeichnet. Lassen Sie uns sie einzeln erklären ( Alle Referenzmaterialien stammen aus der Java Virtual Machine-Spezifikation und werden nicht einzeln zitiert):

In den Montageanweisungen können wir leicht eine lange Liste von Typen finden, die mit Constant Pool beginnen, den wir Constant Pool nennen, und Der offizielle englische Name lautet Run-Time Constant Pool. Wir verstehen darunter einfach eine Tabelle, die zur Kompilierungszeit klare Zahlen und Texte sowie Typinformationen zu Klassen, Methoden und Feldern enthält. Jedes Element in der Tabelle heißt cpinfo und besteht aus einem eindeutigen Bezeichner (Tag) + Namen. Derzeit gibt es insgesamt folgende Tag-Typen:

Zusammenfassung der Java-Kenntnisse: So lesen Sie Lambda-Quellcode

Geposteter Teil des von uns analysierten Bildes:

  1. Das Wort „Konstanter Pool“ im Bild bedeutet, dass es sich bei den aktuellen Informationen um einen konstanten Pool handelt ;

  2. Die zweite Spalte jeder Zeile ist der eindeutige Bezeichner (Tag) von cp_info. Beispielsweise entspricht Methodref CONSTANT_Methodref in der obigen Tabelle (der Wert in der obigen Tabelle entspricht dem Tag 10). ), die darstellt Die aktuelle Zeile stellt die Beschreibungsinformationen der Methode dar, z. B. den Methodennamen, den Eingabeparametertyp, den Ausgabeparametertyp usw. Die spezifische Bedeutung finden Sie in der Java Virtual Machine-Spezifikation. Der Screenshot von Methodref lautet wie folgt folgt:
    Zusammenfassung der Java-Kenntnisse: So lesen Sie Lambda-Quellcodecp_info 的唯一标识 ( tag ) ,比如 Methodref 对应着上表中的 CONSTANT_Methodref(上上图中表格中 value 对应 10 的 tag),代表当前行是表示方法的描述信息的,比如说方法的名称,入参类型,出参数类型等,具体的含义在 Java 虚拟机规范中都可以查询到,Methodref 的截图如下:
    Zusammenfassung der Java-Kenntnisse: So lesen Sie Lambda-Quellcode

  3. 每行的第三列,如果是具体的值的话,直接显示具体的值,如果是复杂的值的话,会显示 cp_info 的引用,比如说图中标红 2 处,引用两个 13 和 14 位置的 cp_info

  4. Wenn es sich um einen bestimmten Wert handelt, wird in der dritten Spalte jeder Zeile der spezifische Wert direkt angezeigt. Wenn es sich um einen komplexen Wert handelt, wird ein Verweis auf cp_info angezeigt Beispielsweise wird in dem mit Rot markierten Bild 2 die Referenz Es gibt zwei cp_info an den Positionen 13 und 14. 13 bedeutet, dass der Methodenname init ist, und 14 bedeutet, dass die Methode nein hat Rückgabewert. Die Kombination aus Name und Rückgabetyp der Methode bedeutet, dass es sich um einen parameterlosen Konstruktor handelt.

Die vierte Spalte jeder Zeile ist der spezifische Wert.

  1. Für den wichtigeren cp_info-Typ erklären wir seine Bedeutung:
  2. InvokeDynamic stellt die dynamische Aufrufmethode dar, die wir später ausführlich erläutern werden;
  3. Fieldref stellt die Beschreibungsinformationen des Feldes dar, z. B. den Namen und Typ des Feldes;
  4. NameAndType ist eine Beschreibung des Feld- und Methodentyps;
  5. MethodHandle Methodenhandle, der Sammelname für dynamisch aufrufende Methoden, aber wir werden es auf jeden Fall wissen wissen, welche Methode zur Laufzeit aufgerufen wird;

MethodType dynamische Methodentypen wissen nur, was ihre Methodentypen sind, wenn sie dynamisch ausgeführt werden.

Von den 3 im obigen Bild rot markierten Stellen haben wir Ljava/lang/invoke/MethodHandles$Lookup, java/lang/invoke/LambdaMetafactory.metafactory ähnlichen Code gefunden, MethodHandles und LambdaMetafactory sind beide java.lang.invoke-Pakete Die folgenden wichtigen Methoden: Das Aufrufpaket implementiert hauptsächlich die Funktionen dynamischer Sprachen. Wir wissen, dass die Java-Sprache eine statische kompilierte Sprache ist. Während der Kompilierung wurden die Typen von Klassen, Methoden, Feldern usw. bestimmt Dynamische Sprache, das heißt, die Typen von Klassen, Methoden und Feldern sind beim Kompilieren nicht bekannt. Sie sind nur beim Ausführen bekannt.

Zum Beispiel diese Codezeile: Runnable runnable = () -> System.out.println(“lambda is run”); Nur wenn es ausgeführt wird. Erst dann werden Sie erkennen, dass dies die Methode Runnable.run() darstellt. Viele Klassen im Invoke-Paket sollen diese () darstellen, die wir Methodenhandles (MethodHandler) nennen. Beim Kompilieren weiß der Compiler nur, dass es sich um ein Methodenhandle handelt, und weiß nicht, welche Methode tatsächlich ausgeführt wird. Ich weiß es bis dahin nicht, daher stellt sich die Frage: Woher weiß die JVM bei der Ausführung, dass das ()-Methodenhandle tatsächlich die Runnable.run()-Methode ausführt?

Schauen wir uns zunächst die Montageanleitung der einfachen Methode an:

Auf dem Bild oben können Sie sehen, dass () in der einfachen Methode ausgeführt wird -> “) im Code ( ), ist eigentlich die Runnable.run-Methode.

Wir gehen auf den Konstantenpool Nr. 2 zurück, der im obigen Bild mit der roten 1 markiert ist. InvokeDynamic zeigt an, dass es sich um einen dynamischen Aufruf handelt, und der Speicherort der beiden Konstantenpools lautet #0:#37 Schauen wir uns an, dass # 37 // run:()Ljava/lang/Runnable darstellt, was darauf hinweist, dass die Runnable.run()-Methode dynamisch aufgerufen werden muss, wenn die JVM tatsächlich ausgeführt wird. Aus den Montageanweisungen können wir ersehen dass () tatsächlich Runnable .run() ist, lassen Sie uns unten debuggen, um es zu beweisen.

Zusammenfassung der Java-Kenntnisse: So lesen Sie Lambda-QuellcodeWir haben die Wörter LambdaMetafactory.metafactory an drei Stellen im obigen Bild gefunden. Durch Abfragen der offiziellen Dokumentation haben wir erfahren, dass diese Methode der Schlüssel zur Verknüpfung mit dem echten Code bei der Ausführung ist, also haben wir einen Haltepunkt in der Metafactory-Methode gesetzt debug Wie unten gezeigt:

Metafactory-Methodeneingabeparameter-Aufrufer stellt den Ort dar, an dem der dynamische Aufruf tatsächlich erfolgt, invokedName stellt den Namen der aufrufenden Methode dar, invokedType stellt die mehreren Eingabeparameter und ausgehenden Parameter des Aufrufs dar, samMethodType stellt die dar Parameter des spezifischen Implementierers, implMethod stellt den tatsächlichen Implementierer dar, instanziierterMethodType entspricht implMethod.

Um den obigen Inhalt zusammenzufassen:

1: Anhand der einfachen Methode der Assembly-Anweisung können wir erkennen, dass die Runnable.run-Methode ausgeführt wird;

2: Während der tatsächlichen Laufzeit, wenn die JVM auf die invokedynamic-Anweisung trifft Bei der einfachen Methode wird die LambdaMetafactory.metafactory-Methode dynamisch aufgerufen und die spezifische Runnable.run-Methode ausgeführt. 🎜🎜Die spezifische Ausführung des Lambda-Ausdruckswerts kann also der invokedynamic JVM-Anweisung zugeschrieben werden. Genau aufgrund dieser Anweisung wissen Sie zwar nicht, was beim Kompilieren zu tun ist, können aber den spezifischen Code finden, der während des Kompilierens ausgeführt werden soll dynamische Laufzeit. 🎜

Dann werfen wir einen Blick auf das Ende der Assembler-Anweisungsausgabe. Wir haben die in der Ausnahmebeurteilungsmethode gefundene interne Klasse gefunden, wie unten gezeigt:

Zusammenfassung der Java-Kenntnisse: So lesen Sie Lambda-Quellcode

Das obige Bild enthält viele Pfeile und die Layer-By -Layer-Ausdruck drückt alle Informationen der aktuellen internen Klasse klar aus.

4. Zusammenfassend lässt sich sagen, dass die Ausführung von Lambda-Ausdrücken hauptsächlich auf der JVM-Anweisung von invokedynamic basiert. Der vollständige Pfad der von uns demonstrierten Klasse ist: demo.eight.Lambda.

Ohne weitere Umschweife ist der Artikel zu Ende, wir freuen uns auf die dritte Serie!

Empfohlenes Lernen: „

Java-Video-Tutorial

Das obige ist der detaillierte Inhalt vonZusammenfassung der Java-Kenntnisse: So lesen Sie Lambda-Quellcode. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:csdn.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen