有时我们会想:“如果异常发生了,所有的东西都能被正确清理吗?”
当涉及到构造器时,又会怎样呢?
总结来说:在构造器内,如果抛出了异常,那么此时这些清理行为也许不能正常工作。
我们还需要考虑的是:如果构造器在其执行过程中半途而废了,也许该对象的某些部分还没有被成功创建,而这些部分在finally子句中确是要被清理的。在这,我们还能用finally来解决这个问题吗?
答案是不能的,我们对于那些在构造阶段可能会抛出异常,并且要求清理的类,最安全的方式就是使用嵌套的try子句。
这种通用的清理法既使在构造器不抛出异常时,我们也应该应用,规则:在创建需要清理的对象之后,立即进入一个try-finally语句块:
class NeedsCleanup{
//construction can't fail
private static long counter = 1;
private final long id = counter ++;
public void dispose() {
System.out.println("NeedCleanup" + id + "disposed");
}
}
class ConstructionException extends Exception{}
class NeedsCleanup2 extends NeedsCleanup{
//Construction can fail
public NeedsCleanup2() throws ConstructionException{
}
}
public class CleanupIdiom {
public static void main(String[] args) {
//Section 1:
NeedsCleanup nc1 = new NeedsCleanup();
try {
//...
}finally {
nc1.dispose();
}
//Section 2:
//If construction cannot fail you can group objects
NeedsCleanup nc2 = new NeedsCleanup();
NeedsCleanup nc3 = new NeedsCleanup();
try {
//...
}finally {
nc3.dispose(); //Reverse order of construction
nc2.dispose();
}
//Section 3:
//If construction can fail you must guard each one
try {
NeedsCleanup2 nc4 = new NeedsCleanup2();
try {
NeedsCleanup2 nc5 = new NeedsCleanup2();
try {
//...
}finally {
nc5.dispose();
}
}catch(ConstructionException e) {
System.out.println(e);
}finally {
nc4.dispose();
}
}catch(ConstructionException e) {
System.out.println(e);
}
}
}class NeedsCleanup{
//construction can't fail
private static long counter = 1;
private final long id = counter ++;
public void dispose() {
System.out.println("NeedCleanup" + id + "disposed");
}
}
class ConstructionException extends Exception{}
class NeedsCleanup2 extends NeedsCleanup{
//Construction can fail
public NeedsCleanup2() throws ConstructionException{
}
}
public class CleanupIdiom {
public static void main(String[] args) {
//Section 1:
NeedsCleanup nc1 = new NeedsCleanup();
try {
//...
}finally {
nc1.dispose();
}
//Section 2:
//If construction cannot fail you can group objects
NeedsCleanup nc2 = new NeedsCleanup();
NeedsCleanup nc3 = new NeedsCleanup();
try {
//...
}finally {
nc3.dispose(); //Reverse order of construction
nc2.dispose();
}
//Section 3:
//If construction can fail you must guard each one
try {
NeedsCleanup2 nc4 = new NeedsCleanup2();
try {
NeedsCleanup2 nc5 = new NeedsCleanup2();
try {
//...
}finally {
nc5.dispose();
}
}catch(ConstructionException e) {
System.out.println(e);
}finally {
nc4.dispose();
}
}catch(ConstructionException e) {
System.out.println(e);
}
}
}
代码分析:
在main()中,Section 1是比较简单的,遵循了在可去除对象之后紧跟try-finally的原则。构造失败,我们不需要使用catch。
Section 2是为了构造和清理,可以看到具有不能失败的构造器对象可以群组在一起。
Section 3:是显示了如何处理那些具有可以失败的构造器,且需要清理的对象。这对于每一个构造器来说,都必须包含在其自己的try-finally语句块中,并且每一个对象构造器都必须跟随一个try-finally语句块以保证正确清理。
运行结果: