Java возвращает ссылку или объект? Вернуться раньше, наконец?

Давайте рассмотрим тестовый класс

import java.util.Date;


public class TestClass {

    public String finallyHappensBeforeReturn(){
        try{
            return "Important Message";
        }finally{
            finallyHappensBeforeReturn();
        }
    }

    public String unaffectedReference(){
        String message = "Important msg";
        try{
            return message;
        }finally{
            message = " meaning of life";
        }
    }

    public Container modifiedReference(){
        Container c = new Container();
        c.set("Important msg");
        try{
            return c;
        }finally{
            c.set("Meaning of life");
        }
    }

    public void timer(){
        try{
            System.out.println("Try time = " + new Date().getTime());
        }finally{
            System.out.println("Finally time = " + new Date().getTime());
        }
    }

    class Container{
        String s;
        void set(String s){
            this.s = s;
        }

        String get(){
            return s;
        }
    }
}

Если мы создадим основной метод следующим образом:

public static void main(String[] args){
    TestClass instance = new TestClass();
    instance.timer();
}

Мы можем ясно видеть вывод, который предполагает, что try происходит раньше, чем finally, как мы и ожидали. Для большинства из нас печатать время бессмысленно, так как на моей, а также на большинстве других машин метод будет выполняться менее чем за миллисекунду, но, тем не менее, я решил включить его.

Если мы изменим main на

public static void main(String[] args){
    TestClass instance = new TestClass();
    System.out.println(instance.unaffectedReference());
}

мы получаем напечатанное «Важное сообщение», что предполагает, что unaffectedReference() возвращает ссылку на строковый литерал «Важное сообщение», мы печатаем его в консоль, и только после этого указатель изменяется, чтобы указывать на объект String «Смысл жизни». Пока имеет смысл.

Однако, если мы изменим main на

public static void main(String[] args){
    TestClass instance = new TestClass();
    System.out.println(instance.modifiedReference().get());
}

получаем "Смысл жизни". Обратите внимание, что мы не сохраняем ссылку на возвраты ContainermodifiedReference(). Итак, если бы это было

public static void main(String[] args){
    TestClass instance = new TestClass();
    Container container = instance.modifiedReference();
    System.out.println(container.get());
}

это имело бы смысл. modifierReference() возвращает ссылку, затем переходит в finally{}, меняет объект на ссылку, ну и ссылки - и только потом печатается значение. Что там произошло? наконец, выполняется до System.out.print(), но ПОСЛЕ возврата? Как это возможно?

И, последний пример -

public static void main(String[] args){
    TestClass instance = new TestClass();
    System.out.println(instance.finallyHappensBeforeReturn());
}

В этом случае мы получаем StackOverflowError, что также говорит о том, что finally происходит перед возвратом. Обратите внимание, что «Важное сообщение» никогда не печатается до того, как будет выдано исключение.

Итак, вопрос в том, что было раньше - наконец или возвращение?


person aiguy    schedule 05.08.2015    source источник


Ответы (1)


Не имеет большого отношения к тому, что выполняется после чего (это попытка, перехват, возврат, наконец - наконец, выполняется после возврата, но до фактического возврата метода, поэтому технически он МОЖЕТ изменить возвращаемое значение - вы просто не должны сделай это). Главное — знать, что String неизменяем.

   public String unaffectedReference(){
    String message = "Important msg";
    try{
        return message;
    }finally{
        message = " meaning of life";
    }
   }

Это вернет «Важное сообщение», а затем изменит сообщение (в методе) на «смысл жизни». Но возвращаемое значение является ссылкой на строку «Важное сообщение», а не на «сообщение». Если бы String НЕ был неизменным, и вы должны были сделать, например, message.setValue ("смысл жизни"), то это было бы распечатано.

 public Container modifiedReference(){
    Container c = new Container();
    c.set("Important msg");
    try{
        return c;
    }finally{
        c.set("Meaning of life");
    }
}

... ваше возвращаемое значение является ссылкой на объект-контейнер, и изменение чего-либо ВНУТРИ этого объекта-контейнера, конечно, изменяет возвращаемое значение, потому что вы возвращаете объект, и этот объект изменяется.

person Florian Schaetz    schedule 05.08.2015
comment
Я использовал String именно потому, что он неизменяем, а Container — потому что это не так. Я думаю, что мог бы использовать Integer, так как он тоже есть, но эй. В случае с возвратом контейнера в третьем примере возникает вопрос, почему System.out.println() не вызывается ДО того, как возвращенный контейнер будет изменен? Как вы сказали, наконец, это происходит после оператора return, но «до того, как метод действительно вернется» - это часть, в которой я не совсем понимаю. Вы могли бы подумать, что после того, как метод вернет, должен быть выполнен следующий оператор. - person aiguy; 05.08.2015
comment
Пока вы находитесь в finally, метод еще не закончился. Код не переходит от метода к точке выполнения и обратно к методу. Он выполняет return, затем блок try, затем метод действительно завершается и выполняется system out. - person Florian Schaetz; 05.08.2015
comment
Итак, подводя итог, когда выполняется оператор return, это не означает, что метод «завершен». Возвращаемое значение где-то кэшируется и может быть перезаписано (другим возвратом в блоке finally) или изменено. По сути, наконец, выполняется после попытки независимо от того, куда метод «возвращается»? - person aiguy; 05.08.2015
comment
В принципе правильно, да. A finally выполняется после возврата, может вернуть что-то еще (но вы не должны) или изменить возвращаемое значение (если оно не является неизменным — и опять же, скорее всего, вы не должны), но все еще выполняется ДО finally (каламбур не предназначен ) выход из метода. Следующий код вне метода выполняется ПОСЛЕ final. - person Florian Schaetz; 05.08.2015