Skip to content
Back to skills

Java Object Construction

ASecurity

Choosing how an object comes into existence in Java: static factory versus public constructor, the of/from/valueOf/getInstance naming conventions, instance control (caching, canonicalisation, value-based classes), enum and holder singletons, noninstantiable utility classes, and passing collaborators in rather than hardwiring them with new. Use when a class has several constructors distinguished only by parameter types, when a constructor does work beyond assigning fields, when a singleton or ...

  • 2 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 19, 2026
developmentjavaexpresstestingapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned September 29, 2026

npx -y skills add robsonkades/agent-skills --skill java-object-construction --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Java Object Construction?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Java Object Construction
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/robsonkades-java-object-construction/badge)](https://www.skillsdirectory.com/skills/robsonkades-java-object-construction)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: java-object-construction
description: >
  Choosing how an object comes into existence in Java: static factory versus public
  constructor, the of/from/valueOf/getInstance naming conventions, instance control
  (caching, canonicalisation, value-based classes), enum and holder singletons,
  noninstantiable utility classes, and passing collaborators in rather than hardwiring them
  with new. Use when a class has several constructors distinguished only by parameter types,
  when a constructor does work beyond assigning fields, when a singleton or a static mutable
  field is proposed, when new appears inside domain logic for something the code can never
  substitute in a test, or when a factory hands back a type its callers should not be able
  to name. Does not cover builders and fluent chains (java-fluent-apis), which dependency
  edge should exist at all (java-dependency-inversion), defensive copying of components
  (java-immutability), or releasing what construction acquires (java-resource-management).
---

# Java Object Construction

## Purpose

Decide how instances are obtained, and keep that decision reversible. Three failure modes
this exists to prevent: the overload set where callers pick the wrong constructor because
the types happen to match; the constructor that does real work — I/O, registration,
overridable calls — so the object is unusable in a test and observable half-built; and the
singleton or static field that is treated as global state when its actual scope is one
class loader in one JVM among N replicas.

## Workflow

Use Java 21 without preview as the example baseline. Inspect compiler/toolchain, runtime,
framework construction/serialization rules and supported callers before changing a creation
path. Do not upgrade Java or add a container for these examples. Unless marked complete, reference
code blocks are partial or alternative sketches; supply imports and collaborators, and compile
alternatives separately. Missing lifecycle/identity evidence makes a recommendation conditional.

1. **Sketch ordinary, advanced and invalid consumer calls before declarations.** Inspect existing
   callers: required values, meaningful defaults, conversion failures, repeated construction,
   aliases and who owns a supplied or acquired resource. Resolve material unknowns from project
   evidence or a focused question; keep minor reversible assumptions explicit.
   If two ways of creating the object differ in
   _meaning_ rather than in parameter types, that difference belongs in a name, not in an
   overload. `Money.ofMinor(1050)` and `Money.ofMajor(new BigDecimal("10.50"))` should not be
   two ambiguous constructors—and an exact decimal must not pass through a `double`.
2. **Compare the forms that address the observed caller risk.** An ordinary constructor is often
   enough; a record fits transparent component values, a named factory expresses distinct creation
   meanings or instance control, and a builder can clarify costly optionality or invalid combinations.
   Compare lifecycle, invariants, framework paths and existing callers, not a fixed progression or
   parameter threshold. Keep the simplest viable form, including the existing one, and state what
   new caller evidence would change the choice; `java-fluent-apis` owns builder mechanics.
3. **Decide instance control explicitly.** Does every successful call have to produce a fresh
   identity? A factory may cache, canonicalise, share or allocate. Identity becomes a contract
   only if the API promises it; otherwise callers must use value equality. Bound any cache.
4. **Keep ordinary domain constructors side-effect-contained.** Validate, normalize, assign.
   Resource-owning types may necessarily acquire a resource and must define failure/cleanup
   semantics. Never register/start threads/call overridable methods/let `this` escape during
   construction—see java-immutability's safe-publication rules.
5. **Make variable collaborators explicit.** Pass a clock, HTTP client, repository or random source
   when its policy, lifetime or substitution matters; a factory can assemble them at the composition
   root. Ordinary owned values may still use `new`. Do not introduce an interface or container merely
   to avoid allocation syntax, and distinguish borrowed collaborators from owned resources.
6. **Verify.** Domain construction is testable without unrelated infrastructure. Resource-owner
   integration tests cover acquisition failure and cleanup using isolated resources. Any static
   mutable state has a justified scope, concurrency and shutdown policy; each factory's identity
   promise (fresh, cached, or unspecified) is written down.

## Rules

- Prefer a named static factory when the class has more than one meaningful way to be
  created, when creation may return a cached or a substituted instance, or when the return
  type should be a common abstraction rather than the concrete class. A published sealed
  hierarchy can expose its variants to exhaustive switches; returning its supertype does not
  make adding variants transparent to callers. Prefer a public constructor when there is
  exactly one way, it always allocates, and the type is the type.
- Follow the platform naming conventions — `of`, `from`, `valueOf`, `instance`/`getInstance`,
  `create`/`newInstance`, `copyOf`, `parse`. A factory called `build`, `make` or `get` on a
  type whose neighbours use `of` may be surprising; an established domain/framework vocabulary can
  justify it. The documented behavior, not the name, determines freshness, validation and ownership.
- A private-constructor-only surface blocks ordinary external subclass construction; nested
  code with private access is a separate case. Use `final` when the type must prohibit all
  subclasses. Blocking external extension is usually the
  point; take it deliberately, not by accident, and say so in the Javadoc rather than
  leaving callers to discover it from a compile error.
- Document whether fresh or canonical identity is guaranteed. An implementation is free to add
  or remove an undocumented cache while preserving value semantics; callers using `==` on that
  basis are wrong. A documented freshness/canonicalization guarantee is an API commitment and
  constrains future implementations.
- If concurrent calls must return the same canonical instance, make winner selection atomic
  and return that winner. A thread-safe cache alone does not make get/create/put atomic or
  prevent callers receiving different candidates; test the identity contract under contention.
- Never cache without a bound. An unbounded interning map keyed by user or tenant data is a
  leak with a factory in front of it — see java-reference-types-and-leaks.
- Do not synchronise on, or key identity off, a value-based class (`Optional`, `LocalDate`,
  `Integer`, the boxed primitives). Their identity is explicitly unspecified and the
  identity-sensitive operations are documented as subject to failure in a future release.
- Prevent ordinary external construction of a utility class with a private constructor.
  Throwing from it can catch accidental internal invocation; `final` expresses no extension,
  but neither is required just to block external construction. `abstract` alone permits
  concrete subclasses and suggests an extension contract.
- Within standard reflection and Java serialization, a single-element enum has the strongest
  built-in singleton guarantees. A `private static final` field plus private constructor can be
  bypassed by deep reflection (subject to module/access policy). Serialization constructs a
  temporary instance before `readResolve` returns the canonical one; references that escape
  through the deserialized graph are not repaired by that replacement. Audit that protocol,
  not merely the hook's presence; see the singleton reference.
- A singleton belongs to its defining class identity and class loader in one JVM. It is not a global lock, not a
  cluster-wide counter and not a distributed cache. When uniqueness must hold across
  replicas, define the invariant and hand it to the relevant distributed-system design;
  leader-election or distributed-locks-and-leases may apply, but neither is automatically required
  or sufficient. The local singleton is at best a handle to that mechanism.
- Static mutable state can couple tests and hide per-replica behavior. Prefer an injected,
  replaceable object for application/tenant/request state. Intentionally class-loader-scoped
  infrastructure may remain static with explicit bounds, concurrency and lifecycle policy;
  a container-managed singleton also needs the right scope and shutdown contract.
- Prefer the lazy-initialisation holder class to double-checked locking when a static
  really must be built lazily; and prefer eager initialisation to both unless the cost of
  building it is proven and the object is genuinely often unused. java-memory-model owns
  the correctness argument.

For the change report, show the decisive consumer calls, creation/identity contract, preserved
callers, ownership on success/failure, and targeted checks run. Exercise every supported creation
path against the same invariants, including a record's canonical constructor; a factory-only check
does not protect paths that bypass it. Tests of one implementation do not establish a new
public identity guarantee or a cluster-wide singleton.

## References

- [Factories and instance control](references/factories-and-instance-control.md) — read
  when choosing between a constructor, a named factory and a record's canonical
  constructor, when naming a factory, when a factory will cache or canonicalise
  instances, or when a factory's return type must survive API evolution; includes a compact
  consumer-first constructor/factory example and misuse checks.
- [Singletons and static state](references/singletons-and-static-state.md) — read whenever
  a singleton, a static registry, a static cache or a static mutable field is proposed or
  found: the forms, what each actually defends against, the testing and class-loader
  consequences, and what changes when the process is one of many.

Files in this skill

  • SKILL.md9.5 KB
  • references/factories-and-instance-control.md14.4 KB
  • references/singletons-and-static-state.md9.1 KB
  • skill.yaml1.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.