Javas Synthesemethoden

In diesem Blog-Beitrag beschäftige ich mich mit dem Konzept der Java-Synthesemethoden. Der Beitrag fasst zusammen, was eine Java-Synthesemethode ist, wie eine erstellt und identifiziert werden kann und welche Auswirkungen Java-Synthesemethoden auf die Java-Entwicklung haben.

In der Java-Sprachspezifikation (Abschnitt 13.1) heißt es: "Alle vom Compiler eingeführten Konstrukte, die kein entsprechendes Konstrukt im Quellcode haben, müssen als synthetisch markiert werden, mit Ausnahme der Standardkonstruktoren und der Klasseninitialisierungsmethode." Weitere Hinweise zur Bedeutung von Synthetik in Java finden Sie in der Javadoc-Dokumentation zu Member.isSynthetic (). In der Dokumentation dieser Methode heißt es, dass "true" zurückgegeben wird, wenn dieses Mitglied vom Compiler eingeführt wurde. Ich mag diese sehr kurze Definition von "synthetisch": ein vom Compiler eingeführtes Java-Konstrukt.

Der Java-Compiler muss synthetische Methoden für verschachtelte Klassen erstellen, wenn die einschließende Klasse auf ihre mit dem privaten Modifikator angegebenen Attribute zugreift. Das nächste Codebeispiel zeigt diese Situation an.

DemonstrateSyntheticMethods.java (Einschließende Klasse ruft ein privates Attribut einer verschachtelten Klasse auf)

package dustin.examples; import java.util.Calendar; import static java.lang.System.out; public final class DemonstrateSyntheticMethods { public static void main(final String[] arguments) { DemonstrateSyntheticMethods.NestedClass nested = new DemonstrateSyntheticMethods.NestedClass(); out.println("String: " + nested.highlyConfidential); } private static final class NestedClass { private String highlyConfidential = "Don't tell anyone about me"; private int highlyConfidentialInt = 42; private Calendar highlyConfidentialCalendar = Calendar.getInstance(); private boolean highlyConfidentialBoolean = true; } } 

Der obige Code wird ohne Zwischenfälle kompiliert. Wenn Javap für die kompilierte .classDatei ausgeführt wird, erfolgt die Ausgabe wie im folgenden Screenshot gezeigt.

Wie aus dem obigen Screenshot hervorgeht, access$100wurde für die verschachtelte Klasse eine synthetische Methode mit dem Namen erstellt NestedClass, um der einschließenden Klasse ihre private Zeichenfolge bereitzustellen. Beachten Sie, dass die Synthesemethode nur für das einzelne private Attribut der NestedClass hinzugefügt wird, auf das die einschließende Klasse zugreift. Wenn ich die einschließende Klasse ändere, um auf alle privaten Attribute von NestedClass zuzugreifen, werden zusätzliche Synthesemethoden generiert. Das nächste Codebeispiel zeigt genau dies und der darauf folgende Screenshot zeigt, dass in diesem Fall vier Synthesemethoden generiert werden.

DemonstrateSyntheticMethods.java (Einschließende Klasse ruft vier private Attribute verschachtelter Klassen auf)

package dustin.examples; import java.util.Calendar; import static java.lang.System.out; public final class DemonstrateSyntheticMethods { public static void main(final String[] arguments) { DemonstrateSyntheticMethods.NestedClass nested = new DemonstrateSyntheticMethods.NestedClass(); out.println("String: " + nested.highlyConfidential); out.println("Int: " + nested.highlyConfidentialInt); out.println("Calendar: " + nested.highlyConfidentialCalendar); out.println("Boolean: " + nested.highlyConfidentialBoolean); } private static final class NestedClass { private String highlyConfidential = "Don't tell anyone about me"; private int highlyConfidentialInt = 42; private Calendar highlyConfidentialCalendar = Calendar.getInstance(); private boolean highlyConfidentialBoolean = true; } } 

Wie die beiden vorherigen Codefragmente oben und die zugehörigen Bilder zeigen, führt der Java-Compiler nach Bedarf Synthesemethoden ein. Wenn die einschließende Klasse nur auf eines der privaten Attribute der verschachtelten Klasse zugreift, access$100wurde vom Compiler nur eine synthetische Methode ( ) erstellt. Wenn jedoch alle vier private Attribute der geschachtelten Klasse von der umschließenden Klasse zugegriffen wurde, vier entsprechende Synthesemethoden wurden durch den Compiler erzeugt ( access$100, access$200, access$300und access$400).

In allen Fällen, in denen eine umschließende Klasse auf die privaten Daten ihrer verschachtelten Klasse zugreift, wurde eine synthetische Methode erstellt, um diesen Zugriff zu ermöglichen. Was passiert, wenn die verschachtelte Klasse einen Accessor für ihre privaten Daten bereitstellt, den die einschließende Klasse verwenden kann? Dies wird in der nächsten Codeliste und in ihrer Ausgabe gezeigt, wie im nächsten Screenshot gezeigt.

DemonstrateSyntheticMethods.java mit Public Accessor der verschachtelten Klasse für private Daten

package dustin.examples; import java.util.Calendar; import java.util.Date; import static java.lang.System.out; public final class DemonstrateSyntheticMethods { public static void main(final String[] arguments) { DemonstrateSyntheticMethods.NestedClass nested = new DemonstrateSyntheticMethods.NestedClass(); out.println("String: " + nested.highlyConfidential); out.println("Int: " + nested.highlyConfidentialInt); out.println("Calendar: " + nested.highlyConfidentialCalendar); out.println("Boolean: " + nested.highlyConfidentialBoolean); out.println("Date: " + nested.getDate()); } private static final class NestedClass { private String highlyConfidential = "Don't tell anyone about me"; private int highlyConfidentialInt = 42; private Calendar highlyConfidentialCalendar = Calendar.getInstance(); private boolean highlyConfidentialBoolean = true; private Date date = new Date(); public Date getDate() { return this.date; } } } 

Der obige Screenshot zeigt, dass der Compiler keine synthetische Methode für den Zugriff auf das private Date-Attribut in der verschachtelten Klasse generieren musste, da die einschließende Klasse über die angegebene getDate()Methode auf dieses Attribut zugegriffen hat . Selbst getDate()wenn dies bereitgestellt wäre, hätte der Compiler eine synthetische Methode für den Zugriff auf den datebeiliegenden Code generiert, um datedirekt (als Eigenschaft) und nicht über die Zugriffsmethode auf das Attribut zuzugreifen .

Der letzte Screenshot zeigt eine weitere Beobachtung. Wie die neu hinzugefügte getDate()Methode in diesem Screenshot zeigt, sind Modifikatoren wie publicdie in der Javap-Ausgabe enthalten. Da für die vom Compiler erstellten Synthesemethoden kein Modifikator angezeigt wird, wissen wir, dass sie auf Paketebene (oder paketprivat) sind. Kurz gesagt, der Compiler hat paketprivate Methoden für den Zugriff auf private Attribute erstellt.

Die Java Reflection APIs bieten einen weiteren Ansatz zur Bestimmung von Synthesemethoden. Die nächste Codeliste bezieht sich auf ein Groovy-Skript, das die Java-Reflektions-APIs verwendet, um bequem Details zu den oben gezeigten Methoden der verschachtelten Klasse bereitzustellen.

ReflectOnMethods.groovy

#!/usr/bin/env groovy import java.lang.reflect.Method import java.lang.reflect.Modifier if (args == null || args.size() < 2) { println "Outer and nested class names must be provided." println "\nUsage #1: reflectOnMethods qualifiedOuterClassName nestedClassName\n" println "\nUsage #2: groovy -cp classpath reflectOnMethods.groovy qualifiedOuterClassName nestedClassName\n" println "\t1. Include outer and nested classes on classpath if necessary" println "\t2. Do NOT include \$ on front of nested class name.\n" System.exit(-1) } def enclosingClassName = args[0] def nestedClassName = args[1] def fullNestedClassName = enclosingClassName + '$' + nestedClassName def enclosingClass = Class.forName(enclosingClassName) Class nestedClass = null enclosingClass.declaredClasses.each { if (!nestedClass && fullNestedClassName.equals(it.name)) { nestedClass = it } } if (nestedClass == null) { println "Unable to find nested class ${fullNestedClassName}" System.exit(-2) } // Use declaredMethods because don't care about inherited methods nestedClass.declaredMethods.each { print "\nMethod '${it.name}' " print "is ${getScopeModifier(it)} scope, " print "${it.synthetic ? 'is synthetic' : 'is NOT synthetic'}, and " println "${it.bridge ? 'is bridge' : 'is NOT bridge'}." } def String getScopeModifier(Method method) { def modifiers = method.modifiers def isPrivate = Modifier.isPrivate(modifiers) def isPublic = Modifier.isPublic(modifiers) def isProtected = Modifier.isProtected(modifiers) String scopeString = "package-private" // default if (isPublic) { scopeString = "public" } else if (isProtected) { scopeString = "protected" } else if (isPrivate) { scopeString = "private" } return scopeString } 

Wenn das obige Groovy-Skript für die oben gezeigte Klasse und verschachtelte Klasse ausgeführt wird, ist die Ausgabe die im nächsten Screenshot gezeigte.

Die Ergebnisse des im vorherigen Bild gezeigten Groovy-Skripts bestätigen, was Javap uns bereits gesagt hat: In der verschachtelten Klasse sind vier Synthesemethoden und eine nichtsynthetische Methode definiert NestedClass. Das Skript sagt uns auch, dass die vom Compiler generierten Synthesemethoden paketprivat sind.

Das Hinzufügen von Synthesemethoden zur verschachtelten Klasse auf Ebene des paketprivaten Bereichs ist nicht das einzige, was der Compiler im obigen Beispiel getan hat. Außerdem wurde der Bereich der verschachtelten Klasse selbst von der privaten Einstellung im Code in package-private in der .classDatei geändert . Während die Synthesemethoden nur für den Fall hinzugefügt wurden, dass die einschließende Klasse auf das private Attribut zugegriffen hat, macht der Compiler die verschachtelte Klasse immer paketprivat, selbst wenn sie im Code als privat angegeben ist. Die gute Nachricht ist, dass dies ein resultierendes Artefakt des Kompilierungsprozesses ist, was bedeutet, dass Code nicht unverändert gegen die geänderte Gültigkeitsbereichsebene der verschachtelten Klasse oder ihrer Synthesemethoden kompiliert werden kann. In der Laufzeit kann es schwierig werden.

Die Klasse Rogue versucht, auf einige der NestedClass-Synthesemethoden zuzugreifen. Der Quellcode wird als Nächstes angezeigt, gefolgt von dem Compilerfehler, der beim Kompilieren dieses Rogue-Quellcodes auftritt.

Rogue.java versucht beim Kompilieren auf synthetische Methoden zuzugreifen

package dustin.examples; import static java.lang.System.out; public class Rogue { public static void main(final String[] arguments) { out.println(DemonstrateSyntheticMethods.NestedClass.getDate()); } } 

Der obige Code wird selbst für die nicht synthetische Methode nicht kompiliert getDate()und meldet diesen Fehler:

Buildfile: C:\java\examples\synthetic\build.xml -init: compile: [javac] Compiling 1 source file to C:\java\examples\synthetic\classes [javac] C:\java\examples\synthetic\src\dustin\examples\Rogue.java:9: dustin.examples.DemonstrateSyntheticMethods.NestedClass has private access in dustin.examples.DemonstrateSyntheticMethods [javac] out.println(DemonstrateSyntheticMethods.NestedClass.getDate()); [javac] ^ [javac] 1 error BUILD FAILED C:\java\examples\synthetic\build.xml:29: Compile failed; see the compiler error output for details. Total time: 1 second 

Wie aus der obigen Kompilierungsfehlermeldung hervorgeht, kann selbst auf die nicht synthetische Methode für die verschachtelte Klasse zur Kompilierungszeit nicht zugegriffen werden, da die verschachtelte Klasse einen privaten Bereich hat. In seinem Artikel Java-Unsicherheiten: Berücksichtigung von Feinheiten, die den Code gefährden können, erörtert Charlie Lai mögliche Situationen, in denen diese vom Compiler eingeführten Änderungen Sicherheitslücken darstellen. Faisal Feroz geht noch weiter und erklärt in dem Beitrag Wie man sicheren Java-Code schreibt: "Verwenden Sie keine inneren Klassen" (Einzelheiten zu inneren Klassen als Teilmenge verschachtelter Klassen finden Sie unter Verschachtelte, innere, Mitglieds- und Top-Level-Klassen). .

Viele von uns können lange Zeit in der Java-Entwicklung arbeiten, ohne ein umfassendes Verständnis der Synthesemethoden zu benötigen. Es gibt jedoch Situationen, in denen das Bewusstsein dafür wichtig ist. Neben den damit verbundenen Sicherheitsproblemen ist es auch wichtig zu wissen, was sie beim Lesen von Stapelspuren sind. Verfahren Namen wie access$100, access$200, access$300, access$400, access$500, access$600, und access$1000in der Stapelüberwachung reflektieren synthetische vom Compiler generierten Methoden.

Originalbeitrag verfügbar unter //marxsoftware.blogspot.com/

.

Diese Geschichte "Java's Synthetic Methods" wurde ursprünglich von JavaWorld veröffentlicht.