By Andres Farias


2012-11-05 20:36:54 8 Comments

Try-catch is meant to help in the exception handling. This means somehow that it will help our system to be more robust: try to recover from an unexpected event.

We suspect something might happen when executing and instruction (sending a message), so it gets enclosed in the try. If that something nearly unexpected happens, we can do something: we write the catch. I don't think we called to just log the exception. I thing the catch block is meant to give us the opportunity of recovering from the error.

Now, let's say we recover from the error because we could fix what was wrong. It could be super nice to do a re-try:

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

This would quickly fall in the eternal loop, but let's say that the fix_the_problem returns true, then we retry. Given that there is no such thing in Java, how would YOU solve this problem? What would be your best design code for solving this?

This is like a philosophical question, given that I already know what I'm asking for is not directly supported by Java.

21 comments

@GirishB 2019-05-23 11:50:09

This is an old question but a solution is still relevant. Here is my generic solution in Java 8 without using any third party library:

public interface RetryConsumer<T> {
    T evaluate() throws Throwable;
}
public interface RetryPredicate<T> {
    boolean shouldRetry(T t);
}
public class RetryOperation<T> {
    private RetryConsumer<T> retryConsumer;
    private int noOfRetry;
    private int delayInterval;
    private TimeUnit timeUnit;
    private RetryPredicate<T> retryPredicate;
    private List<Class<? extends Throwable>> exceptionList;

    public static class OperationBuilder<T> {
        private RetryConsumer<T> iRetryConsumer;
        private int iNoOfRetry;
        private int iDelayInterval;
        private TimeUnit iTimeUnit;
        private RetryPredicate<T> iRetryPredicate;
        private Class<? extends Throwable>[] exceptionClasses;

        private OperationBuilder() {
        }

        public OperationBuilder<T> retryConsumer(final RetryConsumer<T> retryConsumer) {
            this.iRetryConsumer = retryConsumer;
            return this;
        }

        public OperationBuilder<T> noOfRetry(final int noOfRetry) {
            this.iNoOfRetry = noOfRetry;
            return this;
        }

        public OperationBuilder<T> delayInterval(final int delayInterval, final TimeUnit timeUnit) {
            this.iDelayInterval = delayInterval;
            this.iTimeUnit = timeUnit;
            return this;
        }

        public OperationBuilder<T> retryPredicate(final RetryPredicate<T> retryPredicate) {
            this.iRetryPredicate = retryPredicate;
            return this;
        }

        @SafeVarargs
        public final OperationBuilder<T> retryOn(final Class<? extends Throwable>... exceptionClasses) {
            this.exceptionClasses = exceptionClasses;
            return this;
        }

        public RetryOperation<T> build() {
            if (Objects.isNull(iRetryConsumer)) {
                throw new RuntimeException("'#retryConsumer:RetryConsumer<T>' not set");
            }

            List<Class<? extends Throwable>> exceptionList = new ArrayList<>();
            if (Objects.nonNull(exceptionClasses) && exceptionClasses.length > 0) {
                exceptionList = Arrays.asList(exceptionClasses);
            }
            iNoOfRetry = iNoOfRetry == 0 ? 1 : 0;
            iTimeUnit = Objects.isNull(iTimeUnit) ? TimeUnit.MILLISECONDS : iTimeUnit;
            return new RetryOperation<>(iRetryConsumer, iNoOfRetry, iDelayInterval, iTimeUnit, iRetryPredicate, exceptionList);
        }
    }

    public static <T> OperationBuilder<T> newBuilder() {
        return new OperationBuilder<>();
    }

    private RetryOperation(RetryConsumer<T> retryConsumer, int noOfRetry, int delayInterval, TimeUnit timeUnit,
                           RetryPredicate<T> retryPredicate, List<Class<? extends Throwable>> exceptionList) {
        this.retryConsumer = retryConsumer;
        this.noOfRetry = noOfRetry;
        this.delayInterval = delayInterval;
        this.timeUnit = timeUnit;
        this.retryPredicate = retryPredicate;
        this.exceptionList = exceptionList;
    }

    public T retry() throws Throwable {
        T result = null;
        int retries = 0;
        while (retries < noOfRetry) {
            try {
                result = retryConsumer.evaluate();
                if (Objects.nonNull(retryPredicate)) {
                    boolean shouldItRetry = retryPredicate.shouldRetry(result);
                    if (shouldItRetry) {
                        retries = increaseRetryCountAndSleep(retries);
                    } else {
                        return result;
                    }
                } else {
                    // no retry condition defined, no exception thrown. This is the desired result.
                    return result;
                }
            } catch (Throwable e) {
                retries = handleException(retries, e);
            }
        }
        return result;
    }

    private int handleException(int retries, Throwable e) throws Throwable {
        if (exceptionList.contains(e.getClass()) || (exceptionList.isEmpty())) {
            // exception is excepted, continue retry.
            retries = increaseRetryCountAndSleep(retries);
            if (retries == noOfRetry) {
                // evaluation is throwing exception, no more retry left. Throw it.
                throw e;
            }
        } else {
            // unexpected exception, no retry required. Throw it.
            throw e;
        }
        return retries;
    }

    private int increaseRetryCountAndSleep(int retries) {
        retries++;
        if (retries < noOfRetry && delayInterval > 0) {
            try {
                timeUnit.sleep(delayInterval);
            } catch (InterruptedException ignore) {
                Thread.currentThread().interrupt();
            }
        }
        return retries;
    }
}

Let's have a test case like:

@Test
public void withPredicateAndException() {
    AtomicInteger integer = new AtomicInteger();
    try {
        Integer result = RetryOperation.<Integer>newBuilder()
                .retryConsumer(() -> {
                    int i = integer.incrementAndGet();
                    if (i % 2 == 1) {
                        throw new NumberFormatException("Very odd exception");
                    } else {
                        return i;
                    }
                })
                .noOfRetry(10)
                .delayInterval(10, TimeUnit.MILLISECONDS)
                .retryPredicate(value -> value <= 6)
                .retryOn(NumberFormatException.class, EOFException.class)
                .build()
                .retry();
        Assert.assertEquals(8, result.intValue());
    } catch (Throwable throwable) {
        Assert.fail();
    }
}

@Vivek Sethi 2018-10-31 11:29:33

Spring AOP and annotation based solution:

Usage (@RetryOperation is our custom annotation for the job):

@RetryOperation(retryCount = 1, waitSeconds = 10)
boolean someMethod() throws Exception {
}

We'll need two things to accomplish this: 1. an annotation interface, and 2. a spring aspect. Here's one way to implement these:

The Annotation Interface:

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryOperation {
    int retryCount();
    int waitSeconds();
}

The Spring Aspect:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;

@Aspect @Component 
public class RetryAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(RetryAspect.class);

    @Around(value = "@annotation(RetryOperation)")
    public Object retryOperation(ProceedingJoinPoint joinPoint) throws Throwable {

        Object response = null;
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        RetryOperation annotation = method.getAnnotation(RetryOperation.class);
        int retryCount = annotation.retryCount();
        int waitSeconds = annotation.waitSeconds();
        boolean successful = false;

        do {
            try {
                response = joinPoint.proceed();
                successful = true;
            } catch (Exception ex) {
                LOGGER.info("Operation failed, retries remaining: {}", retryCount);
                retryCount--;
                if (retryCount < 0) {
                    throw ex;
                }
                if (waitSeconds > 0) {
                    LOGGER.info("Waiting for {} second(s) before next retry", waitSeconds);
                    Thread.sleep(waitSeconds * 1000l);
                }
            }
        } while (!successful);

        return response;
    }
}

@João Pimentel Ferreira 2018-10-28 14:34:17

The issue with the remaining solutions is that, the correspondent function tries continuously without a time interval in-between, thus over flooding the stack.

Why not just trying only every second and ad eternum?

Here a solution using setTimeout and a recursive function:

(function(){
  try{
    Run(); //tries for the 1st time, but Run() as function is not yet defined
  }
  catch(e){
    (function retry(){
      setTimeout(function(){
        try{
          console.log("trying...");
          Run();
          console.log("success!");
        }
        catch(e){
          retry(); //calls recursively
        }
      }, 1000); //tries every second
    }());
  }
})();



//after 5 seconds, defines Run as a global function
var Run;
setTimeout(function(){
  Run = function(){};
}, 5000);

Replace the function Run() by the function or code that you'd like to retry every second.

@bnsd55 2018-09-08 13:16:00

You can use https://github.com/bnsd55/RetryCatch

Example:

RetryCatch retryCatchSyncRunnable = new RetryCatch();
        retryCatchSyncRunnable
                // For infinite retry times, just remove this row
                .retryCount(3)
                // For retrying on all exceptions, just remove this row
                .retryOn(ArithmeticException.class, IndexOutOfBoundsException.class)
                .onSuccess(() -> System.out.println("Success, There is no result because this is a runnable."))
                .onRetry((retryCount, e) -> System.out.println("Retry count: " + retryCount + ", Exception message: " + e.getMessage()))
                .onFailure(e -> System.out.println("Failure: Exception message: " + e.getMessage()))
                .run(new ExampleRunnable());

Instead of new ExampleRunnable() you can pass your own anonymous function.

@Jonas_Hess 2018-02-20 08:39:16

Here a reusable and more generic approach for Java 8+ that does not require external libraries:

public interface IUnreliable<T extends Exception>
{
    void tryRun ( ) throws T;
}

public static <T extends Exception> void retry (int retryCount, IUnreliable<T> runnable) throws T {
    for (int retries = 0;; retries++) {
        try {
            runnable.tryRun();
            return;
        } catch (Exception e) {
            if (retries < retryCount) {
                continue;
            } else {
                throw e;
            }
        }
    }
}

Usage:

@Test
public void demo() throws IOException {
    retry(3, () -> {
        new File("/tmp/test.txt").createNewFile();
    });
}

@meriton 2012-11-05 20:39:56

As usual, the best design depends on the particular circumstances. Usually though, I write something like:

for (int retries = 0;; retries++) {
    try {
        return doSomething();
    } catch (SomeException e) {
        if (retries < 6) {
            continue;
        } else {
            throw e;
        }
    }
}

@Didier A. 2014-11-22 01:36:49

Wait, why not have the condition inside the for loop declaration like: for(int retries = 0; retries < 6; retries++) ??

@meriton 2014-11-22 14:03:52

Because I only want to throw in the last attempt, and therefore the catch block needs that condition, making the condition in the for redundant.

@Koray Tugay 2018-09-19 14:26:47

I do not think that continue is needed there.. And you can simply flip the if condition.

@Jonathan 2016-04-12 20:02:45

Your exact scenario handled via Failsafe:

RetryPolicy retryPolicy = new RetryPolicy()
  .retryOn(NearlyUnexpectedException.class);

Failsafe.with(retryPolicy)
  .onRetry((r, f) -> fix_the_problem())
  .run(() -> some_instruction());

Pretty simple.

@Maksim 2017-05-19 18:21:27

very nice library.

@Shreyas 2018-06-12 04:34:58

for those wondering, you will need this in your gradle dependencies - compile 'net.jodah:failsafe:1.1.0'

@tushar Mandar 2016-06-02 12:11:18

https://github.com/tusharmndr/retry-function-wrapper/tree/master/src/main/java/io

int MAX_RETRY = 3; 
RetryUtil.<Boolean>retry(MAX_RETRY,() -> {
    //Function to retry
    return true;
});

@rogerdpack 2015-04-23 21:10:31

In case it's useful, a couple more options to consider, all thrown together (stopfile instead of retries, sleep, continue larger loop) all possibly helpful.

 bigLoop:
 while(!stopFileExists()) {
    try {
      // do work
      break;
    }
    catch (ExpectedExceptionType e) {

       // could sleep in here, too.

       // another option would be to "restart" some bigger loop, like
       continue bigLoop;
    }
    // ... more work
}

@rogerdpack 2016-05-12 20:28:23

Down voters please leave comments as to why, thanks!

@xploreraj 2017-07-10 16:12:20

This is a sheer ignorance to downvote and not cite a reason.

@João Pimentel Ferreira 2018-10-28 14:42:52

sleeping there is not obvious since the while loop wouldn't wait

@yegor256 2013-03-24 07:14:22

You can use AOP and Java annotations from jcabi-aspects (I'm a developer):

@RetryOnFailure(attempts = 3, delay = 5)
public String load(URL url) {
  return url.openConnection().getContent();
}

You could also use @Loggable and @LogException annotations.

@Alind Billore 2015-08-07 21:58:45

Wow ! Sounds Fancy ! :)

@Mohamed Taher Alrefaie 2016-03-04 12:32:46

Should be top answer.

@warch 2017-06-09 07:33:44

is there a way to "fix" the error when attempt fails (do some adoptions that may fix the next attempt)? see question: fix_the_problem(); in the catch block

@Michael Lihs 2018-10-28 20:21:04

Given the amount of open issues and the time passed for acknowledged bugs not being fixed, I would not rely on this library.

@Josh 2015-09-04 01:24:45

Im not sure if this is the "Professional" way to do it and i'm not entirely sure if it works for everything.

boolean gotError = false;

do {
    try {
        // Code You're Trying
    } catch ( FileNotFoundException ex ) {
        // Exception
        gotError = true;
    }
} while ( gotError = true );

@David Kayo 2015-01-19 01:56:27

following is my solution with very simple approach!

               while (true) {
                    try {
                        /// Statement what may cause an error;
                        break;
                    } catch (Exception e) {

                    }
                }

@Chandra Shekhar 2016-07-27 14:42:58

please look at @Rohit Jain answer which is more specific and not an infinite loop in negative cases.

@Rohit Jain 2012-11-05 20:39:22

You need to enclose your try-catch inside a while loop like this: -

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;
    }
}

I have taken count and maxTries to avoid running into an infinite loop, in case the exception keeps on occurring in your try block.

@Andres Farias 2012-11-06 10:53:58

I thought in something like this at first, without the maxTries. Thanks for the answer!

@Rohit Jain 2012-11-06 10:55:15

@AndresFarias.. Yeah, the most important point in this answer is to include a maxTries. Else it will run into an infinite loop if user continously gives wrong input, and hence will not exit. You're welcome though. :)

@David Holiday 2014-06-07 00:13:37

thank you for this - it just saved me from having to write some very gnarly code!

@Suat Atan PhD 2015-11-28 11:40:54

Is it possible adding Thread.sleep() function inside the catch at here. Because in some cases like waiting for page response in Selenium library that became critical. Thanks.

@Chandra Shekhar 2016-07-27 14:45:28

Same exception occurs if the exception is not handled even 3rd time

@Balaji Boggaram Ramanarayan 2016-08-24 22:00:58

@SuatAtan - You can add the Thread.sleep(#time) in catch.

@Jarkid 2017-03-27 07:51:06

My implementation is: try { return true; } catch { return false; } and enclose a while loop: while ( !method() ) { method(); }

@Fabián 2017-05-21 04:11:58

I used the while(true) with the scanner.nextInt(); and break; inside, but it kept looping without asking again.

@Krzysztof Walczewski 2017-12-06 10:24:53

Works great! For beginners: If you get positive infinite loop, check if You added "break;" at the end in "try" block.

@Philippe 2018-09-24 22:36:43

Isn't the semantic of maxTries really maxRetries?

@KnightHawk 2014-08-14 15:19:40

I know there are already many similar answers here, and mine is not much different, but I will post it anyway because it deals with a specific case/issue.

When dealing with the facebook Graph API in PHP you sometimes get an error, but immediately re-trying the same thing will give a positive result (for various magical Internet reasons that are beyond the scope of this question). In this case there is no need to fix any error, but to simply try again because there was some kind of "facebook error".

This code is used immediately after creating a facebook session:

//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(); }
    }
}

Also, by having the for loop count down to zero ($attempt--) it makes it pretty easy to change the number of attempts in the future.

@Rahul Malhotra 2014-07-28 07:46:04

Use a do-while to design re-try block.

boolean successful = false;
int maxTries = 3;
do{
  try {
    something();
    success = true;
  } catch(Me ifUCan) {
    maxTries--;
  }
} while (!successful || maxTries > 0)

@lilalinux 2014-09-01 15:06:31

The code should throw the original exception if unsuccessfull

@Stephen P 2012-11-05 20:49:58

Most of these answers are essentially the same. Mine is also, but this is the form I like

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);
}

@Joachim Sauer 2013-09-03 13:55:21

One drawback is that you also call fix_the_problem after the last attempt. That could be a costly operation and could waste some time.

@Stephen P 2013-09-04 18:49:36

@JoachimSauer True. You could if (tryCount < max) fix() -- but this is the format of a general approach; the details would depend on a specific case. There's also a guava based Retryer I've been looking at.

@ach 2012-11-05 21:21:10

Obligatory "enterprisy" solution:

public abstract class Operation {
    abstract public void doIt();
    public void handleException(Exception cause) {
        //default impl: do nothing, log the exception, etc.
    }
}

public class OperationHelper {
    public static void doWithRetry(int maxAttempts, Operation operation) {
        for (int count = 0; count < maxAttempts; count++) {
            try {
                operation.doIt();
                count = maxAttempts; //don't retry
            } catch (Exception e) {
                operation.handleException(e);
            }
        }
    }
}

And to call:

OperationHelper.doWithRetry(5, new Operation() {
    @Override public void doIt() {
        //do some stuff
    }
    @Override public void handleException(Exception cause) {
        //recover from the Exception
    }
});

@cvacca 2014-03-28 21:01:00

You should re-throw the exception if the last re-try fails, as done in the other answers given.

@AlexR 2012-11-05 20:44:15

Although try/catch into while is well-known and good strategy I want to suggest you recursive call:

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);
    }
}

@Dan 2012-11-05 20:54:15

How is recursion better than a loop for this use case?

@Clockwork-Muse 2012-11-05 23:45:40

The stack trace may look a little odd on this one, because wouldn't it have limit count of the method being recursed? As opposed to the loop version, which will throw at the 'original' level...

@Thomas 2012-11-06 01:33:52

Sure looks elegant on paper but I'm not sure recursion is the right approach somehow.

@Andres Farias 2012-11-06 10:51:31

I like recursion very much, although it has a problem too: if there are more instructions already performed this would perform them again. Thanks for the answer!

@sinuhepop 2013-07-21 02:12:47

I don't understand why recursion here too. Anyway, I think it could be simplified to: void retry(int times) { (...) if (times==0) throw w; retry(times--);

@Bolaka 2015-01-16 08:17:07

If an exception occurs within retry (when invoked from the catch recursively) then it will not be handled by catch. So there should be nested try-catch in each catch handler, which is not elegant!

@AlexR 2015-01-16 12:57:48

@bolaka, it will work. The excepion will be caught into the next rery invoked from catch. This is the point of recursion.

@user207421 2015-09-04 03:00:05

It is poor practice to use recursion as a substitute for mere iteration. Recursion is for use when you want to push and pop some data.

@AlexR 2015-09-06 06:46:52

@EJP, you are absolutely right that using loop is better in general case. However if number of retries is limited by relatively small number using recursion IMHO makes code clearer.

@Michael Blackburn 2016-01-07 21:25:02

It makes the code "clearer" at the cost of making a stack trace less clear.

@AlexR 2016-01-08 12:19:48

@MichaelBlackburn, I respect your opinion :)

@AFP_555 2017-11-20 11:58:46

No, please don't use recursion. Terrible practice if the problem is not recursive.

@Crashh 2017-12-26 23:12:45

Classic tail-end recursion that should be replaced with its iterative form

@Sam I am 2012-11-05 20:40:16

All a Try-Catch does is allow your program to fail gracefully. In a catch statement, you generally try to log the error, and maybe roll back changes if you need to.

bool finished = false;

while(finished == false)
{
    try
    {
        //your code here
        finished = true
    }
    catch(exception ex)
    {
        log.error("there was an error, ex");
    }
}

@Rohit Jain 2012-11-05 20:46:28

while (finished == false)??? Any specific reason for THAT comparison?

@Sam I am 2012-11-05 20:47:34

do you mean as opposed to (!finished)?

@Rohit Jain 2012-11-05 20:49:11

Of course, Yes!!

@Sam I am 2012-11-05 20:51:51

@RohitJain it looks too much like while(finished). I prefer to use the more verbose version.

@Rohit Jain 2012-11-05 20:53:14

How on earth does while(!finished) look like while (finished)??

@Michael Blackburn 2016-01-07 21:29:05

@Rohit Because it's only one character different. They all get compiled down to the same thing. In C#, I use a String extension method IsPopulated() which just returns !IsNullOrEmpty() to ensure that my intent is understood by all developers.

@Jordan Kaye 2012-11-05 20:40:12

A simple way to solve the issue would be to wrap the try/catch in a while loop and maintain a count. This way you could prevent an infinite loop by checking a count against some other variable while maintaining a log of your failures. It isn't the most exquisite solution, but it would work.

@Yogendra Singh 2012-11-05 20:39:39

Use a while loop with local status flag. Initialize the flag as false and set it to true when operation is successful e.g. below:

  boolean success  = false;
  while(!success){
     try{ 
         some_instruction(); 
         success = true;
     } catch (NearlyUnexpectedException e){
       fix_the_problem();
     }
  }

This will keep retrying until its successful.

If you want to retry only certain number of times then use a counter as well:

  boolean success  = false;
  int count = 0, MAX_TRIES = 10;
  while(!success && count++ < MAX_TRIES){
     try{ 
         some_instruction(); 
         success = true;
     } catch (NearlyUnexpectedException e){
       fix_the_problem();
     }
  }
  if(!success){
    //It wasn't successful after 10 retries
  }

This will try max 10 times if not successful until then an will exit if its successful before hand.

@Rohit Jain 2012-11-05 20:45:50

Rather than checking for !success in your while, you can just break out of while when success is true.

@Yogendra Singh 2012-11-05 20:47:38

@RohitJain: It looks more clean to me.

@Rohit Jain 2012-11-05 20:48:51

@YogendraSingh.. Strange. as you are not modifying your success anywhere in your catch. So it seems redundant to check for it, on every run of catch.

@Yogendra Singh 2012-11-05 20:59:54

@RohitJain: Catch is just correcting the data. It will go back and run the statement again. If successful, it will modify the success. Try it out.

Related Questions

Sponsored Content

60 Answered Questions

[SOLVED] How do I read / convert an InputStream into a String in Java?

28 Answered Questions

[SOLVED] Catch multiple exceptions at once?

26 Answered Questions

[SOLVED] How can I safely create a nested directory?

42 Answered Questions

[SOLVED] "implements Runnable" vs "extends Thread" in Java

33 Answered Questions

[SOLVED] How do you assert that a certain exception is thrown in JUnit 4 tests?

16 Answered Questions

[SOLVED] Why should I not wrap every block in "try"-"catch"?

9 Answered Questions

[SOLVED] Can I catch multiple Java exceptions in the same catch clause?

5 Answered Questions

[SOLVED] Catch multiple exceptions in one line (except block)

32 Answered Questions

[SOLVED] The case against checked exceptions

5 Answered Questions

[SOLVED] Try-catch speeding up my code?

Sponsored Content