@hirthwork

hirthwork

Меня зовут Пить
hirthwork

Всего лишь бабочка, которой снится, что она программист

Славный отзывчивый парень © https://t.me/point_im/161357

32 я читаю 94 меня читают
5780 постов
48102 комментариев
hirthwork
15 Mar 2019

Здравствуйте, мои маленькие любители поебатися с байткодом. Сегодня я вам расскажу о crucial difference различных access specifiers при объявлении мемберов inner-классов.
Слово в сторону: любой порядочный параноик объявит мембер своего класса как private, а не как либо ещё. Крайне ленивый раздолбай вообще access specifier не напишет. И вот тут-то и зарыта собака.
Возьмём вот такой класс с двумя inner-классами:

public class Main {
    class Inner {
        int field;
    }

    class Pinner {
        private int field;
    }

    int test(final Inner obj) {
        return obj.field;
    }

    int test(final Pinner obj) {
        return obj.field;
    }
}

Ничег сложного, просто два inner-класса, у одного поле int, у другого — private int. Ну и из двух разных функций обращаемся к этим полям.
Скомпилим и посмотрим на байткод:

$ javac Main.java
$ javap -p -c Main.class
Compiled from "Main.java"
public class Main {
  public Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  int test(Main$Inner);
    Code:
       0: aload_1
       1: getfield      #2                  // Field Main$Inner.field:I
       4: ireturn

  int test(Main$Pinner);
    Code:
       0: aload_1
       1: invokestatic  #3                  // Method Main$Pinner.access$000:(LMain$Pinner;)I
       4: ireturn
}

Ну ничего себе! Было private int простое, а стало золотое — вместо банального getfield получаем invokestatic, который...

$ javap -p -c Main\$Pinner.class
Compiled from "Main.java"
class Main$Pinner {
  private int field;

  final Main this$0;

  Main$Pinner(Main);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #2                  // Field this$0:LMain;
       5: aload_0
       6: invokespecial #3                  // Method java/lang/Object."<init>":()V
       9: return

  static int access$000(Main$Pinner);
    Code:
       0: aload_0
       1: getfield      #1                  // Field field:I
       4: ireturn
}

... внутри уже сам делает getfield, т.е. на ровном месте получили лишний вызов функции. А ещё когда вы сделаете что-то вроде:

Pinner pinner = null;
return pinner.field;

То в стектрейсе NullPointerException вы получите лишний фрейм с этим самым Main$Pinner.access$000, который неопытного программиста просто вводит в ступор.

В общем, я сейчас борюсь с желанием повсюду в коде поудалять все private.

Рекомендовано: igelko
15 Mar 2019

а они сами запилить оптимизацию не могут небось из-за какого-нибудь дурацкого обещания, что доступ к полям для private работает именно так.

19 Mar 2019

Всё норм. Разница в производительности заметна только при -Xint. После JIT разницы в скорости нет.

Добавить пост

Вы можете выбрать до 10 файлов общим размером не более 10 МБ.
Для форматирования текста используется Markdown.