DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • The First Annual Recap From JPA Buddy
  • How to Use Java to Build Single Sign-on
  • Pros and Cons for Using GraalVM Native-Images
  • A Systematic Approach for Java Software Upgrades

Trending

  • It’s Not About Control — It’s About Collaboration Between Architecture and Security
  • How Large Tech Companies Architect Resilient Systems for Millions of Users
  • AI’s Role in Everyday Development
  • Performing and Managing Incremental Backups Using pg_basebackup in PostgreSQL 17
  1. DZone
  2. Coding
  3. Java
  4. Exceptions in Java: You're (Probably) Doing It Wrong

Exceptions in Java: You're (Probably) Doing It Wrong

You've probably used exceptions in Java, but have you used them properly? Generally, exceptions are for exceptional cases. See whether or not you should use checked exceptions, and when and how to use Java exceptions.

By 
Sam Atkinson user avatar
Sam Atkinson
·
Feb. 04, 16 · Analysis
Likes (41)
Comment
Save
Tweet
Share
53.5K Views

Join the DZone community and get the full member experience.

Join For Free

I recently wrote about some of the things I do differently in my codebases relative to other programmers. The post was very popular and generated a lot of healthy discussion which I’m always a fan of. One of the cornerstones was that I do not use Checked Exceptions. One of the comments asked how I actually use Exceptions in my codebases, so I wanted to cover this in some more detail.

Exceptions should be for Exceptional cases. These are unexpected scenarios, and usually, will not have a nice easy way of recovery. If there is a sensible recovery option, then do not use an exception; using exceptions to control the flow through your program is bad practice. If we take this to be the case, then the best way to handle an Exception is to alert this out; whether through logs or some other service depends on how you work on your application. You then have to make a choice:

  • Either you keep the application up and have someone manually inspect the application to ensure that it is in the correct state and not damaged (usually a bad idea)

  • Assume your application is damaged and you bring down the instance

The latter option is preferred but is obviously contingent on your architecture being designed to cope with this.

In terms of coding, this is easy to do. If you have an exceptional circumstance, throw an application specific Runtime exception- extend RuntimeException with a name related to your application. This means when tracing logs you know the error is related to code you have written.

Why use runtime and not checked? Checked exceptions lead to ugly code as the exception handling must be propagated upwards, leading to all calling methods having “throws SomeException” on the end, which leads to more catch blocks. The old argument is that this “forces” developers to handle exceptions properly. Anyone on a real code base knows that this does not happen, and Exceptions are routinely ignored, or printed out and then ignored. This isn’t some sign of a terrible developer; it is such a common occurrence that it is a sign that checked Exceptions are broken. If you need further evidence, look to C# and pretty much all of the new JVM languages such as Clojure and Scala have done away with checked exceptions completely.

This is all fine for your own code, however, most libraries and frameworks come with Exceptions built in. How do we handle these?

If you have no sensible way to handle/recover from the exception then throw it up, but first wrap it in a RuntimeException of your own creation. If my application is called “Stockfighter,” I will have a “StockfighterException” to wrap any other Exception:

public class StockfighterException extends RuntimeException{
    public StockfighterException(String error, Exception e) {
        super(error, e);
    }
}

I do this for two reasons; firstly, when I then debug the application I can see where the Exception has come from in my code base and add any useful information. This is immensely useful when debugging. Secondly, as mentioned, I dislike checked exceptions. This means I can then throw it up the stack without polluting all of the methods with “throws XException”.

As mentioned, this will normally kill the application which is the desired effect if it is a genuinely exceptional case. If I don’t want to kill it, or I want to log some extra information, I will usually wrap the key functional parts of my application in an ExceptionHandler. This is usually a decorator of the class which wraps the functions in a try catch block. I will also add ExceptionHandlers to any Threads to ensure they aren’t silently swallowed.

This is also a good separation of concerns in OO terms. If you have multiple implementations of something then you will often need to handle and log exceptions in a similar way in each one. Using a decorating handler means this can be shared amongst the implementations.

One of my favourite patterns is when using composites. In a lot of code, something will happen (receiving an event for example) and I will have multiple subscribers that will want to act on that.

     InfoPublisher publisher = new InfoPublisher();

        publisher.subscribe(
            new ErrorHandlingSubscriber(
                new CompositeSubscriber(
                    new StoreInfomationSubscriber(),
                    new DoSomethingWithInfoSubscriber(),
                    new AnotherActorSubscriber()
                )
            )
        )

My InfoPublisher publishes its event to whatever has “subscribed”. I have a CompositeSubscriber which loops through all of the Subscribers to pass the information to it.

    class CompositeSubscriber implements Subscriber{
    private Subscriber[] subscribers;

    public CompositeSubscriber(Subscriber... subscribers) {

        this.subscribers = subscribers;
    }

    @Override
    public void onInfo(String info) {
        for (Subscriber subscriber : subscribers) {
            subscriber.onInfo(info);
        }
    }
}

This is nice, clean and simple. I can then wrap the whole thing in an ExceptionHandler to deal with anything that goes truly wrong in the codebase.

public class ErrorHandlingSubscriber implements Subscriber{
    private Subscriber subscriber;
    private Logger logger = LoggerFactory.getLogger(ErrorHandlingSubscriber.class);

    public ErrorHandlingSubscriber(Subscriber subscriber) {
        this.subscriber = subscriber;
    }

    @Override
    public void onInfo(String info) {
        try{
            subscriber.onInfo(info);
        }catch (Exception e){
            logger.error("Serious Exception!", e);
            System.exit(1);
        }

    }
}

This way my exception handling is cleanly separated from my functional code.

So, in summary:

  • Never use Exceptions for control flow in your application. Use them only for exceptional circumstances

  • Wrap Checked Exceptions in your own app RuntimeException so you know where the Exception has come from

  • Create a top level ExceptionHandler decorating your code to appropriately log or crash the application.

Follow me on Twitter at @SambaHK for more ramblechats.

application Java (programming language) code style

Opinions expressed by DZone contributors are their own.

Related

  • The First Annual Recap From JPA Buddy
  • How to Use Java to Build Single Sign-on
  • Pros and Cons for Using GraalVM Native-Images
  • A Systematic Approach for Java Software Upgrades

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!