Why Avoid Using Exceptions to Control Your Flow in Java

Java is a universal programming language that has many alternative solutions for your specific tasks. However, there are good approaches that should be followed, and there are also some unsuccessful approaches that we still mostly use.



One of the most common failed approaches is to use exceptions to control the flow of execution. This should be avoided for two reasons:



  1. This reduces the performance and speed of your code.
  2. This makes your code less readable.


Let's start by looking at an example. Here the exception is used to control the flow of execution:



public static int findAge(String name) { try { String ageAsString = findUser(name); return ageAsString.length(); } catch (NameNotFoundException e) { return 0; } } private static String findUser(String name) { if(name==null) { throw new NameNotFoundException(); } return name; }
      
      





If the user provides a non-zero value for the name, then the findAge method will return the length of this name, but if the user name is null, then the findUser method will throw a NameNotFoundException exception and in this case the findAge method will return 0.



How can we convert this code without using exceptions? In general, there are many options, consider one of them:



 public static int findAgeNoEx(String name) { String ageAsString = findUserNoEx(name); return ageAsString.length(); } private static String findUserNoEx(String name) { if(name==null) { return ""; } return name; }
      
      





In order to find out the impact of the exception on performance, I prepared the following code that calls both variants of the methods 10 million times: with and without exception.



 public class ControlFlowWithExceptionOrNot { public static class NameNotFoundException extends RuntimeException { private static final long serialVersionUID = 3L; } private static final int TRIAL = 10000000; public static void main(String[] args) throws InterruptedException { long start = System.currentTimeMillis(); for (int i = 0; i < TRIAL; i++) { findAgeNoEx(null); } System.out.println("Duration :" + (System.currentTimeMillis() - start)); long start2 = System.currentTimeMillis(); for (int i = 0; i < TRIAL; i++) { findAge(null); } System.out.println("Duration :" + (System.currentTimeMillis() - start2)); }; public static int findAge(String name) { try { String ageAsString = findUser(name); return ageAsString.length(); } catch (NameNotFoundException e) { return 0; } } private static String findUser(String name) { if (name == null) { throw new NameNotFoundException(); } return name; } public static int findAgeNoEx(String name) { String ageAsString = findUserNoEx(name); return ageAsString.length(); } private static String findUserNoEx(String name) { if (name == null) { return ""; } return name; } }
      
      





Output:



 Duration :16 Duration :6212
      
      





As you can see, using the exception cost us thousands of milliseconds on my Intel Core i7-3630QM.



If we compare our two findAge methods in terms of readability, then the method without exception is absolutely clear: firstly, we can be absolutely sure that the findUser method returns a string; and secondly, regardless of which string will be returned, we get its length. At the same time, the method with the exception is somewhat confusing: it is not completely clear what exactly the findUser method returns. It can return a string, or it can throw an exception and this is not visible from the method signature. For this reason, the functional programming paradigm does not welcome the use of exceptions.



In the end, it would be better if you use exceptions where the exception actually occurs and is necessary. If you use exceptions to control the flow of execution, this will cause a drop in program performance and make your code less readable.



I hope this article was interesting to you, and perhaps useful.



All Articles