Try-catch是用来帮助异常处理的。这意味着它将在某种程度上帮助我们的系统变得更加健壮:尝试从意外事件中恢复。

我们怀疑在执行和指令(发送消息)时可能会发生一些事情,因此它被包含在try中。如果发生了几乎意想不到的事情,我们可以做些什么:我们编写捕获。我不认为我们调用只是为了记录异常。我认为catch块是为了给我们从错误中恢复的机会。

现在,假设我们从错误中恢复,因为我们可以修复错误。如果能再试一次,那就太好了:

try{ some_instruction(); }
catch (NearlyUnexpectedException e){
   fix_the_problem();
   retry;
}

这将很快陷入永恒循环,但让我们假设fix_the_problem返回true,然后重试。假设在Java中没有这样的东西,你将如何解决这个问题?你认为解决这个问题的最佳设计代码是什么?

这就像一个哲学问题,因为我已经知道Java不直接支持我所要求的内容。


当前回答

虽然try/catch into while是众所周知的好策略,我想建议你递归调用:

void retry(int i, int limit) {
    try {

    } catch (SomeException e) {
        // handle exception
        if (i >= limit) {
            throw e;  // variant: wrap the exception, e.g. throw new RuntimeException(e);
        }
        retry(i++, limit);
    }
}

其他回答

我知道这里已经有很多类似的答案,我的答案也没有太大不同,但我还是会把它贴出来,因为它涉及一个具体的案例/问题。

当你在PHP中处理facebook Graph API时,你有时会得到一个错误,但立即重新尝试同样的事情会得到一个积极的结果(由于各种神奇的互联网原因,超出了这个问题的范围)。在这种情况下,不需要修复任何错误,而是简单地再试一次,因为有某种“facebook错误”。

这段代码在创建facebook会话后立即使用:

//try more than once because sometimes "facebook error"
$attempt = 3;
while($attempt-- > 0)
{
    // To validate the session:
    try 
    {
        $facebook_session->validate();
        $attempt = 0;
    } 
    catch (Facebook\FacebookRequestException $ex)
    {
        // Session not valid, Graph API returned an exception with the reason.
        if($attempt <= 0){ echo $ex->getMessage(); }
    } 
    catch (\Exception $ex) 
    {
        // Graph API returned info, but it may mismatch the current app or have expired.
        if($attempt <= 0){ echo $ex->getMessage(); }
    }
}

此外,通过让for循环计数为零($attempt——),可以很容易地改变将来的尝试次数。

这些答案基本上都是一样的。我的也是,但我喜欢这种形式

boolean completed = false;
Throwable lastException = null;
for (int tryCount=0; tryCount < config.MAX_SOME_OPERATION_RETRIES; tryCount++)
{
    try {
        completed = some_operation();
        break;
    }
    catch (UnlikelyException e) {
        lastException = e;
        fix_the_problem();
    }
}
if (!completed) {
    reportError(lastException);
}

此解决方案允许您配置一个可重用的功能,以便在不使用任何外部库的情况下基于特定异常进行重试

//创建一个适合你需要的函数。

@FunctionalInterface
public interface ThrowableBiFunction<U,T,R> {
    R apply(U u ,T t) throws Exception;
}

//这是解决方案的关键

public interface ExceptionRetryable<T, U, R> {
    
 int getRetries();
   
 List<Class<? extends Exception>> getRetryableExceptions();

    default R execute(ThrowableBiFunction<T, U, R> function, T t, U u) throws Exception {
        int numberOfRetries = getRetries();
        return execute(function, t, u, numberOfRetries);
    }

    default R execute(ThrowableBiFunction<T, U, R> function, T t, U u, int retryCount) throws Exception {
        try {
            log.info(" Attempting to execute ExceptionRetryable#execute ,Number of remaining retries {} ",retryCount);
            return function.apply(t, u);
        } catch (Exception e) {

           log.info(" error occurred in ExceptionRetryable#execute",e);
            if (retryCount == 0)
                throw e;
            for (Class exp : getRetryableExceptions()) {
                if (e.getClass() == exp) {
                   return execute(function, t, u, retryCount - 1);
                }
            }
            throw e;
        }

    }
}

//创建一个异常可重执行的实现

public class TestRetryable implements ExceptionRetryable<String, String, List<String>> {
    @Override
    public int getRetries() {
        return 10;
    }

    @Override
    public List<Class<? extends Exception>> getRetryableExceptions() {
        return Arrays.asList(new Exception1().getClass(), new Exception2().getClass());
        ;
    }
}

//最后创建一个throwablebiffunction,它封装了需要在exception上重试的代码段和ExceptionRetryable实例

 TestRetryable retryable = new TestRetryable();
    ThrowableBiFunction<Integer,Long, String> testRetrablefcn = { i, l ->
           // your code goes here
};
    Integer i = 0;
    Long l = 1l;
      String output = testRetrablefcn.execute(testRetrablefcn,i,l); 

如果不是所有异常都需要重试,那么只有一些例外。如果至少要尝试一次,这里有一个替代的实用方法:

void runWithRetry(Runnable runnable, Class<Exception> exClass, int maxRetries) {
        Exception err = null;
        do {
            maxRetries--;
            try {
                runnable.run();
                err = null;
            } catch (Exception e) {
                if(exClass.isAssignableFrom(e.getClass())){
                    err = e;
                }else {
                    throw e;
                }
            }
        } while (err != null && maxRetries > 0);

        if (err != null) {
            throw err;
        }
    }

用法:

    runWithRetry(() -> {
       // do something
    }, TimeoutException.class, 5)

你需要像这样在while循环中封装try-catch: -

int count = 0;
int maxTries = 3;
while(true) {
    try {
        // Some Code
        // break out of loop, or return, on success
    } catch (SomeException e) {
        // handle exception
        if (++count == maxTries) throw e;
    }
}

我已经采取了count和maxTries,以避免运行到一个无限循环,以防异常继续发生在你的try块。