Подтвердить что ты не робот

Разрешение этой ссылки

Я был бы признателен за понимание в следующем: "Java Concurrency in Practice":

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

  • "escape" здесь просто означает, что мы, вероятно, можем вызвать метод экземпляра, прежде чем экземпляр будет полностью построен?
    Я не вижу 'this', избегая области действия экземпляра любым другим способом.
  • Как "final" предотвращает это? Есть ли какой-то аспект "final" в создании экземпляра, который мне не хватает?
4b9b3361

Ответ 1

  • Это означает вызов кода вне класса и передачу this.
    Этот код будет считать, что экземпляр полностью инициализирован и может сломаться, если это не так. Аналогично, ваш класс может предположить, что некоторые методы будут вызваны только после того, как экземпляр будет полностью инициализирован, но внешний код, вероятно, нарушит эти предположения.

  • final методы не могут быть переопределены, поэтому вы можете доверять им, чтобы они не проходили this.
    Если вы вызываете какой-либо метод не final в конструкторе для класса не final, производный класс может переопределить этот метод и передать this где угодно.
     
    Даже когда вы вызываете методы final, вам все равно нужно убедиться, что они безопасно написаны – что они не пропускают this в любом месте и сами не назовут какие-либо методы не final.

Ответ 2

"Escape" означает, что ссылка на частично построенный объект this может быть передана другому объекту в системе. Рассмотрим этот сценарий:

public Foo {
    public Foo() {
        setup();
    }

    protected void setup() {
       // do stuff
    }
}

public Bar extends Foo implements SomeListener {
    @Override protected void setup() {
        otherObject.addListener(this);
    }
}

Проблема заключается в том, что новый объект Bar регистрируется с помощью otherObject до завершения его построения. Теперь, если otherObject начинает вызывать методы на barObject, поля, возможно, не были инициализированы, или barObject в противном случае может находиться в несогласованном состоянии. Ссылка на barObject (this на себя) "ускользнула" в остальную часть системы до ее готовности.

Вместо этого, если метод setup() final на Foo, класс Bar не может помещать туда код, который сделает объект видимым до завершения конструктора Foo.

Ответ 3

Я считаю, что пример похож на

public class Foo {
    public Foo() {
        doSomething();
    }

    public void doSomething() {
        System.out.println("do something acceptable");
    }
}

public class Bar extends Foo {
    public void doSomething() {
        System.out.println("yolo");
        Zoom zoom = new Zoom(this); // at this point 'this' might not be fully initialized
    }
}

Поскольку супер-конструктор всегда вызывается первым (либо неявным, либо явно), doSomething всегда будет вызван для дочернего класса. Поскольку вышеупомянутый метод не является ни final, ни private, вы можете переопределить его в дочернем классе и делать все, что хотите, что может противоречить тому, что должно было делать Foo#doSomething().

Ответ 4

Per безопасное кодирование

Пример BAD код:

final class Publisher {
  public static volatile Publisher published;
  int num;

  Publisher(int number) {
    published = this;
    // Initialization
    this.num = number;
    // ...
  }
}   

Если инициализация объекта (и, следовательно, его построение) зависит от проверки безопасности внутри конструктора, проверка безопасности может быть обойдена, когда ненадежный вызывающий абонент получит частично инициализированный экземпляр. См. Правило OBJ11-J. Будьте осторожны, позволяя конструкторам бросать исключения для получения дополнительной информации.

final class Publisher {
  public static Publisher published;
  int num;

  Publisher(int number) {
    // Initialization
    this.num = number;
    // ...
    published = this;
  }
}

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

Правильный код:

final class Publisher {
  static volatile Publisher published;
  int num;

  Publisher(int number) {
    // Initialization
    this.num = number;
    // ...
    published = this;
  }
}

Эта ссылка, как говорят, сбежала, когда она была доступна за пределами его текущего объема. Ниже приводятся общие способы, с помощью которых ссылка может выйти:

Returning this from a non-private, overridable method that is invoked from the constructor of a class whose object is being

построен. (Для получения дополнительной информации см. Правило MET05-J. Убедитесь, что конструкторы не называют переопределяемыми методами.)     Возврат этого из не приватного метода изменчивого класса, который позволяет вызывающему персоналу косвенно манипулировать состоянием объекта. Эта обычно происходит в реализации цепочки методов; см. правило VNA04-J. Убедитесь, что вызовы прикомандированных методов являются атомарными для получения дополнительной информации.     Передача этого в качестве аргумента для метода alien, вызываемого из конструктора класса, объект которого строится.     Использование внутренних классов. Внутренний класс неявно содержит ссылку на экземпляр своего внешнего класса, если не объявлен внутренний класс статичный.     Публикация, присваивая это публичной статической переменной из конструктора класса, объект которого строится.     Выбрасывание исключения из конструктора. Это может привести к тому, что код будет уязвим для атаки финализатора; см. правило OBJ11-J. Будьте осторожны позволяя конструкторам бросать исключения для получения дополнительной информации.     Передача состояния внутреннего объекта чужой. Это позволяет способу получить эту ссылку для внутреннего объекта-члена.

В этом правиле описываются потенциальные последствия разрешения этого ссылка на бегство во время строительства объекта, включая расу условий и неправильной инициализации. Например, объявление поля как правило, гарантирует, что все потоки видят поле полностью инициализированное состояние; однако, позволяя этой ссылке уйти во время построения объекта может выставить поле другим потокам в неинициализированное или частично инициализированное состояние. Правило TSM03-J. Не публиковать частично инициализированные объекты, в которых описываются гарантии предоставляемые различными механизмами для безопасной публикации, основывается на соответствие этому правилу. Следовательно, программы не должны допускать эта ссылка на выход во время построения объекта.

В целом важно определить случаи, когда ссылка может выходить за пределы текущего контекста. В в частности, общедоступные переменные и методы должны быть тщательно тщательно.