Странное поведение?
public class Test2 { public static void main(String[] args) { Test2 obj=new Test2(); String a=obj.go(); System.out.print(a); } public String go() { String q="hii"; try { return q; } finally { q="hello"; System.out.println("finally value of q is "+q); } }
Почему эта печать hii
после возврата из функции go()
, значение изменилось на «привет» в блоке finally?
выход программы
finally value of q is hello hii
- Почему Intellij-IDEA игнорирует мой тег comcat / conf / server.xml Context?
- Проверьте размер коллекции с помощью JSTL
- Почему «final» не допускается в методах интерфейса Java 8?
- Качание на OSX: как ловушка command-Q?
- Как преобразовать Список в другой список
- Как читать stream pdf в angularjs
- Подключение входного streamа к выходному streamу
- Java 8 Streams: несколько фильтров или сложное условие
- Транзакция, отмеченная только как откат: как мне найти причину
- Веб-скребок с Java
- getResourceAsStream () всегда возвращает null
- Java-массивы печатают странные цифры и текст
- Невозможно разобрать hibernate.cfg.xml в автономном режиме
Это потому, что вы вернули значение, которое было оценено с q
прежде чем вы изменили значение q
в блоке finally. Вы вернули q
, который оценил его значение; то вы изменили q
в блоке finally
, который не повлиял на загруженное значение; то завершение возвращается с использованием оцененного значения.
Не пишите сложный код, как это. Если это смущает парня, который его написал, представьте проблемы, которые он вызовет следующего парня, через несколько лет после того, как вы находитесь где-то в другом месте.
return
возвращает значение без ссылки. При return q;
получает выполненное в текущем значении catch
значение q
reference кэшируется методом в качестве результата. Поэтому, даже если в блоке finally
вы переназначаете q
новым значением, оно не влияет на значение, уже кэшированное методом.
Если вы хотите обновить значение, которое должно быть возвращено, вам придется использовать другой return
в своем блоке finally
например
} finally { q = "hello"; System.out.println("finally value of q is " + q); return q;//here you set other value in return }
Другим способом воздействия на возвращаемое значение является изменение состояния кэшированного объекта. Например, если q
был List
мы могли бы добавить к нему новый элемент (но обратите внимание, что изменение состояния – это не то же самое, что переназначение нового экземпляра, так же как мы можем изменить состояние final
переменной, но мы не можем переназначить его).
} finally { q.add(new Element); //this will place new element (update) in List //object stored by return because it is same object from q reference System.out.println("finally value of q is " + q); }
Наконец, выполняется после возврата, но до того, как метод действительно вернется к вызывающему. Это аналогично броску. Это происходит после броска и перед выходом из блока. Возвращаемое значение уже задано в некотором регистре, считывая переменную q. Если q было изменчивым, вы могли бы изменить его в конце, и вы увидите это изменение в вызывающем. Почему это так работает? Во-первых, это, пожалуй, наименее сложно реализовать. Два, это дает вам максимальную гибкость. Вы можете переопределить возвращаемое значение, наконец, с явным возвратом. Сохранение по умолчанию позволяет выбрать либо поведение.
[ Отредактировано после комментария от EJP, мой первый ответ не ответил на вопрос и был также неправильным. ]
Теперь мой ответ должен быть прав, объясняя, что по мере того, как блок try и блок finally завершаются нормально, q возвращается. И причина, по которой возвращается значение «hii», объясняется в ответе EJP. Я все еще ищу объяснения в JLS.
Взгляните на JLS 14.20.2 Выполнение try-catch-finally
Оператор try с блоком finally выполняется первым выполнением блока try. Тогда есть выбор:
Если выполнение блока try завершается нормально, тогда выполняется блок finally, а затем есть выбор:
Если окончательный блок завершится нормально, то инструкция try завершится нормально.
[…]
и JLS 14.17. Операция возврата
Оператор return с выражением пытается передать управление вызывающему методу, который содержит его; значение выражения становится значением вызова метода. Точнее, выполнение такого оператора возврата сначала оценивает выражение. Если по какой-либо причине оценка Expression неожиданно завершается, то по этой причине оператор return завершается внезапно. Если оценка Expression завершается нормально, создавая значение V, то оператор возврата завершается внезапно, причина – возврат со значением V
А также:
В предыдущих описаниях говорилось «попытки передать управление», а не просто «передает управление», потому что, если в методе или конструкторе есть какие-либо попытки (§14.20), в блоках try которых содержится оператор return, то любые окончательные предложения этих утверждений try быть выполненным, чтобы быть наиболее внутренним до внешнего, до того как управление передается вызывающему методу или конструктору. Резкое завершение предложения finally может привести к нарушению передачи управления, инициированного оператором return.
Попробуйте использовать StringBuffer вместо String, и вы увидите изменение …. кажется, оператор return блокирует объект, который должен быть возвращен, а не ссылка. Вы также можете проверить это, напечатав hash-код:
- объект возвращается из go ()
- объект в конце
-
объект, печатаемый с основного ()
public static void main (String [] args) {
Test obj=new Test(); StringBuffer a=obj.go(); System.out.print(a); } public StringBuffer go() { StringBuffer q=new StringBuffer("hii"); try { return q; } finally { q=q.append("hello"); System.out.println("finally value of q is "+q); } }
Что, наконец, блокирует?
« Определение из Java ». Блок finally всегда выполняется, когда блок try завершается. Это гарантирует, что блок finally будет выполнен, даже если произойдет непредвиденное исключение ».
Таким образом, он печатает «окончательное значение q – привет», как только он существует, блок try и переходит в строку System.out.print (a); и выводит значение, возвращаемое методом go ().
Если у вас есть отладчики, такие как netbeans или eclipse, его можно проанализировать, сохранив точку останова и просыпаясь через код.
Ну, я нашел следующее:
Возврат фактически возвращает значение и его копируется в String a=obj.go();
, прежде чем исполнение будет завершено .
Давайте проверим его в следующих экспериментах.
public class Test2 { public static void main(String[] args) { Test2 obj=new Test2(); String a=obj.go(); System.out.print(a); } public String go() { String q="hii"; try { return q; } finally { q="hello"; System.out.println("finally value of q is "+q); } }
выход программы
наконец, значение q приветствует
HII
и если вместо String взять StringBuffer,
public class Test2 { public static void main(String[] args) { // TODO Auto-generated method stub Test2 obj=new Test2(); StringBuffer a=obj.go(); System.out.print(a); } public StringBuffer go(){ StringBuffer q=new StringBuffer("hii"); try{ return q; } finally{ q.replace(0, q.length(), "hello"); System.out.println("finally value of q is "+q); /*return q1;*/ } } }
Выход будет,
наконец, значение q приветствует
Здравствуйте
и, наконец, если вместо String взять int,
public class Test2 { public static void main(String[] args) { // TODO Auto-generated method stub Test2 obj=new Test2(); int a=obj.go(); System.out.print(a); } public int go(){ int q=1; try{ return q; } finally{ q=2; System.out.println("finally value of q is "+q); /*return q1;*/ } } }
выход
наконец, значение q равно 2
1
**Ananlysis**
1. В первом случае верните скопированный адрес String в переменную a , а затем excecution переходит к finally, где String изменяется. Но так как в случае строк, мы не можем манипулировать ни строкой, которую строит новая строка. Таким образом, в переменной сохраняется адрес исходной строки, который печатается.
2. Во втором случае верните скопированный адрес StringBuffer в переменную a , и, наконец, этот объект StringBuffer будет обработан, а создаст новый. поэтому значение, которое хранилось в переменной a, также получает манипулирование, что видно в инструкции print.
3. В третьем случае значение int копируется в переменной a , прежде чем выполнение переходит к окончательному. и, следовательно, a получает значение 1., а затем, наконец, мы изменили значение q, которое в любом случае не изменит значение a .