Компилятор Java генерирует дополнительные методы, которые появляются в следах стека. Сначала это может сбить с толку. Кто они и почему они существуют?
Модель доступа в JVM
Модель доступа в JVM существенно не изменилась с версии 1.0. Однако в Java 5.0 добавлены такие функции, как внутренние / вложенные классы и конвариантные возвращаемые типы, которых не было в оригинальном проекте. Так как они поддерживаются?
Проблема с вложенными классами
Вложенные классы могут обращаться к закрытым методам и полям внешнего класса. Однако JVM не поддерживает это, поэтому компилятор генерирует методы в качестве обходного пути.
Частные Методы
В этом примере внутренний класс вызывает закрытый метод
init () внешнего класса.
public class PrivateMethod { // line 4 private void init() { throw new UnsupportedOperationException(); // line 6 }; class Inner { Inner() { init(); } } public static void main(String... args) { PrivateMethod pm = new PrivateMethod(); Inner inner = pm.new Inner(); } }
Так как же выглядит трассировка стека, когда мы запускаем это?
Exception in thread "main" java.lang.UnsupportedOperationException at com.google.code.java.core.javac.generated.PrivateMethod.init(PrivateMethod.java:6) at com.google.code.java.core.javac.generated.PrivateMethod.access$000(PrivateMethod.java:4) at com.google.code.java.core.javac.generated.PrivateMethod$Inner.(PrivateMethod.java:11) at com.google.code.java.core.javac.generated.PrivateMethod.main(PrivateMethod.java:17)
В трассировке стека есть метод с именем
access $ 000 в строке 4, которая является первой строкой класса. Это настоящий метод? Что мы видим в байт-коде?
$ javap -c -private -classpath . com.google.code.java.core.javac.generated.PrivateMethod static void <b>access$000</b>(com.google.code.java.core.javac.generated.PrivateMethod); Code: 0: aload_0 1: invokespecial #1; //Method init:()V 4: return $ javap -c -private -classpath . com.google.code.java.core.javac.generated.PrivateMethod\$Inner Compiled from "PrivateMethod.java" public class com.google.code.java.core.javac.generated.PrivateMethod$Inner extends java.lang.Object{ final com.google.code.java.core.javac.generated.PrivateMethod this$0; com.google.code.java.core.javac.generated.PrivateMethod$Inner(com.google.code.java.core.javac.generated.PrivateMethod); Code: 0: aload_0 1: aload_1 2: putfield #1; //Field this$0:Lcom/google/code/java/core/javac/generated/PrivateMethod; 5: aload_0 6: invokespecial #2; //Method java/lang/Object."":()V 9: aload_1 10: invokestatic #3; //Method com/google/code/java/core/javac/generated/PrivateMethod. <b>access$000</b>:(Lcom/google/code/java/core/javac/generated/PrivateMeth 13: return }
Компилятор добавил статический метод, который не является закрытым, поэтому класс Inner может косвенно обращаться к закрытому методу внешнего класса.
Частные поля
Доступ к закрытым полям осуществляется через методы получения и установки, но компилятор может генерировать методы для операций присваивания, такие как
++ и
* =
public class PrivateField { private int num = 0; public class Inner { public void set(int n) { num = n; } public int get() { return num; } public void increment() { num++; } public void multiply(int n) { num *= n; } } }
Компилируется с четырьмя сгенерированными методами, по одному для каждого из get, set, ++ и * =
$ javap -c -private -classpath . com.google.code.java.core.javac.generated.PrivateField <b>setter</b> static int access$002(com.google.code.java.core.javac.generated.PrivateField, int); Code: 0: aload_0 1: iload_1 2: dup_x1 3: putfield #1; //Field num:I 6: ireturn <b>getter</b> static int access$000(com.google.code.java.core.javac.generated.PrivateField); Code: 0: aload_0 1: getfield #1; //Field num:I 4: ireturn increment ++ static int access$008(com.google.code.java.core.javac.generated.PrivateField); Code: 0: aload_0 1: dup 2: getfield #1; //Field num:I 5: dup_x1 6: iconst_1 7: iadd 8: putfield #1; //Field num:I 11: ireturn multiplier *= static int access$028(com.google.code.java.core.javac.generated.PrivateField, int); Code: 0: aload_0 1: dup 2: getfield #1; //Field num:I 5: iload_1 6: imul 7: dup_x1 8: putfield #1; //Field num:I 11: ireturn
Ковариантные типы возврата
Ковариантные возвращаемые типы — это функция, которая была добавлена в Java 5.0, но не поддерживается в JVM. По этой причине компилятор генерирует метод, который переопределяет все супер методы и их возвращаемые типы.
public class CovariantReturnTypeA { public Object method() { return 1L; } } public class CovariantReturnTypeB extends CovariantReturnTypeA { public Number method() { return 2.0; } } public class CovariantReturnTypeC extends CovariantReturnTypeB { public Integer method() { return 3; } }
Последний метод () переопределяет метод, который возвращает Number, и метод, который возвращает Object. Чтобы реализовать это, компиляция генерирует метод с той же сигнатурой, который вызывает метод, который возвращает Integer
$ javap -c -private -classpath . com.google.code.java.core.javac.generated.CovariantReturnTypeC public java.lang.Integer method(); Code: 0: iconst_3 1: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 4: areturn public java.lang.Number method(); Code: 0: aload_0 1: invokevirtual #3; //Method method:()Ljava/lang/Integer; 4: areturn public java.lang.Object method(); Code: 0: aload_0 1: invokevirtual #3; //Method method:()Ljava/lang/Integer; 4: areturn
В байт-коде есть три реализации метода ().
Последствия для производительности
Влияние производительности на Java Standard Edition невелико, поскольку эти методы могут быть встроенными и эффективно не оказывать влияния. В Java Mobile Edition это может быть не так.
Они могут вызвать некоторую путаницу, и если это проблема, вы можете вместо этого сделать поля / методы локальными для пакета, и сгенерированные методы исчезнут.
От http://vanillajava.blogspot.com/2011/07/java-secret-generated-methods.html