JAVA中why无返回值

阅读: 评论:0

JAVA中why无返回值

JAVA中why无返回值

我有一个简单的Java类,如下所示:

public class Test {

private String s;

public String foo() {

try {

s ="dev";

return s;

}

finally {

s ="override variable s";

System.out.println("Entry in finally Block");

}

}

public static void main(String[] xyz) {

Test obj = new Test();

System.out.println(obj.foo());

}

}

这段代码的输出是这样的:

Entry in finally Block

dev

为什么s不会在finally块中重写,而是控制打印输出?

obj.foo()返回s的值;

你应该把return语句放在finally块上,和我一起重复,最后总是执行块

在try块中它返回s

陈述的顺序很重要。在更改值之前,您将返回s。

@Juvanis它正常运行。

但是,您可以在finally块中返回新值,这与C#(您不能)不同

@AlvinWong是的,我们终于可以回来了,它给出了一些警告....最后阻止不能正常完成

也许这只是你错过了,但养成了从大写字母开始一直编写类名的习惯。

相关:最终返回"发生后"

除了java能够以这种方式工作之外,我认为使用不容易理解的机制(在许多语言中工作方式不同)并不是一个好的编码实践。许多人(像我一样)将假设返回离开方法而不执行finally块。那么为什么不避免这种设计并避免任何误解呢?只是把回报放在你最后一块之下。

@ToBe阅读此java acle/javase/tutorial/essential/exceptions/

@dev你是对的,我现在可以看到它的好处。我不确定在大多数情况下我喜欢它。但是tnx的信息!

catch中的return语句的行为可能重复,最后

@PeterMortensen问题正如你所指出的那样,这个问题中只有一部分最终会返回,但是另外一件事就是为什么"s"不会被finally块中的编译器覆盖。

try块在执行return语句时完成,return语句执行时的值s是方法返回的值。 finally子句稍后更改s的值(在return语句完成之后)不会(在此时)更改返回值。

请注意,上述内容涉及finally块中s本身值的更改,而不是s引用的对象的更改。如果s是对可变对象的引用(String不是)并且在finally块中更改了对象的内容,那么将在返回的值中看到这些更改。

有关所有这些操作的详细规则可以在Java语言规范的第14.20.2节中找到。请注意,return语句的执行计为try块的突然终止(开始的部分"如果执行块因任何其他原因而突然完成R ...."适用)。有关return语句突然终止块的原因,请参见JLS的第14.17节。

通过进一步的细节:如果try块和finally都阻塞

由于return语句,try-finally语句突然终止,因此§14.20.2中的以下规则适用:

If execution of the try block completes abruptly for any other reason R [besides throwing an exception], then the finally block is executed, and then there is a choice:

If the finally block completes normally, then the try statement completes abruptly for reason R.

If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded).

结果是finally块中的return语句确定整个try-finally语句的返回值,并且丢弃try块中返回的值。如果try块抛出异常,则会在try-catch-finally语句中发生类似的事情,它会被catch块捕获,并且catch块和finally块都有return语句。

如果我使用StringBuilder类而不是String而不是在finally块中附加一些值,它会改变返回值。为什么?

@dev - 我在答案的第二段讨论这个问题。在您描述的情况下,finally块不会更改返回的对象(StringBuilder),但它可以更改对象的内部。 finally块在方法实际返回之前执行(即使return语句已完成),因此这些更改在调用代码看到返回值之前发生。

尝试使用List,您可以获得与StringBuilder相同的行为。

@yogeshprajapati - 是的。对于任何可变的返回值(StringBuilder,List,Set,ad nauseum)也是如此:如果更改finally块中的内容,那么当方法时,在调用代码中会看到这些更改终于退出了。

这说明会发生什么,它没有说明原因(如果它指向JLS中涵盖的内容,那将特别有用)。

因为返回值在调用finally之前放在堆栈上。

这是真的,但它没有解决OP关于为什么返回的字符串没有改变的问题。这与字符串不变性和引用与对象有关,而不是在栈上推送返回值。

两个问题都是相关的。

@templatetypedef即使String是可变的,使用=也不会改变它。

@Owen - 我认为你将变量与他们引用的内存对象混淆。 =不会改变字符串,它会为不同的内存对象分配引用。可变性是值的属性(垃圾收集的内存对象),而不是引用它们的变量。

@Saul - Owen的观点(这是正确的)是不变性与OP的finally块不影响返回值的原因无关。我认为templatetypedef可能会得到什么(虽然这不清楚)是因为返回的值是对不可变对象的引用,甚至更改finally块中的代码(除了使用另一个return语句)不能影响从方法返回的值。

@TedHopp - 呃..显然我误读了Owen的评论。出于一些奇怪的原因,我记得看到他反问,如果String是可变的那么为什么=会改变它。因此评论。

如果我们查看字节码内部,我们会注意到JDK已经进行了重大优化,而foo()方法看起来像:

String tmp = null;

try {

s ="dev"

tmp = s;

s ="override variable s";

return tmp;

} catch (RuntimeException e){

s ="override variable s";

throw e;

}

和字节码:

0:  ldc #7;         //loading String"dev"

2:  putstatic   #8; //storing it to a static variable

5:  getstatic   #8; //loading"dev" from a static variable

8:  astore_0        //storing"dev" to a temp variable

9:  ldc #9;         //loading String"override variable s"

11: putstatic   #8; //setting a static variable

14: aload_0         //loading a temp avariable

15: areturn         //returning it

16: astore_1

17: ldc #9;         //loading String"override variable s"

19: putstatic   #8; //setting a static variable

22: aload_1

23: athrow

java保留"dev"字符串在返回之前被更改。事实上,这根本没有最终阻止。

这里有两件事值得注意:

字符串是不可变的。当您将s设置为"覆盖变量s"时,将s设置为引用内联String,而不是将s对象的固有char缓冲区更改为"覆盖变量s"。

您在堆栈上添加了对s的引用以返回到调用代码。之后(当finally块运行时),更改引用不应该对堆栈上已有的返回值执行任何操作。

所以,如果我采取stringbuffer比sting将被覆盖?

@dev - 如果要更改finally子句中缓冲区的内容,可以在调用代码中看到。但是,如果您为s分配了新的字符串缓冲区,则行为将与现在相同。

是的,如果我追加到字符串缓冲区中,则块更改会反映在输出上。

@ 0xCAFEBABE你也给出了很好的答案和概念,我非常感谢你。

我稍微改变了你的代码以证明特德的观点。

正如您在输出中看到的那样s确实已经改变但是在返回之后。

public class Test {

public String s;

public String foo() {

try {

s ="dev";

return s;

} finally {

s ="override variable s";

System.out.println("Entry in finally Block");

}

}

public static void main(String[] xyz) {

Test obj = new Test();

System.out.println(obj.foo());

System.out.println(obj.s);

}

}

输出:

Entry in finally Block

dev

override variable s

为什么重写的字符串不返回。

正如Ted和Tordek已经说过"在最终执行之前将返回值放在堆栈上"

虽然这是一个很好的附加信息,但我不愿意对它进行投票,因为它没有(单独)回答这个问题。

从技术上讲,如果定义了finally块,则只有当finally块还包含return时,才会忽略try块中的return。

这是一个可疑的设计决策,回想起来可能是一个错误(很像默认情况下引用是可空/可变的,并且根据一些情况,检查异常)。在许多方面,这种行为与对finally意味着什么的口语理解完全一致 -"无论事先在try块中发生什么,总是运行此代码。"因此,如果从finally块返回true,则整体效果必须始终为return s,不是吗?

一般来说,这很少是一个很好的习惯用法,你应该自由地使用finally块来清理/关闭资源,但很少从它们返回一个值。

试试这个:如果你想打印s的覆盖值。

finally {

s ="override variable s";

System.out.println("Entry in finally Block");

return s;

}

它给出警告..最后块没有正常完成

本文发布于:2024-01-31 13:44:52,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170667989328948.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:返回值   JAVA
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23