Странное поведение?
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()
, значение было изменено на "hello" в блоке finally?
вывод программы
finally value of q is hello
hii
Ответы
Ответ 1
Это потому, что вы вернули значение, которое было оценено с q
, прежде чем вы изменили значение q
в блоке finally. Вы вернули q
, который оценил его значение; то вы изменили q
в блоке finally
, который не повлиял на загруженное значение; то завершение возвращается с использованием оцененного значения.
Не записывайте хитрый код, как это. Если он смущает парня, который его написал, представьте проблемы, которые он вызовет следующего парня, через несколько лет после того, как вы находитесь где-то в другом месте.
Ответ 2
return
возвращает значение без ссылки. Когда return q;
выполняется в catch
текущее значение q
ссылка кэшируется методом в качестве результата. Таким образом, даже если в блоке 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
, но мы можем " t переназначить его).
} 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);
}
Ответ 3
Наконец выполняется после возврата, но до того, как метод действительно вернется к вызывающему. Это аналогично броску. Это происходит после броска и перед выходом из блока. Возвращаемое значение уже задано в некотором регистре, считывая переменную q. Если q было изменчивым, вы могли бы изменить его в конце, и вы увидите это изменение в вызывающем. Почему это так работает? Во-первых, это, пожалуй, наименее сложно реализовать. Два, это дает вам максимальную гибкость. Вы можете переопределить возвращаемое значение, наконец, с явным возвратом. Сохранение по умолчанию позволяет выбрать либо поведение.
Ответ 4
[ Отредактировано после комментария от EJP, мой первый ответ не ответил на вопрос, а также был неправильным.]
Теперь мой ответ должен быть прав, объясняя, что по мере того, как блок try и блок finally завершаются нормально, q возвращается. И причина, по которой возвращается значение "hii", объясняется в ответе EJP. Я все еще ищу объяснения в JLS.
Посмотрите JLS 14.20.2 Выполнение try-catch-finally
Оператор try с блоком finally выполняется первым выполнением блока try. Тогда есть выбор:
Если выполнение блока try завершается нормально, тогда выполняется блок finally, а затем есть выбор:
Если блок finally завершается нормально, то инструкция try завершается нормально.
[...]
и JLS 14.17 Оператор возврата
Оператор return с выражением пытается передать управление вызывающему методу, который содержит его; значение выражения становится значением вызова метода. Точнее, выполнение такого оператора возврата сначала оценивает выражение. Если по какой-либо причине оценка Expression неожиданно завершается, то по этой причине оператор return завершается внезапно. Если оценка Expression завершается нормально, создавая значение V, то оператор возврата завершается внезапно, причина - возврат со значением V
и
В предыдущих описаниях говорится "попытки передать управление", а не просто "передает управление", потому что, если в методе или конструкторе есть какие-либо попытки (§14.20), в которых в блоках try содержатся оператор return, тогда любые окончательные положения этих операторы try будут выполняться, чтобы быть наиболее внутренним до внешнего, до того как управление передается вызывающему методу или конструктору. Резкое завершение предложения finally может привести к нарушению передачи управления, инициированного оператором return.
Ответ 5
Попробуйте использовать StringBuffer вместо String, и вы увидите изменение.... кажется, оператор return блокирует объект, который должен быть возвращен, а не ссылка. Вы также можете проверить это, напечатав хэш-код:
- объект возвращается из go()
- объект в конце
-
объект, печатаемый из main()
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);
}
}
Ответ 6
Что такое блок?
-By определение из Java "Блок finally всегда выполняется, когда блок try завершается. Это гарантирует, что блок finally будет выполнен, даже если возникает неожиданное исключение".
Итак, он печатает "окончательное значение q - это привет", как только он существует, блок try и переходит в строку System.out.print(a); и печатает значение, возвращаемое методом go().
Если у вас есть отладчики, такие как netbeans или eclipse, его можно проанализировать, сохранив точку останова и просыпаясь через код.
Ответ 7
Ну, я нашел следующее:
Возврат на самом деле возвращает значение, и его копируется в 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 означает hello
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 означает hello
привет
и, наконец, если вместо 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, также обрабатывается, что видно в инструкции печати.
3. В третьем случае значение int копируется в переменной a, прежде чем выполнение будет завершено. и, таким образом, a получает значение 1. а затем в итоге мы изменили значение q, которое в любом случае не изменит значение a.. p >