Явное поведение Java при возврате из блока finally

Попробуйте этот фрагмент кода. Почему getValueB() возвращает 1 вместо 2? В конце концов, функция increment() вызывается дважды.

    public class ReturningFromFinally
    {
      public static int getValueA() // This returns 2 as expected
      {
         try     { return 1; }
         finally { return 2; }
      }

      public static int getValueB() // I expect this to return 2, but it returns 1
      {
        try     { return increment(); }
        finally { increment(); }
      }

      static int counter = 0;

      static int increment()
       {
          counter ++;
          return counter;
       }

      public static void main(String[] args)
      {
          System.out.println(getValueA()); // prints 2 as expected
          System.out.println(getValueB()); // why does it print 1?
      }
}

Ответ 1

В конце концов, функция increment() вызывается дважды.

Да, но возвращаемое значение определяется перед вторым вызовом.

Возвращаемое значение определяется оценкой выражения в операторе return в этот момент времени, а не "перед тем, как выполнение оставит метод".

От раздел 14.17 JLS:

Оператор return с выражением пытается передать управление вызывающему методу, который содержит его; значение выражения становится значением вызова метода. Точнее, выполнение такого оператора return сначала оценивает выражение. Если по какой-либо причине оценка Expression неожиданно завершается, то по этой причине оператор return завершается внезапно. Если оценка Expression завершается нормально, создавая значение V, то оператор возврата завершается внезапно, причиной является возврат со значением V.

Выполнение затем переносится в блок finally, согласно разделу 14.20.2 JLS. Это не переоценивает выражение в инструкции return.

Если ваш блок finally был:

finally { return increment(); }

то новое возвращаемое значение будет конечным результатом метода (согласно разделу 14.20.2), но вы этого не делаете.

Ответ 2

Смотрите мой комментарий.

Он вернет 2, если у вас есть finally { return increment(); }. Первое выражение выражения return выполняется перед блоком finally. См. Раздел §14.20.2 JLS.

Если выполнение блока try завершается нормально, выполняется блок finally, а затем есть выбор:

  • Если блок finally завершается нормально, то инструкция try выполняется нормально.
  • Если блок finally неожиданно завершает работу по причине S, то оператор try завершается внезапно для причины S.

Вызов getValue2 (как у вас сейчас) дважды приведет к 1, за которым следует 3.

Ответ 3

блок finally в GetValue2 не возвращает ничего. Он только вызывает метод для увеличения counter.

Ответ 4

Потому что в методе getValue2() вы, наконец, блокируете только вызовы increment(), он не возвращает его. Так что ваш код делает, он увеличивает и возвращает счетчик (1), а затем увеличивает счетчик на 2, но не возвращает его.

Ответ 5

У вас нет явного возврата во втором примере. В этом случае он вернет значение в блоке try. Это делает интуитивный смысл, потому что Java уже выполнил код внутри блока try. После выполнения блока finally это не будет выполнено после выполнения блока.

Ответ 6

Цель метода finally состоит в том, чтобы гарантировать, что ressources закрыты в любом случае. Рассмотрим пример:

public List<Person> getPersons() {
    Connection conn = openConnection();
    try {
        return selectPersons(conn);
    } finally {
        conn.close()
    }
}

Оператор conn.close() выполняется ПОСЛЕ selectPersons (conn). В противном случае selectPersons (conn) вызовет ошибку закрытия соединения.