理解’finally’块

我写了七个测试用例来理解finally块的行为。 finally如何运作的逻辑是什么?

 package core; public class Test { public static void main(String[] args) { new Test().testFinally(); } public void testFinally() { System.out.println("One = " + tryOne()); System.out.println("Two = " + tryTwo()); System.out.println("Three = " + tryThree()); System.out.println("Four = " + tryFour()); System.out.println("Five = " + tryFive()); System.out.println("Six = " + trySix()); System.out.println("Seven = " + trySeven()); } protected StringBuilder tryOne() { StringBuilder builder = new StringBuilder(); try { builder.append("Cool"); return builder.append("Return"); } finally { builder = null; } } protected String tryTwo() { String builder = "Cool"; try { return builder += "Return"; } finally { builder = null; } } protected int tryThree() { int builder = 99; try { return builder += 1; } finally { builder = 0; } } protected StringBuilder tryFour() { StringBuilder builder = new StringBuilder(); try { builder.append("Cool"); return builder.append("Return"); } finally { builder.append("+1"); } } protected int tryFive() { int count = 0; try { count = 99; } finally { count++; } return count; } protected int trySix() { int count = 0; try { count = 99; } finally { count = 1; } return count; } protected int trySeven() { int count = 0; try { count = 99; return count; } finally { count++; } } } 

为什么builder = null不起作用?

为什么builder.append("+1")有效,而count++ (在trySeven ()中)不起作用?

一旦你做了返回,覆盖它的唯一方法是做另一个返回(如在Java中的finally块中返回 ,这几乎总是一个坏主意),或者突然完成。 你的测试永远不会从最终返回。

JLS§14.1定义了突然完成。 突然完成类型之一是返回。 由于返回,1,2,3,4和7中的try块突然完成。 正如§14.20.2所解释的那样 ,如果try块除了throw之外突然因R而完成,则立即执行finally块。

如果finally块正常完成(这意味着没有返回,除其他外),“try语句因为原因R而突然完成”。 换句话说,尝试启动的返回保持不变; 这适用于所有测试。 如果你从finally返回,“try语句因为原因S而突然完成(原因R被丢弃)。” (这里是新的重要回报)。

所以在tryOne中,如果你这样做:

 finally { builder = null; return builder; } 

这个新的返回S将覆盖原始返回R.

对于tryFour builder.append("+1") ,请记住StringBuilder是可变的,因此您仍然会返回对try中指定的同一对象的引用。 你只是在做最后一分钟的突变。

tryFivetrySix都是直截了当的。 由于try中没有返回,try和finally都正常完成,并且执行方式就像没有try-finally一样。

让我们从更常见的用例开始 – 您必须关闭一个资源以避免泄漏。

 public void deleteRows(Connection conn) throws SQLException { Statement statement = conn.createStatement(); try { statement.execute("DELETE * FROM foo"); } finally { statement.close(); } } 

在这种情况下,我们必须在完成后关闭语句,因此我们不会泄漏数据库资源。 这将确保在抛出exception的情况下,我们将在函数退出之前始终关闭Statement。

try {…} finally {…}块用于确保在方法终止时始终执行某些操作。 它对Exception案例最有用。 如果你发现自己做了这样的事情:

 public String thisShouldBeRefactored(List foo) { try { if(foo == null) { return null; } else if(foo.length == 1) { return foo.get(0); } else { return foo.get(1); } } finally { System.out.println("Exiting function!"); } } 

你最后没有正确使用。 对此有性能损失。 如果您有必须清理的exception情况,请坚持使用它。 尝试重构以上内容:

 public String thisShouldBeRefactored(List foo) { final String result; if(foo == null) { result = null; } else if(foo.length == 1) { result = foo.get(0); } else { result = foo.get(1); } System.out.println("Exiting function!"); return result; } 

离开try块时会执行finally块。 “return”语句执行两个操作,一个设置函数的返回值,另一个退出函数。 通常这看起来像一个primefaces操作,但在try块中,它将导致finally块在设置返回值之后和函数退出之前执行。

返回执行:

  1. 分配返回值
  2. 最后跑块
  3. 退出function

示例一(原始):

 int count = 1;//Assign local primitive count to 1 try{ return count; //Assign primitive return value to count (1) }finally{ count++ //Updates count but not return value } 

例二(参考):

 StringBuilder sb = new StringBuilder();//Assign sb a new StringBuilder try{ return sb;//return a reference to StringBuilder }finally{ sb.append("hello");//modifies the returned StringBuilder } 

例三(参考):

  StringBuilder sb = new StringBuilder();//Assign sb a new StringBuilder try{ return sb;//return a reference to StringBuilder }finally{ sb = null;//Update local reference sb not return value } 

例四(返回):

  int count = 1; //assign count try{ return count; //return current value of count (1) }finally{ count++; //update count to two but not return value return count; //return current value of count (2) //replaces old return value and exits the finally block } 

builder = nullbuilder.append("+1") 正在运行。 只是他们并没有影响你的回归。 该函数返回return语句具有的内容,而不管之后发生的情况。

之所以存在差异是因为builder是通过引用传递的。 builder=null更改builder=null本地副本。 builder.append("+1")会影响父级所拥有的副本。

为什么builder = null不起作用?
因为您将本地引用设置为null,这不会更改内存的内容。 所以它正在工作,如果你尝试在finally块之后访问构建器,那么你将得到null。
为什么builder.append("+1") work?
因为您正在使用引用修改内存的内容,所以它应该起作用。
为什么count++在testFive()中不起作用?
它对我很好。 它按预期输出100。

考虑编译器实际为return语句做了什么,例如在tryOne()中:它将对builder的引用复制回调用函数的环境。 在完成此操作之后,但在控制返回到调用函数之前,finally块执行。 所以你在实践中有更多这样的东西:

 protected StringBuilder tryOne() { StringBuilder builder = new StringBuilder(); try { builder.append("Cool"); builder.append("Return"); StringBuilder temp = builder; return temp; } finally { builder = null; } } 

或者,就语句实际执行的顺序而言(当然忽略可能的exception),它看起来更像是:

 protected StringBuilder tryOne() { StringBuilder builder = new StringBuilder(); builder.append("Cool"); builder.append("Return"); StringBuilder temp = builder; builder = null; return temp; } 

因此设置builder = null会运行,它只是没有做任何有用的事情。 但是,运行builder.append("something") 具有可见效果,因为temp和builder都引用相同(可变)对象。

同样,trySeven()中真正发生的事情更像是这样:

 protected int trySeven() { int count = 0; count = 99; int temp = count; count++; return temp; } 

在这种情况下,由于我们处理的是int,因此副本是独立的,因此递增副本不会影响另一个。

所有这一切,事实仍然是将return语句置于try-finally块中显然令人困惑,所以如果你在这个问题上有任何选择,你最好重写一些东西以便所有你的返回语句在任何try-finally块之外。

StringBuilder不能为null,它需要一个字符串值。

使用字符串时,空参数通常很糟糕。 count ++未声明??? builder.append(“”)你要追加一个字符串 – 好。 计数=计数++;