2010-07-12 2 views
5

Я написал семь тестовых примеров для понимания поведения блока finally. Какова логика работы finally?Понимание блока «finally»

package core; 

public class Test { 
    public static void main(String[] args) { 
     new Test().testFinally(); 
    } 

    public void testFinally() { 
     System.out.println("One = " + tryOne()); 
     System.out.println("Two = " + tryTwo()); 
     System.out.println("Three = " + tryThree()); 
     System.out.println("Four = " + tryFour()); 
     System.out.println("Five = " + tryFive()); 
     System.out.println("Six = " + trySix()); 
     System.out.println("Seven = " + trySeven()); 
    } 

    protected StringBuilder tryOne() { 
     StringBuilder builder = new StringBuilder(); 
     try { 
      builder.append("Cool"); 
      return builder.append("Return"); 
     } 
     finally { 
      builder = null; 
     } 
    } 

    protected String tryTwo() { 
     String builder = "Cool"; 
     try { 
      return builder += "Return"; 
     } 
     finally { 
      builder = null; 
     } 
    } 

    protected int tryThree() { 
     int builder = 99; 
     try { 
      return builder += 1; 
     } 
     finally { 
      builder = 0; 
     } 
    } 

    protected StringBuilder tryFour() { 
     StringBuilder builder = new StringBuilder(); 
     try { 
      builder.append("Cool"); 
      return builder.append("Return"); 
     } 
     finally { 
      builder.append("+1"); 
     } 
    } 

    protected int tryFive() { 
     int count = 0; 
     try { 
      count = 99; 
     } 
     finally { 
      count++; 
     } 
     return count; 
    } 

    protected int trySix() { 
     int count = 0; 
     try { 
      count = 99; 
     } 
     finally { 
      count = 1; 
     } 
     return count; 
    } 

    protected int trySeven() { 
     int count = 0; 
     try { 
      count = 99; 
      return count; 
     } 
     finally { 
      count++; 
     } 
    } 
} 

Почему builder = null не работает?

Почему builder.append("+1") работа тогда count++trySeven()) делает не работу?

ответ

11

После того, как вы сделаете возврат, единственный способ переопределить это - сделать еще один возврат (как обсуждалось в Returning from a finally block in Java, это почти всегда плохая идея), или иначе завершаться внезапно. Ваши тесты никогда не возвращаются от наконец.

JLS § 14.1 определяет крутое завершение. Одним из резких типов завершения является возврат. Блок try в 1,2,3,4 и 7 резко завершается из-за возврата. Как объясняется в § 14.20.2, если блок try завершается внезапно по причине R, кроме броска, блок finally немедленно выполняется.

Если блок finally завершается нормально (что не подразумевает возврата, между прочим), «оператор try внезапно завершается по причине R.». Другими словами, возврат, инициированный попыткой, остается нетронутым; это относится ко всем вашим тестам. Если вы вернетесь из окончательно, «оператор try завершится внезапно для разума S (и причина R отбрасывается)». (S здесь является новым переопределяющим возвратом).

Так tryOne, если вы сделали:

finally { 
      builder = null; 
      return builder; 
     } 

это новое возвращение S будет переопределять оригинальный обратный R.

Для builder.append("+1") в tryFour, имейте в виду StringBuilder изменяемые, так что вы все еще возвращая ссылку на тот же объект, указанный в try. Вы просто делаете мутацию в последнюю минуту.

tryFive и trySix - прямолинейные. Поскольку в попытке нет возврата, попытка и, наконец, оба завершаются нормально, и она выполняется так же, как если бы не было попытки - наконец.

-3

StringBuilder не может быть пустым, он ожидает строковое значение.

Нулевые аргументы, как правило, плохие при работе со строками. count ++ не объявлен ??? builder.append ("") вы добавляете строку - хорошо. count = count ++;

+2

Символ 'StringBuilder' ссылка, безусловно, может быть нулевым. Объявлен 'count'. –

+0

даже после создания экземпляра? –

+4

Каждый раз. 'StringBuilder sb = new StringBuilder(); sb = null; 'является юридическим кодом (даже если это не очень полезно в этом случае). StringBuilder - это ссылочный тип, а * любой * ссылочный тип может быть нулевым. – cHao

0

builder = null и builder.append("+1") Работает. Просто они не влияют на то, что вы возвращаете. Функция возвращает то, что имеет оператор return, независимо от того, что происходит потом.

Причина в том, что существует разница в том, что builder передается по ссылке. builder=null копия builder. builder.append("+1") влияет на копию, хранящуюся у родителя.

2

Давайте начнем с использования, которое вы увидите чаще всего - у вас есть ресурс, который вы должен, чтобы избежать утечки.

public void deleteRows(Connection conn) throws SQLException { 
    Statement statement = conn.createStatement(); 
    try { 
     statement.execute("DELETE * FROM foo"); 
    } finally { 
     statement.close(); 
    } 
} 

В этом случае мы должны закрыть инструкцию, когда мы закончили, поэтому мы не утечка ресурсов базы данных. Это гарантирует, что в случае исключения Исключения мы всегда будем закрывать наше заявление до выхода функции.

try {...} finally {...} блоки предназначены для обеспечения того, что что-то всегда будет выполняться при завершении метода. Это наиболее полезно для случаев исключения. Если вы обнаружите, что делаете что-то вроде этого:

public String thisShouldBeRefactored(List<String> foo) { 
    try { 
     if(foo == null) { 
      return null; 
     } else if(foo.length == 1) { 
      return foo.get(0); 
     } else { 
      return foo.get(1); 
     } 
    } finally { 
     System.out.println("Exiting function!"); 
    } 
} 

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

public String thisShouldBeRefactored(List<String> foo) { 
    final String result; 

    if(foo == null) { 
     result = null; 
    } else if(foo.length == 1) { 
     result = foo.get(0); 
    } else { 
     result = foo.get(1); 
    } 

    System.out.println("Exiting function!"); 

    return result; 
} 
+0

приветствую Роберта;) – HanuAthena

0

Почему builder = null не работает?
Поскольку вы устанавливаете локальную ссылку на нуль, которая не изменит содержимое памяти. Таким образом, он работает, если вы попытаетесь получить доступ к строителю после окончательного блока, тогда вы получите нуль.
Почему builder.append("+1") work?
Поскольку вы изменяете содержимое памяти с помощью ссылки, поэтому она должна работать.
Почему count++ не работает в testFive()?
Он отлично работает со мной. Он выводит 100, как ожидалось.

0

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

protected StringBuilder tryOne() { 
    StringBuilder builder = new StringBuilder(); 
    try { 
     builder.append("Cool"); 
     builder.append("Return"); 
     StringBuilder temp = builder; 
     return temp; 
    } finally { 
     builder = null; 
    } 
} 

Или, с точки зрения того, что заявления на самом деле получить выполненный (игнорируя возможные исключения, конечно), это выглядит примерно так:

protected StringBuilder tryOne() { 
    StringBuilder builder = new StringBuilder(); 
    builder.append("Cool"); 
    builder.append("Return"); 
    StringBuilder temp = builder; 
    builder = null; 
    return temp; 
} 

Так что установка builder = null действительно запускается, она просто ничего не делает. Тем не менее, работа builder.append("something")будет иметь видимый эффект, поскольку и temp, и builder ссылаются на один и тот же (изменяемый) объект.

Кроме того, что действительно происходит в trySeven() что-то больше, как это:

protected int trySeven() { 
    int count = 0; 
    count = 99; 
    int temp = count; 
    count++; 
    return temp; 
} 

В этом случае, так как мы имеем дело с междунар, копии независимы, поэтому приращением один не делает влияют на другие.

Все, что сказано, остается фактом, что включение операторов return в блок try-finally довольно сбивает с толку, поэтому, если у вас есть какой-либо выбор в этом вопросе, вам лучше переписать вещи так, чтобы все ваши операторы return находятся за пределами блоков try-finally.

2

Блок finally выполняется, когда вы покидаете блок try. Оператор «return» выполняет две функции: одну устанавливает возвращаемое значение функции, а два - из функции. Обычно это будет выглядеть как атомная операция, но в блоке try это приведет к тому, что блок finally будет выполнен после того, как будет установлено возвращаемое значение и до выхода функции.

исполнение

Возврата:

  1. Присвоить возвращаемое значение
  2. запустить, наконец, блоки
  3. выхода функция

Пример один (примитив):

int count = 1;//Assign local primitive count to 1 
try{ 
    return count; //Assign primitive return value to count (1) 
}finally{ 
    count++ //Updates count but not return value 
} 

Пример два (ссылка):

StringBuilder sb = new StringBuilder();//Assign sb a new StringBuilder 
try{ 
    return sb;//return a reference to StringBuilder 
}finally{ 
    sb.append("hello");//modifies the returned StringBuilder 
} 

Пример три (ссылка):

StringBuilder sb = new StringBuilder();//Assign sb a new StringBuilder 
    try{ 
     return sb;//return a reference to StringBuilder 
    }finally{ 
     sb = null;//Update local reference sb not return value 
    } 

Пример четыре (Обратно):

int count = 1; //assign count 
    try{ 
     return count; //return current value of count (1) 
    }finally{ 
     count++;  //update count to two but not return value 
     return count; //return current value of count (2) 
        //replaces old return value and exits the finally block 
    } 
Смежные вопросы