What is the Flyweight pattern, and where does the JDK use it for boxed integers?
answer
- Flyweight = share immutable objects to save memory
- Integer.valueOf caches -128..127
- autoboxing compiles to valueOf
- use equals() not ==
- Long/Short/Byte/Character/Boolean cache too; Float/Double don't
basics
~10 sFlyweight reuses shared, unchangeable objects instead of creating new ones. The JDK does this for small Integer values: Integer.valueOf for numbers -128 to 127 returns the same cached object every time.
solid answer
~40 sFlyweight is a structural design pattern that minimizes memory by sharing identical, immutable objects rather than allocating a new one each time. The JDK applies it to boxed values: Integer.valueOf(int) keeps a cache of the Integer objects for -128 to 127 and returns the same instance for repeated calls in that range, while values outside it always allocate a fresh object. Because autoboxing (e.g. Integer i = 5) compiles to Integer.valueOf, this cache is automatic. The practical consequence is that == on small boxed Integers can appear to work (same cached instance) but breaks for larger values, so you must use equals() to compare values. Similar caches exist for Long, Short, Byte, Character, and Boolean.
go deeper
Knows Integer.valueOf caches small integers and that you should compare with equals(), not ==.
Explains autoboxing compiles to valueOf, names the -128..127 range, and can predict == results for in-range vs out-of-range values.
Connects it to the Flyweight pattern and immutability, knows the cache covers other wrappers, and can articulate the bug class it creates and how to avoid it.
Discusses the rationale (allocation pressure), the configurability of the high bound, JIT/escape-analysis interactions, and sets team conventions to prevent identity-comparison bugs at scale.
## What 'Flyweight' means A **design pattern** is a reusable solution to a common programming problem. **Flyweight** is one of the classic structural patterns. Its goal is to reduce memory use when a program needs a huge number of objects that are largely identical. Instead of creating a separate object for every occurrence, you create one **shared** object and hand the same reference out repeatedly. This only works safely when the shared object is **immutable** — its state can never change after construction. If it could change, one caller mutating it would surprise every other caller holding the same reference. Immutability is what makes sharing safe. ## What 'boxed integer' means Java has two number worlds: - **primitives** like `int` — raw values, no object identity, very cheap. - **wrapper objects** like `Integer` — real objects on the heap that *wrap* an `int` so it can be used where an object is required (e.g. in a `List<Integer>`). Converting an `int` to an `Integer` is called **boxing**; the reverse is **unboxing**. Since Java 5, the compiler does this for you automatically — **autoboxing**. Writing `Integer x = 5;` is compiled into `Integer x = Integer.valueOf(5);`. ## How the JDK applies Flyweight `Integer.valueOf(int)` does **not** always allocate a new object. It maintains an internal cache (`IntegerCache`) holding pre-built `Integer` instances for a small range — by specification **-128 to 127** (the low bound is fixed; the high bound can be raised via the `-XX:AutoBoxCacheMax` / `java.lang.Integer.IntegerCache.high` JVM setting). For any value in that range, `valueOf` returns the **same cached instance** every time. For values outside the range it constructs a new `Integer`. So the cached small `Integer`s are the flyweights: shared, immutable, reused. The same caching idea is used for other wrappers: `Long`, `Short`, `Byte` cache -128..127, `Character` caches 0..127, and `Boolean.TRUE`/`Boolean.FALSE` are effectively singletons. `Float` and `Double` do **not** cache. ## Why it matters in practice Because autoboxing routes through `valueOf`, two separate `Integer i1 = 100; Integer i2 = 100;` actually point at the **same** cached object, so `i1 == i2` is `true`. But `Integer i3 = 200; Integer i4 = 200;` allocate two different objects, so `i3 == i4` is `false`. The `==` operator on objects compares **references** (are these the very same object?), not values. The cache makes `==` *accidentally* correct for small numbers and *silently wrong* for large ones — a classic bug. The fix is always to compare wrapper values with `.equals()` (or unbox to `int` and use `==` on primitives). The original intent of the cache is performance/memory: programs box small integers constantly (loop counters, map keys, collection elements), so reusing the common ones avoids millions of tiny allocations.
- Why is immutability a precondition for the Flyweight pattern?Because the same instance is shared across many callers; if it were mutable, one caller's change would corrupt the value seen by everyone else. Integer is immutable, so sharing is safe.
- Does new Integer(5) participate in the cache?No. The constructor always creates a brand-new object and bypasses the cache; it is also deprecated in favor of Integer.valueOf, exactly to encourage cache use.
saying these in an interview costs you the question
- Saying the cache is configurable on the low end — only the high bound can be raised
- Claiming new Integer(5) uses the cache — the constructor always allocates (and is deprecated)
- Thinking Float and Double are cached
- Confusing == (reference identity) with value equality