What are string literals and the string constant pool, and how do underscores in numeric literals work? When can you NOT use an underscore?
answer
- Double quotes = String literal -> pooled (interned)
- Literal == literal is true; use .equals for content
- new String() makes a distinct, non-pooled object
- Underscores group digits, compiler strips them
- _ must have a digit on BOTH sides
- No _ at ends, next to '.', suffix, or base prefix
basics
~20 sA string literal is text in double quotes; identical literals are shared (interned) in a string pool, so "hi" == "hi" is true. Underscores group digits in numbers for readability, like 1_000_000, but can't sit at the start, end, or next to a dot or base prefix.
solid answer
~40 sA string literal is characters in double quotes, e.g. "hello"; it produces a String object. Java interns string literals in the string constant pool, so two identical literals refer to the same object and == returns true between them, whereas new String("hi") makes a distinct object. Underscores, added in Java 7, are visual digit separators inside numeric literals: 1_000_000, 0xFF_FF, 0b1010_0101, 3.141_592. The compiler simply strips them. The restriction is that an underscore must sit strictly between two digits: not at the start or end of the number, not adjacent to the decimal point, not next to the L/f/d suffix, and not right after the 0x/0b prefix or before/after the leading-zero octal marker in the relevant spots. They affect readability only, never the value or type.
code
java · 19 lines// String interning
String a = "hi";
String b = "hi";
String c = new String("hi");
System.out.println(a == b); // true (same pooled literal)
System.out.println(a == c); // false (new makes a distinct object)
System.out.println(a.equals(c)); // true (same characters)
// Underscore digit separators (compiler ignores them)
int million = 1_000_000; // == 1000000
int hex = 0xCAFE_BABE;
double pi = 3.141_592;
// These do NOT compile:
// int x = _100; // leading underscore
// int y = 100_; // trailing underscore
// double z = 3._14; // adjacent to the decimal point
// long w = 100_L; // adjacent to the suffix
// int q = 0x_FF; // right after the base prefixgo deeper
Know strings use double quotes and that you compare them with .equals, and that 1_000_000 is just a readable way to write 1000000.
Explain the constant pool / interning, the == vs .equals distinction including new String, and state the underscore placement rule with examples that fail.
Discuss compile-time constant folding/pooling vs runtime concatenation, String.intern, and why == coincidentally works for literals but is a bug in general comparisons.
Address memory and GC implications of the intern pool (historic PermGen vs heap), security of pooled secrets in strings, and conventions/linters enforcing .equals and readable digit grouping.
## String literals A **String literal** is a run of characters between **double quotes**: `"hello"`, `""` (empty), `"line1\nline2"` (escapes allowed). Evaluating a String literal yields a reference to a `String` **object** — an immutable sequence of chars. ### The string constant pool and interning Java maintains a **string constant pool**: an internal table of unique String values. When the compiler sees a String literal, it places that value in the pool, and every identical literal anywhere in the program refers to the **same** pooled object. This sharing is called **interning**. Consequences: ```java String a = "hi"; String b = "hi"; a == b; // true -> same pooled object (reference equality) String c = new String("hi"); a == c; // false -> new String(...) makes a DISTINCT object a.equals(c); // true -> same characters ``` `==` on references tests **identity** (same object); `.equals` tests **value** (same characters). Because literals are interned, `==` happens to work *between literals*, but you must always use `.equals` for String content comparison, since strings from `new`, I/O, or concatenation at runtime are generally not pooled. (You can manually pool a runtime string with `String.intern()`.) Compile-time constant concatenation like `"ab" + "c"` is folded to the literal `"abc"` and pooled, but `someVar + "c"` is computed at runtime and not pooled. Interning saves memory (one copy of each distinct literal) — that is its purpose, not enabling `==` comparisons. ## Underscores in numeric literals (Java 7+) Large numbers are hard to read: `1000000` vs `1_000_000`. Java 7 added **underscore digit separators**: underscores placed *between digits* of any numeric literal, which the compiler **ignores entirely** (they change neither value nor type). They work in all bases and in floating-point: ```java int million = 1_000_000; long card = 1234_5678_9012_3456L; int hex = 0xFF_EC_DE_5E; int binary = 0b0101_0010_1010_1110; double pi = 3.141_592_653; ``` ### Where underscores are NOT allowed An underscore must be **immediately surrounded by digits**. It is a **compile error** to place it: - at the **start** or **end** of the number: `_100`, `100_` - **adjacent to the decimal point**: `3._14`, `3_.14` - **before the `L`/`f`/`d` suffix**: `100_L`, `1.0_f` - **right after the base prefix** `0x` / `0b`: `0x_FF`, `0b_101` - **right before** the prefix or in other non-digit-adjacent spots Note a subtle case: `0_52` is allowed (it's the octal literal 052 = 42 with a separator after the leading zero, since the zero is a digit), but `_052` is not. The rule to remember is simply: **a `_` must have a digit on both sides.** ## Why both features matter String interning explains the classic `==` vs `.equals` interview trap and the memory model of literals. Underscores are a pure-readability tool — use them to group thousands, bytes, or card-number chunks, and know the placement rule so a misplaced `_` doesn't cause a confusing compile error.
- Why is new String("hi") == "hi" false?The literal "hi" is interned in the string pool, but new String("hi") explicitly constructs a brand-new String object on the heap, distinct from the pooled one. == compares object identity, so it is false. .equals compares characters and is true. You can fold the new one back into the pool with .intern().
- Is 0x_FF a valid literal?No. An underscore must sit strictly between two digits; 0x_FF places it right after the base prefix and before the first digit, which is a compile error. 0xF_F or 0xFF would be valid.
saying these in an interview costs you the question
- Comparing strings with == in general code (works only for pooled literals)
- Thinking interning's purpose is to make == work (it is memory saving)
- Believing underscores change the numeric value
- Putting an underscore at the start/end or next to a dot/suffix/prefix and expecting it to compile