Странное ваще поведение?

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

person Harinder    schedule 25.06.2012    source источник
comment
Возможно, вам захочется прочитать это: stackoverflow.com /вопросы/65035/   -  person Harry Joy    schedule 25.06.2012
comment
Значение локальной переменной q было изменено на привет, да. Но то, что ты вернул, было привет.   -  person maksimov    schedule 25.06.2012
comment
Пожалуйста, посмотрите на мой новый ответ ... давайте попробуем уточнить эту концепцию.   -  person Ahmad    schedule 26.06.2012


Ответы (7)


Это потому, что вы вернули значение, которое было вычислено из q до того, как вы изменили значение q в блоке finally. Вы вернули q, что оценило его значение; затем вы изменили q в блоке finally, что не повлияло на загруженное значение; затем завершение возврата с использованием оцененного значения.

Не пишите такой хитрый код. Если это сбивает с толку парня, который это написал, представьте, какие проблемы это вызовет у следующего парня через несколько лет, когда вы будете где-то еще.

person user207421    schedule 25.06.2012
comment
но значение возвращается после выполнения блока finally.. поэтому - person Harinder; 25.06.2012
comment
@Dennis Возвращаемое значение было загружено до выполнения блока finally. Я уже сказал это. - person user207421; 25.06.2012
comment
@ Деннис, да, но в finally вы уже прошли инструкцию return, а не непосредственно перед ней. - person maksimov; 25.06.2012
comment
Большое спасибо, только что опубликовал этот вопрос для обновления знаний, и нет, я не буду писать такой код. - person Harinder; 25.06.2012
comment
Просто добавить: при возврате объекта возвращается ссылка на объект. Таким образом, присвоение переменной другой строке в блоке finally не влияет на возвращаемое значение. - person nhahtdh; 25.06.2012
comment
@EJP ... но когда мы возвращаемся из блока finally, как было предложено выше, мы получаем значение, установленное в finally ... как это возможно ..? .. - person Ahmad; 25.06.2012
comment
@Ahmad Потому что оператор return в блоке finally выполняется после всего остального, то есть finally. - person user207421; 25.06.2012
comment
@EJP .. пожалуйста, проверьте мой новый ответ .. и подтвердите его - person Ahmad; 26.06.2012
comment
@ Ахмад, я не знаю, почему вы просто повторяете мой ответ в комментариях под ним или почему вы просите меня под моим ответом просмотреть ваш ответ, который вы отредактировали на основе моего ответа. Если ваш ответ совпадает с моим, значит, он правильный. Вы же не можете серьезно ожидать, что я скажу что-то еще, не так ли? - person user207421; 02.08.2012
comment
@EJP ... ну, на самом деле это не было основано на вашем ответе. Я провел эксперименты и записал их результаты. В конце концов я пропустил ваш ответ и опубликовал свой комментарий. Все равно извините.. - person Ahmad; 02.08.2012
comment
@Ahmad Если ваш ответ основан на независимом эксперименте, вам не нужно, чтобы я его подтверждал. Если это противоречит моему, я, очевидно, так и скажу; если он такой же, как у меня, я, очевидно, подтвержу это, так что вам не нужно спрашивать: вам просто нужно решить, такой же ли это. Я не понимаю, зачем вам вообще нужно, чтобы я вмешивался в это в любом случае. - person user207421; 19.09.2012

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, но не можем переназначить ее).

} 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);
}
person Pshemo    schedule 25.06.2012
comment
«Значение (объект) + из+ ссылки, а не точная ссылка», «чтение/сохранение объекта из ссылки» и «объект, хранящийся в ответе» не имеют смысла. Не ответ. Вероятно, здесь есть ответ, который изо всех сил пытается найти, но терминология настолько запутана, что смысл не передается. - person user207421; 02.08.2012

Наконец выполняется после возврата, но до того, как метод фактически вернется к вызывающей стороне. Это аналог броска. Это происходит после броска и перед выходом из блока. Возвращаемое значение уже установлено в каком-то регистре чтением переменной q. Если бы q был изменяемым, вы могли бы изменить его в finally и увидеть это изменение в вызывающем объекте. Почему это работает именно так? Во-первых, это, вероятно, наименее сложно реализовать. Во-вторых, это дает вам максимальную гибкость. Вы можете переопределить возвращаемое значение в finally с помощью явного возврата. Сохранение его по умолчанию позволяет выбрать любое поведение.

person John Watts    schedule 25.06.2012

[Отредактировано после комментария от 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

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

А также:

В предыдущих описаниях говорится о попытках передачи управления, а не просто о передаче управления, потому что, если в методе или конструкторе есть какие-либо операторы try (§14.20), блоки try которых содержат оператор return, тогда будут выполнены все предложения finally этих операторов try в порядок, от внутреннего к внешнему, прежде чем управление будет передано вызывающему методу или конструктору. Внезапное завершение предложения finally может нарушить передачу управления, инициированную оператором return.

person pgras    schedule 25.06.2012
comment
Как именно это отвечает на вопрос? - person user207421; 25.06.2012
comment
Все равно не отвечает на вопрос. 'q' не возвращается. Возвращается значение q при выполнении оператора return. - person user207421; 25.06.2012

Попробуйте использовать 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);
            }
        }
    
person Twaha Mehmood    schedule 25.06.2012
comment
«Блокирует возвращаемый объект» бессмысленно. Не ответ. - person user207421; 02.08.2012

Что такое блок finally?

- Согласно определению из Java "Блок finally всегда выполняется, когда блок try завершает работу. Это гарантирует, что блок finally будет выполнен, даже если возникнет непредвиденное исключение».

Таким образом, он печатает «наконец-то значение q — привет», как только он существует в блоке try и переходит к строке System.out.print(a); и печатает значение, возвращаемое методом go().

Если у вас есть отладчики, такие как netbeans или eclipse, их можно проанализировать, сохраняя точку останова и пробуждая код.

person Reachgoals    schedule 12.01.2013

Ну, я нашел следующее,

Return на самом деле возвращает значение, и оно копируется в 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 — привет

привет

и если мы возьмем StringBuffer вместо String следующим образом,

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 — привет

привет

и, наконец, если мы возьмем int вместо String следующим образом,

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. В первом случае верните скопированный адрес строки в переменную a, затем выполнение переходит к окончательному изменению строки. Но поскольку в случае строк мы не можем манипулировать какой-либо строкой, создается новая строка. Таким образом, в переменной a сохраняется адрес исходной строки, которая выводится на печать.

2. Во втором случае вернуть скопированный адрес StringBuffer в переменную a, и, наконец, этим объектом StringBuffer манипулируют, а не создают новый. поэтому значение, которое было сохранено в переменной a, также подвергается манипулированию, что видно в операторе печати.

3. В третьем случае значение int копируется в переменную a, прежде чем выполнение перейдет к finally. и, таким образом, a получает значение 1. а затем, наконец, мы изменили значение q, которое в любом случае не меняет значение a.

person Ahmad    schedule 26.06.2012
comment
Ваши первые два примера не имеют значения. Первый — это просто неверный код, поскольку String.repace() не изменяет значение, а возвращает новое, которое вы выбрасываете. Второй изменяет значение, но, поскольку это не то, что делает OP, я не вижу смысла. Третий просто повторяет то, о чем спрашивает ОП, и повторяет ответ, который уже был дан. - person user207421; 26.06.2012