Pull to refresh

Comments 11

Благодарю за интересный и актуальный материал. Вот только кажись опечатка:

Поля, составляющие объект, могут быть переданы виртуальной машиной в вызываемый метод через регистры CPU или т.п, ...

"т.п" скорее всего опечатка или не понятное сокращение.

С каждым годом все больше галочек, палочек, - так скоро java превратится в какашку типа rust... :(((

Большое спасибо за ваши статьи! Спасибо за эти расшифровки докладов (видео я бы ни за что не посмотрел)!

Поэтому команда Valhalla сформулировала определенные требования по присвоению значения, которые говорят, что необходимо присваивать эти значения перед вызовом конструктора суперкласса.

Если не ошибаюсь, это было запрещено на уровне исходного кода (до вызова super-конструктора), но в байт-коде можно выполнять подобные операции при обязательном условии вызова super-конструктора до выхода из самого конструктора (и такой класс успешно пройдет загрузку и валидацию). Т.е. такой фокус можно провернуть с помощью генеренного байт-кода, если очень нужно.

К сожалению это не так. Доступ к this до вызова super запрещён и это явно проверяется

Всё правильно Серёга написал, это только особенность языка, что вызов super() должен быть первым оператором в конструкторе, на уровне байт-кода это не проверяется.

Я проверил это так. Написал пару классов:

public class Parent {
  private final String name;
  public Parent(String name) { this.name = name;}
  public String getParentName() { return name;}
}

public class Child extends Parent {
  private String myName;
  public Child(String childName, String parentName) {super(parentName);this.myName=childName;}
  public String getFullName() { return "I am " + myName + " , a child of a " + getParentName();}
  public static void main(String[] args) {
    Child child = new Child("ChildClass","ParentClass");
    System.out.println(child.getFullName());
  }
}

Скомпилировал, и через javap дизассемблировал. Конструктор класса Child выглядит вот так:

  public Child(java.lang.String, java.lang.String);
    descriptor: (Ljava/lang/String;Ljava/lang/String;)V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: aload_0
         1: aload_2
         2: invokespecial #1                  // Method Parent."<init>":(Ljava/lang/String;)V
         5: aload_0
         6: aload_1
         7: putfield      #7                  // Field myName:Ljava/lang/String;
        10: return
      LineNumberTable:
        line 3: 0

С jvm-ассемблерами оказалось много возни, поэтому я просто шестнадцатеричным редактором переставил байты, чтобы стало вот так:

public Child(java.lang.String, java.lang.String);
    descriptor: (Ljava/lang/String;Ljava/lang/String;)V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: aload_0
         1: aload_1
         2: putfield      #7                  // Field myName:Ljava/lang/String;
         5: aload_0
         6: aload_2
         7: invokespecial #1                  // Method Parent."<init>":(Ljava/lang/String;)V
        10: return
      LineNumberTable:
        line 3: 0

И всё отлично работает.

Ну, значит эта jvm не проводит полную верификацию.

Я лично чаще взаимодействую с андроидом, поэтому точно знаю, что верификатор байткода dalvik явно требует, чтобы до вызова инициализатора не было вообще никакого доступа к регистру this.

Этот регистр до вызова <init> имеет специальный атрибут для проверки. Как и вообще любой другой регистр новосозданного объекта - после аллокации нового объекта, но до вызова его конструктора, доступ к нему запрещён

"Изобрели" структуры из .net. Не верю, что не знали про существование точно такого же и решающего ровно такие же проблемы типа данных в лагере конкурентов

Тут проблема в другом - как это вписать в язык и платформу, которым в этом году исполняется 30 лет, не нарушив обратную совместимость и соблюдая принятые в языке конвенции.
.net появился на 10 лет позже и очевидно учел в т.ч. опыт java, плюс на тот момент не было проблемы не сломать существующий код

До меня не сразу дошло, в чём суть проблемы Identity. Если нужно проверить, что две ссылки указывают на один и тот же объект через равенство, то им не обязательно быть именно ссылками на память, можно формировать их на основе полей объекта, и это сработает. Но проблема Identity состоит в том, что ссылки на два разных объекта с одними и теми же полями обязаны быть не равны.

Предполагаю, что если бы дизайн языка писали сегодня, сделали бы два разных оператора == и ===. Тройное равенство было бы сравнением identity, а двойное - неявный вызов null-safe Objects.equals, на уровне исходного кода естественно запрещающий сравнение объектов, которые не могут быть приведены друг к другу. Тогда бы и не было известной проблемы со сравнением Integer.valueOf(128).

Сейчас это уже невозможно, т.к. пришлось бы переопределить поведение именно ==, а делать наоборот очевидно бессмысленно.

Sign up to leave a comment.