[{"data":1,"prerenderedAt":130},["ShallowReactive",2],{"qa-\u002Fjava\u002Fexceptions\u002Fcustom-exceptions":3},{"page":4,"siblings":118,"blog":127},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":109,"related":110,"seo":111,"seoDescription":112,"stem":113,"subtopic":6,"topic":114,"topicSlug":115,"updated":116,"__hash__":117},"qa\u002Fjava\u002Fexceptions\u002Fcustom-exceptions.md","Custom Exceptions",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","Java","java",{},true,3,"\u002Fjava\u002Fexceptions\u002Fcustom-exceptions",[23,28,32,36,41,45,49,53,57,61,65,69,73,77,81,85,89,93,97,101,105],{"id":24,"difficulty":25,"q":26,"a":27},"why-custom-exceptions","easy","Why would you create a custom exception instead of using a built-in one?","A custom exception gives an error a **domain-specific name and type** that\ncallers can catch precisely. `throw new RuntimeException(\"insufficient funds\")`\nforces callers to parse a string or catch everything; `throw new\nInsufficientFundsException(...)` lets them write `catch\n(InsufficientFundsException e)` and react to *that* failure alone.\n\n```java\n\u002F\u002F vague — caller can't distinguish this from any other RuntimeException\nthrow new RuntimeException(\"order not found: \" + id);\n\n\u002F\u002F expressive — a dedicated type the caller can target\nthrow new OrderNotFoundException(id);\n```\n\nCustom exceptions also let you **carry structured data** (the offending id, an\nerror code) and document the failure modes of your API in its type signature.\n**Rule of thumb:** create one when callers need to *react differently* to this\nerror or need data from it; otherwise reuse a built-in.\n",{"id":29,"difficulty":14,"q":30,"a":31},"extend-which-class","Which class should a custom exception extend?","Extend **`Exception`** to make it **checked** (callers must catch or declare it)\nor **`RuntimeException`** to make it **unchecked** (no compiler obligation).\nNever extend `Throwable` or `Error` directly — those are reserved for the JVM and\ncatch-all framework code.\n\n```java\n\u002F\u002F checked: caller is forced to handle it\npublic class ConfigParseException extends Exception { }\n\n\u002F\u002F unchecked: a programming\u002Fusage error, no catch obligation\npublic class InvalidStateException extends RuntimeException { }\n```\n\nThe choice signals intent: subclass `Exception` for a recoverable condition the\ncaller should plan for, `RuntimeException` for a bug or precondition violation.\n**Rule of thumb:** pick the superclass first — it encodes whether the error is\n*expected* or a *defect*.\n",{"id":33,"difficulty":14,"q":34,"a":35},"standard-constructors","What are the four standard constructors a custom exception should provide?","Mirror the four constructors that `Exception`\u002F`RuntimeException` themselves\nexpose, so callers can supply a message, a cause, or both:\n\n```java\npublic class PaymentException extends RuntimeException {\n  public PaymentException() { super(); }\n  public PaymentException(String message) { super(message); }\n  public PaymentException(String message, Throwable cause) {\n    super(message, cause);\n  }\n  public PaymentException(Throwable cause) { super(cause); }\n}\n```\n\nThe **`(String, Throwable)`** and **`(Throwable)`** constructors are the ones\npeople forget, yet they're the most important — they enable **exception\nchaining** (wrapping a lower-level cause). **Rule of thumb:** always include at\nleast the `(String message)` and `(String message, Throwable cause)` forms.\n",{"id":37,"difficulty":38,"q":39,"a":40},"checked-vs-unchecked-design","hard","How do you decide between a checked and an unchecked custom exception?","Make it **checked** when the error is a **recoverable, expected condition** the\ncaller can reasonably handle (a missing file, a failed network call). Make it\n**unchecked** when it represents a **programming error** or violated precondition\nthe caller can't sensibly recover from (`null` argument, illegal state).\n\n| Aspect | Checked (`extends Exception`) | Unchecked (`extends RuntimeException`) |\n| ------ | ----------------------------- | -------------------------------------- |\n| Compiler enforces handling | Yes | No |\n| Use for | Recoverable, anticipated | Bugs \u002F preconditions |\n| Method signature | Must declare with `throws` | No declaration needed |\n| Caller's typical action | Catch & recover\u002Fretry | Let it propagate, fix the bug |\n\n**Rule of thumb:** \"Can the caller do something useful about it?\" Yes -> checked\n(or at least catchable); no -> unchecked.\n",{"id":42,"difficulty":38,"q":43,"a":44},"checked-exception-debate","What is the argument against checked exceptions?","Critics argue checked exceptions **hurt scalability and clutter code**: every\ncaller must catch or re-declare them, so adding a checked exception to a method\nripples `throws` clauses up the whole call stack. They also break cleanly with\n**lambdas and streams**, whose functional interfaces don't declare checked\nexceptions.\n\n```java\n\u002F\u002F a checked exception can't escape a stream lambda directly\nfiles.stream()\n     .map(f -> Files.readString(f)); \u002F\u002F won't compile — IOException is checked\n```\n\nThe result is often **swallowing or blanket-wrapping** in `RuntimeException`,\ndefeating the purpose. Modern frameworks (Spring, many libraries) lean almost\nentirely on **unchecked** exceptions for this reason. **Rule of thumb:** know\nboth sides — checked enforces handling, but overusing it leaks implementation and\nfights functional code.\n",{"id":46,"difficulty":14,"q":47,"a":48},"exception-chaining","What is exception chaining and why does it matter?","Exception chaining is **wrapping a low-level exception inside a higher-level one\nwhile keeping the original as the \"cause.\"** It lets you raise a meaningful\ndomain exception without **throwing away the root-cause diagnostic information**.\n\n```java\ntry {\n  return jdbc.query(sql);\n} catch (SQLException e) {\n  \u002F\u002F wrap, but keep the SQLException as the cause\n  throw new RepositoryException(\"failed to load user \" + id, e);\n}\n```\n\nThe chained cause appears in the stack trace under **\"Caused by:\"**, so you see\nboth the abstract failure and the SQL error that triggered it. Losing the cause\nmeans losing the line that actually broke. **Rule of thumb:** when translating an\nexception, always pass the original in as the cause.\n",{"id":50,"difficulty":14,"q":51,"a":52},"initcause-vs-constructor","What is the difference between the cause constructor and initCause()?","Both record a **cause**; the constructor is the preferred, concise form, while\n`initCause()` exists for exceptions whose class predates the cause constructors or\ndoesn't expose one.\n\n```java\n\u002F\u002F preferred — set the cause at construction\nthrow new ServiceException(\"lookup failed\", sqlEx);\n\n\u002F\u002F fallback — when no cause constructor is available\nServiceException ex = new ServiceException(\"lookup failed\");\nex.initCause(sqlEx);   \u002F\u002F can be called only ONCE\nthrow ex;\n```\n\n`initCause()` may be called **exactly once** and only if the cause wasn't already\nset, otherwise it throws `IllegalStateException`. `getCause()` reads it back.\n**Rule of thumb:** use the `(message, cause)` constructor; reach for `initCause`\nonly when a constructor isn't an option.\n",{"id":54,"difficulty":25,"q":55,"a":56},"getcause","What does getCause() return and when is it useful?","`Throwable.getCause()` returns the **underlying exception** that was chained in,\nor `null` if none was set. It lets handlers and logging code **walk the chain**\nto find the root cause.\n\n```java\ntry {\n  service.process();\n} catch (ServiceException e) {\n  Throwable root = e;\n  while (root.getCause() != null) root = root.getCause();\n  log.error(\"root cause: {}\", root.getMessage());\n}\n```\n\nMost logging frameworks print the full chain automatically (the \"Caused by:\"\nlines), so you rarely walk it by hand — but `getCause()` is how you inspect it\nprogrammatically (e.g., to unwrap a known wrapped exception). **Rule of thumb:**\nuse `getCause()` to inspect or unwrap; let the logger print the chain.\n",{"id":58,"difficulty":38,"q":59,"a":60},"preserve-stack-trace","How can you accidentally lose a stack trace, and how do you preserve it?","You lose the original trace by **catching an exception and throwing a new one\nwithout passing the cause** — the new exception's trace starts at the `throw`\nsite, hiding where it really failed.\n\n```java\n\u002F\u002F BAD — the original cause and its line are gone\ncatch (SQLException e) {\n  throw new DaoException(\"query failed\");\n}\n\n\u002F\u002F GOOD — chain the cause; both traces are kept\ncatch (SQLException e) {\n  throw new DaoException(\"query failed\", e);\n}\n```\n\nEqually bad is **swallowing** (`catch (Exception e) {}`) or logging *and*\nrethrowing a fresh exception, which double-logs and detaches the trace. **Rule of\nthumb:** when you rethrow, always carry the cause; never create a new exception\nthat drops the original.\n",{"id":62,"difficulty":14,"q":63,"a":64},"dont-swallow","What does it mean to swallow an exception and why is it dangerous?","Swallowing is **catching an exception and doing nothing** (or just logging at a\nlow level) so the failure silently disappears. The program continues in a broken\nstate and the bug surfaces far away, with no trace of the origin.\n\n```java\n\u002F\u002F the worst line in any codebase\ntry {\n  risky();\n} catch (Exception e) {\n  \u002F\u002F empty — failure vanishes\n}\n```\n\nIf you genuinely must ignore an exception, **comment why** and at minimum log it\nwith the stack trace. Catching `Exception`\u002F`Throwable` broadly is itself a smell —\nit can hide `RuntimeException`s and even `Error`s you never meant to suppress.\n**Rule of thumb:** never leave a `catch` block empty; handle, rethrow, or log\nwith the cause.\n",{"id":66,"difficulty":14,"q":67,"a":68},"custom-fields","How do you add custom data to an exception?","Add **final fields** populated through the constructor and exposed via getters.\nThis lets handlers retrieve structured context (an id, an error code) instead of\nparsing the message string.\n\n```java\npublic class OrderNotFoundException extends RuntimeException {\n  private final long orderId;\n\n  public OrderNotFoundException(long orderId) {\n    super(\"order not found: \" + orderId);\n    this.orderId = orderId;\n  }\n  public long getOrderId() { return orderId; }\n}\n```\n\nNow a caller can do `catch (OrderNotFoundException e) { retry(e.getOrderId()); }`.\nKeep the fields **immutable** (`final`) since an exception is a one-shot value\nobject. **Rule of thumb:** put any data the handler might need into typed fields,\nnot just the message.\n",{"id":70,"difficulty":25,"q":71,"a":72},"naming-convention","What is the naming convention for exception classes?","The class name should **end with `Exception`** and describe the failure clearly,\ne.g. `InsufficientFundsException`, `OrderNotFoundException`, `ConfigParseException`.\nSubtypes of `Error` end with `Error`.\n\n```java\nclass InvalidTokenException extends RuntimeException { } \u002F\u002F good\nclass TokenProblem extends RuntimeException { }          \u002F\u002F unclear — avoid\n```\n\nThe convention makes exceptions instantly recognizable in code and stack traces,\nand mirrors the JDK's own (`IllegalArgumentException`, `IOException`). Name them\nfor the **problem**, not the throwing method. **Rule of thumb:** suffix with\n`Exception` and name the condition, not the location.\n",{"id":74,"difficulty":14,"q":75,"a":76},"serializable","Why should a custom exception be serializable and how?","`Throwable` already **implements `Serializable`**, so every exception is\nserializable by inheritance — important because exceptions cross JVM boundaries\n(RMI, distributed apps, app servers). To keep serialization stable you should\ndeclare a **`serialVersionUID`** and ensure any custom fields are themselves\nserializable.\n\n```java\npublic class RemoteCallException extends RuntimeException {\n  private static final long serialVersionUID = 1L;\n  private final String endpoint;   \u002F\u002F String is serializable — OK\n\n  public RemoteCallException(String endpoint, Throwable cause) {\n    super(\"call failed: \" + endpoint, cause);\n    this.endpoint = endpoint;\n  }\n}\n```\n\nWithout an explicit `serialVersionUID`, the compiler-generated one changes with\nany edit, breaking deserialization of older instances. **Rule of thumb:** add a\n`serialVersionUID` and keep custom fields serializable (or mark them `transient`).\n",{"id":78,"difficulty":14,"q":79,"a":80},"immutable-exceptions","Should custom exceptions be immutable?","Yes — treat an exception as an **immutable value object**. Set everything through\nthe constructor and make fields `final`; an exception describes a moment in time\nand shouldn't be mutated after it's thrown.\n\n```java\npublic class ValidationException extends RuntimeException {\n  private final List\u003CString> errors;\n\n  public ValidationException(List\u003CString> errors) {\n    super(errors.size() + \" validation error(s)\");\n    this.errors = List.copyOf(errors);   \u002F\u002F defensive, unmodifiable copy\n  }\n  public List\u003CString> getErrors() { return errors; }\n}\n```\n\nDefensively copy mutable inputs (here `List.copyOf`) so the stored data can't be\nchanged by the caller afterward. Immutability also makes exceptions safe to share\nacross threads. **Rule of thumb:** all-`final` fields, defensive copies, no\nsetters.\n",{"id":82,"difficulty":14,"q":83,"a":84},"dont-extend-throwable","Why should you not extend Throwable or Error directly?","`Throwable` is the root of *everything* thrown; extending it directly creates a\ntype that is neither a clean `Exception` nor an `Error`, confusing callers and\ncatch blocks. `Error` is reserved for **serious JVM-level failures** (e.g.\n`OutOfMemoryError`) that applications are **not meant to catch**.\n\n```java\nclass MyFailure extends Error { }   \u002F\u002F WRONG — implies an unrecoverable JVM error\n\nclass MyFailure extends RuntimeException { }  \u002F\u002F right level of abstraction\n```\n\nThrowing an `Error` subtype tricks catch-all `catch (Exception e)` handlers into\n*not* catching it, and signals \"the JVM is broken\" when it isn't. **Rule of\nthumb:** subclass `Exception` or `RuntimeException` — leave `Throwable` and\n`Error` to the platform.\n",{"id":86,"difficulty":14,"q":87,"a":88},"dont-overuse","When is creating a custom exception overkill?","When a **standard JDK exception already says exactly what's wrong**, reuse it\nrather than inventing a parallel type. A bad argument is\n`IllegalArgumentException`; a bad object state is `IllegalStateException`; a\n`null` you reject is `NullPointerException` (or `Objects.requireNonNull`).\n\n```java\n\u002F\u002F no need for a custom \"BadAgeException\"\nif (age \u003C 0) throw new IllegalArgumentException(\"age must be >= 0\");\n\nObjects.requireNonNull(name, \"name\");  \u002F\u002F throws NPE with a clear message\n```\n\nA codebase littered with one-off exception classes that nobody catches\nspecifically is just noise. **Rule of thumb:** create a custom exception only\nwhen callers will *catch it by type* or need *data from it*; otherwise reuse a\nbuilt-in.\n",{"id":90,"difficulty":14,"q":91,"a":92},"throwing-in-constructor","Can you throw an exception from a constructor, and what are the implications?","Yes — throwing from a constructor is the correct way to **reject invalid\nconstruction** so no half-initialized object escapes. If the constructor throws,\nthe object is **never assigned** to the variable and becomes garbage.\n\n```java\npublic class Percentage {\n  private final int value;\n  public Percentage(int value) {\n    if (value \u003C 0 || value > 100)\n      throw new IllegalArgumentException(\"0..100, got \" + value);\n    this.value = value;   \u002F\u002F only reached if valid\n  }\n}\n```\n\nValidate **before** assigning fields, and prefer **unchecked** exceptions here\nsince invalid arguments are programming errors. Beware throwing from a constructor\nthat already opened resources — the object's `close()` won't be called, so clean\nup first. **Rule of thumb:** fail fast in the constructor to enforce invariants.\n",{"id":94,"difficulty":14,"q":95,"a":96},"rethrowing","What does it mean to rethrow an exception?","Rethrowing is **catching an exception, doing some work (log, clean up, add\ncontext), then throwing it onward** so an outer handler still sees it. You can\nrethrow the same instance or wrap it.\n\n```java\ntry {\n  process(record);\n} catch (ServiceException e) {\n  metrics.increment(\"process.failure\");\n  throw e;            \u002F\u002F rethrow the SAME exception, trace intact\n}\n```\n\nRethrowing the **same** instance preserves the original stack trace perfectly.\nSince Java 7, **precise rethrow** lets the compiler narrow what a rethrown\n`Exception` can actually be, so you can declare specific subtypes in `throws`.\n**Rule of thumb:** rethrow the same object to keep the trace; wrap only when you\nneed to change the abstraction level.\n",{"id":98,"difficulty":38,"q":99,"a":100},"exception-translation","What is exception translation and why is it a best practice?","Exception translation is **catching a low-level exception and throwing a\nhigher-level one appropriate to your API's abstraction** — so callers depend on\n*your* exception types, not on implementation details like `SQLException` or\n`IOException`.\n\n```java\n\u002F\u002F the repository hides JDBC behind its own abstraction\npublic User findById(long id) {\n  try {\n    return jdbc.queryForObject(SQL, mapper, id);\n  } catch (SQLException e) {\n    throw new UserRepositoryException(\"findById \" + id, e); \u002F\u002F translate + chain\n  }\n}\n```\n\nThis keeps callers decoupled from the data layer; you could swap JDBC for an ORM\nwithout changing the exceptions they catch. Always **chain the cause** so the\nlow-level detail is still available for debugging. **Rule of thumb:** translate\nat layer boundaries, and never let leaky low-level exceptions cross your API.\n",{"id":102,"difficulty":14,"q":103,"a":104},"business-vs-technical","What is the difference between business and technical exceptions?","A **business (domain) exception** represents a violation of business rules that\nthe application expects and often handles (`InsufficientFundsException`,\n`DuplicateEmailException`). A **technical (system) exception** represents\ninfrastructure failure (`DatabaseUnavailableException`, network timeouts) that's\nusually unexpected and bubbles up to generic handling.\n\n```java\n\u002F\u002F business: expected, often caught and shown to the user\nthrow new InsufficientFundsException(accountId, amount);\n\n\u002F\u002F technical: infrastructure broke — log, alert, return 500\nthrow new ServiceUnavailableException(\"payment gateway down\", cause);\n```\n\nSeparating them lets you handle each differently: business errors map to friendly\nuser messages and 4xx responses, technical ones to retries, alerts, and 5xx.\n**Rule of thumb:** model business failures as their own hierarchy, distinct from\ntechnical\u002Finfrastructure failures.\n",{"id":106,"difficulty":38,"q":107,"a":108},"unchecked-default-style","Why do many modern Java codebases default to unchecked exceptions?","Modern style (popularized by Spring and much of the ecosystem) favors\n**unchecked exceptions** because they keep method signatures clean, **work with\nlambdas and streams**, and avoid forcing every caller to catch or re-declare.\nHandling is centralized instead of scattered.\n\n```java\n\u002F\u002F a single global handler turns domain exceptions into HTTP responses\n@ExceptionHandler(OrderNotFoundException.class)\npublic ResponseEntity\u003C?> handle(OrderNotFoundException e) {\n  return ResponseEntity.status(404).body(e.getMessage());\n}\n```\n\nThe trade-off is the compiler no longer reminds callers an error can occur, so\nteams rely on **documentation (`@throws` Javadoc) and convention** instead. Some\nargue this loses checked exceptions' safety, but most accept it for the reduced\nceremony. **Rule of thumb:** default to unchecked domain exceptions and handle\nthem centrally, documenting them clearly.\n",21,null,{"description":11},"Java custom exceptions interview questions — when and how to create your own exception classes, checked vs unchecked design, exception chaining, preserving stack traces, and best practices.","java\u002Fexceptions\u002Fcustom-exceptions","Exceptions","exceptions","2026-06-20","2aLetMEoLmBtyD9OBd0Ho317zD_7ntF_i_n55yrfnNg",[119,123,126],{"subtopic":120,"path":121,"order":122},"Exception Handling","\u002Fjava\u002Fexceptions\u002Fexception-handling",1,{"subtopic":124,"path":125,"order":12},"try-with-resources","\u002Fjava\u002Fexceptions\u002Ftry-with-resources",{"subtopic":6,"path":21,"order":20},{"path":128,"title":129},"\u002Fblog\u002Fjava-custom-exceptions","Java Custom Exceptions — Design, Chaining & Best Practices",1782244116833]