[{"data":1,"prerenderedAt":146},["ShallowReactive",2],{"qa-\u002Fjava\u002Ffundamentals\u002Fstrings":3},{"page":4,"siblings":129,"blog":143},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":12,"path":20,"questions":21,"questionsCount":120,"related":121,"seo":122,"seoDescription":123,"stem":124,"subtopic":6,"topic":125,"topicSlug":126,"updated":127,"__hash__":128},"qa\u002Fjava\u002Ffundamentals\u002Fstrings.md","Strings",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"easy","md","Java","java",{},true,"\u002Fjava\u002Ffundamentals\u002Fstrings",[22,27,31,35,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116],{"id":23,"difficulty":24,"q":25,"a":26},"string-immutable","medium","Why are Strings immutable in Java, and what does that buy you?","A `String` wraps a `private final` array of characters that is never exposed\nor modified after construction; every \"modifying\" method returns a **new**\n`String`. Immutability buys four things interviewers want to hear:\n\n- **Security** — a path, URL, or username can't be changed after a check.\n- **Caching \u002F pooling** — identical literals can be shared safely.\n- **Thread safety** — an immutable object needs no synchronization.\n- **Hashcode caching** — the hash is computed once and can never go stale,\n  making `String` an ideal `HashMap` key.\n\n```java\nString s = \"hello\";\ns.concat(\" world\");      \u002F\u002F returns a NEW String, discarded here\nSystem.out.println(s);   \u002F\u002F \"hello\" — s itself is unchanged\ns = s.toUpperCase();     \u002F\u002F reassign the reference to the new String\n```\n\nThe trade-off is garbage: heavy string building creates throwaway objects,\nwhich is exactly why `StringBuilder` exists.\n",{"id":28,"difficulty":24,"q":29,"a":30},"string-pool","What is the String constant pool?","The string pool (a.k.a. the **intern pool**) is a special area where the JVM\nstores **one shared copy** of each distinct string **literal**. Two identical\nliterals refer to the same pooled object, saving memory. Strings built with\n`new` are *not* pooled — they create a fresh heap object.\n\n```java\nString a = \"hi\";              \u002F\u002F pooled\nString b = \"hi\";              \u002F\u002F same pooled object\nString c = new String(\"hi\");  \u002F\u002F new heap object, NOT pooled\na == b   \u002F\u002F true  — same pooled reference\na == c   \u002F\u002F false — different objects\n```\n\nImmutability is what makes pooling safe — since no one can mutate `\"hi\"`,\nit's harmless to share it everywhere. Since Java 7 the pool lives on the\nheap (not PermGen), so it's subject to normal garbage collection.\n",{"id":32,"difficulty":24,"q":33,"a":34},"literal-vs-new","What is the difference between String s = \"x\" and new String(\"x\")?","A **literal** is resolved at class-load time and points at the **pooled**\nobject — repeated literals share one instance. **`new String(\"x\")`** forces a\nbrand-new object on the heap *plus* puts `\"x\"` in the pool, so it can create\n**two** objects and is almost always wasteful.\n\n```java\nString a = \"x\";            \u002F\u002F 1 pooled object\nString b = \"x\";            \u002F\u002F reuses the pooled object\nString c = new String(\"x\"); \u002F\u002F 1 new heap object (+ \"x\" already pooled)\na == b          \u002F\u002F true\na == c          \u002F\u002F false — c is a distinct object\na.equals(c)     \u002F\u002F true  — same characters\n```\n\nRule: never write `new String(\"literal\")` — it defeats pooling for no benefit.\n",{"id":36,"difficulty":37,"q":38,"a":39},"intern","hard","What does the intern() method do?","`intern()` returns the **canonical pooled copy** of a string: if an equal\nstring is already in the pool, you get that reference; otherwise the string\nis added to the pool and that reference is returned. It lets you force\n`==` to work on computed strings.\n\n```java\nString a = \"hi\";\nString c = new String(\"hi\");\na == c            \u002F\u002F false\na == c.intern()   \u002F\u002F true — intern() returns the pooled \"hi\"\n\nString built = (\"h\" + new String(\"i\")).intern();\na == built        \u002F\u002F true\n```\n\nUse it sparingly: interning huge numbers of unique strings just fills the\npool and can hurt more than it helps. It's mainly useful for deduplicating a\nbounded set of repeated values to save memory.\n",{"id":41,"difficulty":14,"q":42,"a":43},"eqeq-vs-equals","What is the difference between == and .equals() for Strings?","`==` compares **references** (do both variables point to the *same* object?),\nwhile `.equals()` compares the **characters**. For strings you almost always\nwant `.equals()` — `==` only \"works\" by accident when both operands happen to\nbe the same pooled literal.\n\n```java\nString a = \"hi\";\nString b = new String(\"hi\");\na == b         \u002F\u002F false — different objects\na.equals(b)    \u002F\u002F true  — same characters\n\nString c = \"hi\";\na == c         \u002F\u002F true  — both the pooled literal (coincidence, don't rely on it)\n```\n\nThis is the single most common Java beginner bug. Always compare string\n*content* with `.equals()` (or `equalsIgnoreCase`).\n",{"id":45,"difficulty":14,"q":46,"a":47},"null-safe-equals","How do you compare strings safely when one might be null?","Calling `.equals()` on a `null` reference throws **`NullPointerException`**.\nThe classic trick is to put the **known constant on the left**, or use the\nnull-safe `Objects.equals`.\n\n```java\nString input = maybeNull();\n\ninput.equals(\"yes\");          \u002F\u002F NPE if input is null\n\"yes\".equals(input);          \u002F\u002F safe — false when input is null\n\nObjects.equals(input, \"yes\"); \u002F\u002F safe both ways\n```\n\nThe \"Yoda condition\" (`\"yes\".equals(input)`) is the idiomatic fix; reach for\n`Objects.equals(a, b)` when *either* side could be null.\n",{"id":49,"difficulty":24,"q":50,"a":51},"stringbuilder-vs-buffer","What is the difference between String, StringBuilder and StringBuffer?","| Type | Mutable? | Thread-safe? | Use when |\n| ---- | -------- | ------------ | -------- |\n| `String` | no | yes (immutable) | fixed text, map keys |\n| `StringBuilder` | yes | no | building strings (the default) |\n| `StringBuffer` | yes | yes (synchronized) | shared across threads (rare) |\n\n`StringBuilder` and `StringBuffer` share the **same API**; the only\ndifference is `StringBuffer`'s methods are `synchronized`, which makes it\nslower.\n\n```java\nStringBuilder sb = new StringBuilder();\nsb.append(\"a\").append(1).append(true); \u002F\u002F fluent, mutates in place\nString result = sb.toString();         \u002F\u002F \"a1true\"\n```\n\nPrefer **`StringBuilder`** by default; you rarely need `StringBuffer` because\nshared mutable string state across threads is uncommon (and usually better\nhandled with a local builder per thread).\n",{"id":53,"difficulty":24,"q":54,"a":55},"concat-in-loop","Why is concatenating strings in a loop with + a problem?","Because `String` is immutable, `s += part` allocates a **new** `String` (and\ncopies all existing characters) on **every iteration** — overall **O(n²)**\nwork and a pile of garbage. A single `StringBuilder` reuses one growing\nbuffer for amortized **O(n)**.\n\n```java\n\u002F\u002F O(n²) — a new String each iteration\nString r = \"\";\nfor (String p : parts) r += p;\n\n\u002F\u002F O(n) — one reused buffer\nStringBuilder sb = new StringBuilder();\nfor (String p : parts) sb.append(p);\nString r2 = sb.toString();\n```\n\nThe compiler optimizes a *single* `a + b + c` expression into `StringBuilder`\ncalls automatically — the problem is concatenation **inside a loop**, which it\ncannot collapse.\n",{"id":57,"difficulty":37,"q":58,"a":59},"plus-compiles-to","What does the + operator on strings compile to?","Historically `javac` rewrote `a + b + c` into a chain of\n`new StringBuilder().append(a).append(b).append(c).toString()`. Since\n**Java 9**, it instead emits an **`invokedynamic`** call bound to\n`StringConcatFactory` (\"indified string concatenation\"), letting the JVM pick\nthe optimal strategy at runtime.\n\n```java\nString s = \"x\" + n + \"y\";\n\u002F\u002F pre-9 : new StringBuilder().append(\"x\").append(n).append(\"y\").toString()\n\u002F\u002F 9+    : invokedynamic -> StringConcatFactory.makeConcatWithConstants(...)\n```\n\nPractical takeaway is unchanged: a single `+` expression is fine and fast; a\n`+=` accumulation **inside a loop** still creates one object per iteration —\nuse `StringBuilder` there.\n",{"id":61,"difficulty":14,"q":62,"a":63},"equalsignorecase-compareto","What is the difference between equals, equalsIgnoreCase and compareTo?","- **`equals`** — case-**sensitive** content equality, returns `boolean`.\n- **`equalsIgnoreCase`** — same, but ignores case.\n- **`compareTo`** — **lexicographic ordering**, returns an `int`: negative if\n  this string sorts before the argument, `0` if equal, positive if after.\n\n```java\n\"abc\".equals(\"ABC\")            \u002F\u002F false\n\"abc\".equalsIgnoreCase(\"ABC\")  \u002F\u002F true\n\"apple\".compareTo(\"banana\")    \u002F\u002F negative (a \u003C b)\n\"abc\".compareTo(\"abc\")         \u002F\u002F 0\n\"abc\".compareToIgnoreCase(\"ABC\") \u002F\u002F 0\n```\n\nUse `equals`\u002F`equalsIgnoreCase` for \"are these the same?\" and `compareTo`\n(or `String.CASE_INSENSITIVE_ORDER`) when **sorting**.\n",{"id":65,"difficulty":24,"q":66,"a":67},"substring","How does substring work and what are its gotchas?","`substring(begin)` returns from `begin` to the end; `substring(begin, end)`\nreturns `[begin, end)` — **`end` is exclusive**. Indices out of range throw\n`StringIndexOutOfBoundsException`.\n\n```java\n\"interview\".substring(0, 5);  \u002F\u002F \"inter\" — chars 0..4\n\"interview\".substring(5);     \u002F\u002F \"view\"\n\"abc\".substring(3);           \u002F\u002F \"\"  (begin == length is legal)\n\"abc\".substring(1, 1);        \u002F\u002F \"\"  (begin == end)\n\"abc\".substring(2, 1);        \u002F\u002F exception — end \u003C begin\n```\n\nModern note: since Java 7u6 `substring` **copies** the chars into a new array\n(older JVMs shared the backing array, which could leak the whole parent\nstring), so there's no longer a hidden memory-retention trap.\n",{"id":69,"difficulty":14,"q":70,"a":71},"indexof-charat-contains","How do indexOf, charAt and contains work?","- **`charAt(i)`** — the `char` at index `i` (0-based); out of range throws\n  `StringIndexOutOfBoundsException`.\n- **`indexOf(x)`** — first index of a char or substring, or **`-1`** if absent.\n- **`contains(seq)`** — `boolean`, true if the substring occurs.\n\n```java\nString s = \"banana\";\ns.charAt(1);          \u002F\u002F 'a'\ns.indexOf('a');       \u002F\u002F 1   (first match)\ns.indexOf('a', 2);    \u002F\u002F 3   (start searching from index 2)\ns.lastIndexOf('a');   \u002F\u002F 5\ns.indexOf(\"xyz\");     \u002F\u002F -1  (not found)\ns.contains(\"nan\");    \u002F\u002F true\n```\n\nRemember `indexOf` returns **−1**, not throws, when nothing matches — guard\nwith `if (i >= 0)` before using the result as an index.\n",{"id":73,"difficulty":24,"q":74,"a":75},"split","How does String.split work and what are its surprises?","`split(regex)` breaks a string on a **regular expression** (not a literal!)\nand returns a `String[]`. Two surprises: it **drops trailing empty strings**\nby default, and regex metacharacters must be escaped.\n\n```java\n\"a,b,c\".split(\",\")         \u002F\u002F [\"a\", \"b\", \"c\"]\n\"a,b,,\".split(\",\")         \u002F\u002F [\"a\", \"b\"]  — trailing empties removed\n\"a,b,,\".split(\",\", -1)     \u002F\u002F [\"a\", \"b\", \"\", \"\"] — limit -1 keeps them\n\"a.b.c\".split(\".\")         \u002F\u002F [] — \".\" matches ANY char; needs \"\\\\.\"\n\"a.b.c\".split(\"\\\\.\")       \u002F\u002F [\"a\", \"b\", \"c\"]\n```\n\nPass a negative **limit** to keep trailing blanks, and escape `. | ( ) [ ] \\`\netc. For a plain delimiter use `Pattern.quote(delim)`.\n",{"id":77,"difficulty":24,"q":78,"a":79},"replace-vs-replaceall","What is the difference between replace and replaceAll?","**`replace`** works on **literal** text (a `char` or `CharSequence`) and\nreplaces every occurrence. **`replaceAll`** (and `replaceFirst`) take a\n**regex** as the pattern. Both return a new string — `String` is immutable.\n\n```java\n\"a.b.c\".replace(\".\", \"-\")      \u002F\u002F \"a-b-c\"  — literal dot\n\"a.b.c\".replaceAll(\".\", \"-\")   \u002F\u002F \"-----\"  — \".\" is regex \"any char\"!\n\"a.b.c\".replaceAll(\"\\\\.\", \"-\") \u002F\u002F \"a-b-c\"  — escaped dot\n\"a1b2\".replaceAll(\"\\\\d\", \"#\")  \u002F\u002F \"a#b#\"   — regex digit class\n```\n\nIf you don't need regex, prefer `replace` — it's faster and avoids the\n\"every char got replaced\" trap.\n",{"id":81,"difficulty":24,"q":82,"a":83},"trim-vs-strip","What is the difference between trim() and strip()?","Both remove leading and trailing whitespace, but **`trim`** (legacy) only\nstrips characters `\u003C= U+0020` (ASCII space and control chars), while\n**`strip`** (Java 11+) is **Unicode-aware** and removes all Unicode\nwhitespace. `strip` also has one-sided variants.\n\n```java\n\"  hi  \".trim()              \u002F\u002F \"hi\"\n\" hi \".trim()      \u002F\u002F \" hi \" — thin space NOT removed\n\" hi \".strip()     \u002F\u002F \"hi\"             — Unicode-aware\n\" hi \".stripLeading()        \u002F\u002F \"hi \"\n\" hi \".stripTrailing()       \u002F\u002F \" hi\"\n```\n\nPrefer **`strip`** on modern Java for correct handling of non-ASCII\nwhitespace; `trim` is fine for plain ASCII input.\n",{"id":85,"difficulty":14,"q":86,"a":87},"isblank-isempty","What is the difference between isEmpty() and isBlank()?","**`isEmpty()`** is true only when the length is `0`. **`isBlank()`** (Java\n11+) is true when the string is empty **or** contains only whitespace.\n\n```java\n\"\".isEmpty()      \u002F\u002F true\n\"   \".isEmpty()   \u002F\u002F false — it has characters\n\"   \".isBlank()   \u002F\u002F true  — only whitespace\n\"x\".isBlank()     \u002F\u002F false\n```\n\nNeither is null-safe, so check for `null` first (or use a helper like\n`StringUtils.isBlank`). Use `isBlank` when \"all-spaces\" should count as\n\"empty\" — e.g. validating user input.\n",{"id":89,"difficulty":37,"q":90,"a":91},"char-passwords","Why use char[] instead of String for passwords?","A `String` is **immutable** and may sit in the **pool**, so you cannot clear\nits contents — the password lingers in memory until garbage collected and\ncould surface in a heap dump. A **`char[]`** can be **explicitly wiped**\nright after use.\n\n```java\nchar[] pw = readPassword();\ntry {\n    authenticate(pw);\n} finally {\n    Arrays.fill(pw, '\\0'); \u002F\u002F overwrite — minimizes the exposure window\n}\n```\n\nThis is why `JPasswordField.getPassword()` and `Console.readPassword()`\nreturn `char[]`. Strings also risk accidental logging via `toString()`,\nwhich `char[]` avoids.\n",{"id":93,"difficulty":24,"q":94,"a":95},"string-conversions","How do you convert between String, char[] and byte[]?","Use `toCharArray()` \u002F the `String(char[])` constructor for characters, and\n`getBytes(charset)` \u002F `new String(bytes, charset)` for bytes. **Always\nspecify a charset** for byte conversions to avoid platform-default surprises.\n\n```java\nchar[] chars = \"hi\".toCharArray();        \u002F\u002F ['h','i']\nString fromChars = new String(chars);      \u002F\u002F \"hi\"\nString fromChars2 = String.valueOf(chars); \u002F\u002F \"hi\"\n\nbyte[] bytes = \"hi\".getBytes(StandardCharsets.UTF_8);\nString fromBytes = new String(bytes, StandardCharsets.UTF_8); \u002F\u002F \"hi\"\n```\n\nOmitting the charset uses the JVM default encoding, which differs across\nmachines and causes the classic \"works on my box\" mojibake bug — pass\n`StandardCharsets.UTF_8` explicitly.\n",{"id":97,"difficulty":14,"q":98,"a":99},"string-to-number","How do you convert between Strings and numbers?","Parse with the wrapper classes' static methods; produce text with\n`String.valueOf` or concatenation. `parseXxx` returns a **primitive**,\n`valueOf` a **wrapper object**.\n\n```java\nint n     = Integer.parseInt(\"42\");        \u002F\u002F primitive int\ndouble d  = Double.parseDouble(\"3.14\");\nInteger w = Integer.valueOf(\"42\");          \u002F\u002F Integer object\nString s  = String.valueOf(42);             \u002F\u002F \"42\"\nString s2 = \"\" + 42;                         \u002F\u002F \"42\" via concatenation\n```\n\nA malformed string throws **`NumberFormatException`**, so validate or\ntry\u002Fcatch when parsing untrusted input.\n",{"id":101,"difficulty":24,"q":102,"a":103},"format-and-join","How do String.format, String.join and repeat work?","- **`String.format`** builds a string from a printf-style template.\n- **`String.join`** glues elements with a delimiter (Java 8+).\n- **`repeat`** duplicates a string `n` times (Java 11+).\n\n```java\nString.format(\"%s is %d\", \"Ada\", 36);   \u002F\u002F \"Ada is 36\"\nString.format(\"%.2f\", 3.14159);          \u002F\u002F \"3.14\"\nString.join(\"-\", \"a\", \"b\", \"c\");          \u002F\u002F \"a-b-c\"\nString.join(\",\", List.of(\"x\", \"y\"));      \u002F\u002F \"x,y\"\n\"ab\".repeat(3);                            \u002F\u002F \"ababab\"\n```\n\n`String.join` is the clean way to build delimiter-separated output without a\ntrailing-comma loop; `format` is handy but slower than direct concatenation\nfor hot paths.\n",{"id":105,"difficulty":14,"q":106,"a":107},"text-blocks","What are text blocks in Java?","Text blocks (Java 15+) are multi-line string literals delimited by triple\nquotes `\"\"\"`. They preserve line breaks and let you write JSON, SQL, or HTML\nwithout escaping quotes or concatenating lines.\n\n```java\nString json = \"\"\"\n    {\n      \"name\": \"Ada\",\n      \"role\": \"engineer\"\n    }\n    \"\"\";\n```\n\nIncidental leading whitespace is stripped relative to the closing delimiter's\nindentation, and the result is an ordinary `String` — it's pooled and supports\nevery `String` method. Use `\\` at line end to suppress a newline.\n",{"id":109,"difficulty":24,"q":110,"a":111},"switch-on-string","How does switch on a String work under the hood?","Since Java 7 you can `switch` on a `String`. The compiler implements it in\ntwo steps: it switches on the string's **`hashCode()`** (an `int` switch),\nthen guards each matched bucket with an **`.equals()`** check to handle hash\ncollisions. So it is null-sensitive and case-sensitive.\n\n```java\nswitch (cmd) {\n    case \"start\" -> run();\n    case \"stop\"  -> halt();\n    default      -> usage();\n}\n\u002F\u002F a null cmd throws NullPointerException — switch calls cmd.hashCode()\n```\n\nBecause matching is by `equals`, the labels are exact and case-sensitive;\nnormalize with `toLowerCase()` first if you need case-insensitive matching.\n",{"id":113,"difficulty":37,"q":114,"a":115},"string-hashcode","How is a String's hashCode computed and why is it cached?","`String.hashCode()` uses the polynomial formula\n`s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]`, accumulated in an `int`.\nBecause the string is **immutable**, the result is computed **once** and\nstored in a `private int hash` field, then returned on every later call.\n\n```java\n\"Aa\".hashCode()  \u002F\u002F 2112\n\"BB\".hashCode()  \u002F\u002F 2112 — famous collision (same hash, not equal)\n```\n\nThe constant `31` is an odd prime that the JVM can optimize to\n`(h \u003C\u003C 5) - h`. Caching is safe precisely *because* the contents can never\nchange — one of the concrete payoffs of immutability and why `String` is the\ngo-to `HashMap` key.\n",{"id":117,"difficulty":24,"q":118,"a":119},"chars-stream","How do you iterate or process the characters of a String?","Classic options are an indexed loop with `charAt`, or `toCharArray()` with a\nfor-each. Java 8 added **`chars()`**, an `IntStream` of code units, for a\nfunctional style.\n\n```java\nString s = \"abc\";\nfor (int i = 0; i \u003C s.length(); i++) {   \u002F\u002F indexed\n    char c = s.charAt(i);\n}\nfor (char c : s.toCharArray()) { }        \u002F\u002F for-each over a copy\n\nlong vowels = s.chars()                    \u002F\u002F IntStream of code units\n    .filter(c -> \"aeiou\".indexOf(c) >= 0)\n    .count();\n```\n\n`chars()` yields `int` code *units*; for full Unicode (emoji \u002F supplementary\ncharacters) use `codePoints()` instead, which combines surrogate pairs.\n",24,null,{"description":11},"Java String interview questions — String immutability, the string pool and intern(), == vs equals, String vs StringBuilder vs StringBuffer, concatenation performance, and common String API methods.","java\u002Ffundamentals\u002Fstrings","Fundamentals","fundamentals","2026-06-20","JO-iZQ8XInhM-XwQLjKVpyi-5Ylro-3l0QziRdncVGs",[130,134,135,139],{"subtopic":131,"path":132,"order":133},"Data Types & Variables","\u002Fjava\u002Ffundamentals\u002Fdata-types-variables",1,{"subtopic":6,"path":20,"order":12},{"subtopic":136,"path":137,"order":138},"Arrays","\u002Fjava\u002Ffundamentals\u002Farrays",3,{"subtopic":140,"path":141,"order":142},"Keywords & Modifiers","\u002Fjava\u002Ffundamentals\u002Fkeywords-modifiers",4,{"path":144,"title":145},"\u002Fblog\u002Fjava-strings-immutability-stringbuilder","Java Strings — Immutability, the String Pool & StringBuilder Explained",1782244115173]