[{"data":1,"prerenderedAt":1742},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjava-strings-immutability-stringbuilder":3},{"id":4,"title":5,"body":6,"description":1728,"difficulty":1729,"extension":1730,"framework":1731,"frameworkSlug":79,"meta":1732,"navigation":298,"order":105,"path":1733,"qaPath":1734,"seo":1735,"stem":1736,"subtopic":1737,"topic":1738,"topicSlug":1739,"updated":1740,"__hash__":1741},"blog\u002Fblog\u002Fjava-strings-immutability-stringbuilder.md","Java Strings — Immutability, the String Pool & StringBuilder Explained",{"type":7,"value":8,"toc":1712},"minimark",[9,14,43,47,74,171,204,211,235,353,370,385,457,467,491,584,606,658,662,668,738,760,836,844,871,964,1011,1047,1051,1090,1180,1215,1316,1352,1438,1442,1455,1501,1512,1527,1602,1630,1634,1708],[10,11,13],"h2",{"id":12},"the-most-used-class-in-java","The most-used class in Java",[15,16,17,21,22,26,27,30,31,34,35,38,39,42],"p",{},[18,19,20],"code",{},"String"," is the first type most Java developers touch and the one they understand\nleast. It looks like a primitive, prints like text, and yet it's a full-blown object\nwith surprising behavior around equality, memory, and performance. Interviewers lean on\nstrings precisely because a clear answer reveals whether you understand ",[23,24,25],"strong",{},"object\nidentity",", ",[23,28,29],{},"immutability",", and ",[23,32,33],{},"the cost of allocation",". This guide walks through\nthe model: why strings are immutable, how the pool works, when ",[18,36,37],{},"=="," lies to you, and why\n",[18,40,41],{},"StringBuilder"," exists.",[10,44,46],{"id":45},"immutability-the-foundation","Immutability: the foundation",[15,48,49,50,52,53,56,57,26,60,26,63,66,67,70,71,73],{},"A ",[18,51,20],{}," wraps a ",[18,54,55],{},"private final"," array of characters that is never exposed or modified\nafter construction. Every \"modifying\" method — ",[18,58,59],{},"concat",[18,61,62],{},"toUpperCase",[18,64,65],{},"replace"," —\nreturns a ",[23,68,69],{},"brand-new"," ",[18,72,20],{}," and leaves the original untouched. This is the single\nfact that explains almost everything else about strings.",[75,76,81],"pre",{"className":77,"code":78,"language":79,"meta":80,"style":80},"language-java shiki shiki-themes github-light github-dark","String s = \"hello\";\ns.concat(\" world\");      \u002F\u002F returns a NEW String, immediately discarded here\nSystem.out.println(s);   \u002F\u002F \"hello\" — s itself never changed\ns = s.toUpperCase();     \u002F\u002F you must REASSIGN to keep the new String\nSystem.out.println(s);   \u002F\u002F \"HELLO\"\n","java","",[18,82,83,103,125,140,159],{"__ignoreMap":80},[84,85,88,92,96,100],"span",{"class":86,"line":87},"line",1,[84,89,91],{"class":90},"sVt8B","String s ",[84,93,95],{"class":94},"szBVR","=",[84,97,99],{"class":98},"sZZnC"," \"hello\"",[84,101,102],{"class":90},";\n",[84,104,106,109,112,115,118,121],{"class":86,"line":105},2,[84,107,108],{"class":90},"s.",[84,110,59],{"class":111},"sScJk",[84,113,114],{"class":90},"(",[84,116,117],{"class":98},"\" world\"",[84,119,120],{"class":90},");      ",[84,122,124],{"class":123},"sJ8bj","\u002F\u002F returns a NEW String, immediately discarded here\n",[84,126,128,131,134,137],{"class":86,"line":127},3,[84,129,130],{"class":90},"System.out.",[84,132,133],{"class":111},"println",[84,135,136],{"class":90},"(s);   ",[84,138,139],{"class":123},"\u002F\u002F \"hello\" — s itself never changed\n",[84,141,143,146,148,151,153,156],{"class":86,"line":142},4,[84,144,145],{"class":90},"s ",[84,147,95],{"class":94},[84,149,150],{"class":90}," s.",[84,152,62],{"class":111},[84,154,155],{"class":90},"();     ",[84,157,158],{"class":123},"\u002F\u002F you must REASSIGN to keep the new String\n",[84,160,162,164,166,168],{"class":86,"line":161},5,[84,163,130],{"class":90},[84,165,133],{"class":111},[84,167,136],{"class":90},[84,169,170],{"class":123},"\u002F\u002F \"HELLO\"\n",[15,172,173,174,177,178,181,182,185,186,189,190,192,193,196,197,200,201,203],{},"Immutability buys four things worth naming in an interview: ",[23,175,176],{},"security"," (a validated path\nor username can't be mutated behind your back), ",[23,179,180],{},"safe sharing\u002Fcaching"," (identical text\ncan be pooled), ",[23,183,184],{},"thread safety"," (no synchronization needed for a value that never\nchanges), and ",[23,187,188],{},"hashcode caching"," (the hash is computed once and can never go stale,\nmaking ",[18,191,20],{}," the ideal ",[18,194,195],{},"HashMap"," key). The price is ",[23,198,199],{},"garbage",": heavy string building\nchurns out throwaway objects — which is exactly the problem ",[18,202,41],{}," solves.",[10,205,207,208],{"id":206},"the-string-pool-and-literals-vs-new-string","The string pool and literals vs ",[18,209,210],{},"new String()",[15,212,213,214,217,218,222,223,226,227,230,231,234],{},"The JVM keeps a ",[23,215,216],{},"string constant pool"," (the ",[219,220,221],"em",{},"intern pool",") holding ",[23,224,225],{},"one shared copy","\nof each distinct string ",[23,228,229],{},"literal",". Two identical literals point at the same pooled\nobject, saving memory. A string created with ",[18,232,233],{},"new String(\"x\")",", by contrast, forces a\nfresh heap object — defeating the pool for no benefit.",[75,236,238],{"className":77,"code":237,"language":79,"meta":80,"style":80},"String a = \"x\";              \u002F\u002F pooled object\nString b = \"x\";              \u002F\u002F reuses the SAME pooled object\nString c = new String(\"x\");  \u002F\u002F a distinct heap object (\"x\" is also in the pool)\n\nSystem.out.println(a == b);  \u002F\u002F true  — same pooled reference\nSystem.out.println(a == c);  \u002F\u002F false — c is a different object\nSystem.out.println(a.equals(c)); \u002F\u002F true — same characters\n",[18,239,240,256,270,294,300,317,334],{"__ignoreMap":80},[84,241,242,245,247,250,253],{"class":86,"line":87},[84,243,244],{"class":90},"String a ",[84,246,95],{"class":94},[84,248,249],{"class":98}," \"x\"",[84,251,252],{"class":90},";              ",[84,254,255],{"class":123},"\u002F\u002F pooled object\n",[84,257,258,261,263,265,267],{"class":86,"line":105},[84,259,260],{"class":90},"String b ",[84,262,95],{"class":94},[84,264,249],{"class":98},[84,266,252],{"class":90},[84,268,269],{"class":123},"\u002F\u002F reuses the SAME pooled object\n",[84,271,272,275,277,280,283,285,288,291],{"class":86,"line":127},[84,273,274],{"class":90},"String c ",[84,276,95],{"class":94},[84,278,279],{"class":94}," new",[84,281,282],{"class":111}," String",[84,284,114],{"class":90},[84,286,287],{"class":98},"\"x\"",[84,289,290],{"class":90},");  ",[84,292,293],{"class":123},"\u002F\u002F a distinct heap object (\"x\" is also in the pool)\n",[84,295,296],{"class":86,"line":142},[84,297,299],{"emptyLinePlaceholder":298},true,"\n",[84,301,302,304,306,309,311,314],{"class":86,"line":161},[84,303,130],{"class":90},[84,305,133],{"class":111},[84,307,308],{"class":90},"(a ",[84,310,37],{"class":94},[84,312,313],{"class":90}," b);  ",[84,315,316],{"class":123},"\u002F\u002F true  — same pooled reference\n",[84,318,320,322,324,326,328,331],{"class":86,"line":319},6,[84,321,130],{"class":90},[84,323,133],{"class":111},[84,325,308],{"class":90},[84,327,37],{"class":94},[84,329,330],{"class":90}," c);  ",[84,332,333],{"class":123},"\u002F\u002F false — c is a different object\n",[84,335,337,339,341,344,347,350],{"class":86,"line":336},7,[84,338,130],{"class":90},[84,340,133],{"class":111},[84,342,343],{"class":90},"(a.",[84,345,346],{"class":111},"equals",[84,348,349],{"class":90},"(c)); ",[84,351,352],{"class":123},"\u002F\u002F true — same characters\n",[15,354,355,356,358,359,362,363,369],{},"Immutability is what makes pooling safe: since no one can mutate ",[18,357,287],{},", sharing it\neverywhere is harmless. Since Java 7 the pool lives on the ",[23,360,361],{},"heap"," (not PermGen), so it's\nsubject to ordinary garbage collection. The practical rule: ",[23,364,365,366],{},"never write\n",[18,367,368],{},"new String(\"literal\")"," — it's pure waste.",[15,371,372,373,376,377,380,381,384],{},"When you have a ",[219,374,375],{},"computed"," string and genuinely want the pooled instance, ",[18,378,379],{},"intern()","\nreturns the canonical copy: if an equal string is already pooled you get that reference,\notherwise the string is added and that reference returned. Use it sparingly — interning\nmillions of unique strings just bloats the pool. It earns its keep only when deduplicating\na ",[23,382,383],{},"bounded"," set of repeated values.",[75,386,388],{"className":77,"code":387,"language":79,"meta":80,"style":80},"String a = \"hi\";\nString c = new String(\"hi\");\nSystem.out.println(a == c);          \u002F\u002F false\nSystem.out.println(a == c.intern()); \u002F\u002F true — intern() hands back the pooled \"hi\"\n",[18,389,390,401,419,435],{"__ignoreMap":80},[84,391,392,394,396,399],{"class":86,"line":87},[84,393,244],{"class":90},[84,395,95],{"class":94},[84,397,398],{"class":98}," \"hi\"",[84,400,102],{"class":90},[84,402,403,405,407,409,411,413,416],{"class":86,"line":105},[84,404,274],{"class":90},[84,406,95],{"class":94},[84,408,279],{"class":94},[84,410,282],{"class":111},[84,412,114],{"class":90},[84,414,415],{"class":98},"\"hi\"",[84,417,418],{"class":90},");\n",[84,420,421,423,425,427,429,432],{"class":86,"line":127},[84,422,130],{"class":90},[84,424,133],{"class":111},[84,426,308],{"class":90},[84,428,37],{"class":94},[84,430,431],{"class":90}," c);          ",[84,433,434],{"class":123},"\u002F\u002F false\n",[84,436,437,439,441,443,445,448,451,454],{"class":86,"line":142},[84,438,130],{"class":90},[84,440,133],{"class":111},[84,442,308],{"class":90},[84,444,37],{"class":94},[84,446,447],{"class":90}," c.",[84,449,450],{"class":111},"intern",[84,452,453],{"class":90},"()); ",[84,455,456],{"class":123},"\u002F\u002F true — intern() hands back the pooled \"hi\"\n",[10,458,460,462,463,466],{"id":459},"vs-equals-the-classic-bug",[18,461,37],{}," vs ",[18,464,465],{},".equals()"," — the classic bug",[15,468,469,471,472,475,476,479,480,471,482,485,486,102,488,490],{},[18,470,37],{}," compares ",[23,473,474],{},"references"," (do both variables point to the ",[219,477,478],{},"same"," object?), while\n",[18,481,465],{},[23,483,484],{},"characters",". For strings you almost always want ",[18,487,465],{},[18,489,37],{}," only ever \"works\" by accident when both operands happen to be the same pooled literal.",[75,492,494],{"className":77,"code":493,"language":79,"meta":80,"style":80},"String a = \"hi\";\nString b = new String(\"hi\");\nSystem.out.println(a == b);      \u002F\u002F false — different objects\nSystem.out.println(a.equals(b)); \u002F\u002F true  — same characters\n\nString c = \"hi\";\nSystem.out.println(a == c);      \u002F\u002F true — both the pooled literal (coincidence!)\n",[18,495,496,506,522,538,554,558,568],{"__ignoreMap":80},[84,497,498,500,502,504],{"class":86,"line":87},[84,499,244],{"class":90},[84,501,95],{"class":94},[84,503,398],{"class":98},[84,505,102],{"class":90},[84,507,508,510,512,514,516,518,520],{"class":86,"line":105},[84,509,260],{"class":90},[84,511,95],{"class":94},[84,513,279],{"class":94},[84,515,282],{"class":111},[84,517,114],{"class":90},[84,519,415],{"class":98},[84,521,418],{"class":90},[84,523,524,526,528,530,532,535],{"class":86,"line":127},[84,525,130],{"class":90},[84,527,133],{"class":111},[84,529,308],{"class":90},[84,531,37],{"class":94},[84,533,534],{"class":90}," b);      ",[84,536,537],{"class":123},"\u002F\u002F false — different objects\n",[84,539,540,542,544,546,548,551],{"class":86,"line":142},[84,541,130],{"class":90},[84,543,133],{"class":111},[84,545,343],{"class":90},[84,547,346],{"class":111},[84,549,550],{"class":90},"(b)); ",[84,552,553],{"class":123},"\u002F\u002F true  — same characters\n",[84,555,556],{"class":86,"line":161},[84,557,299],{"emptyLinePlaceholder":298},[84,559,560,562,564,566],{"class":86,"line":319},[84,561,274],{"class":90},[84,563,95],{"class":94},[84,565,398],{"class":98},[84,567,102],{"class":90},[84,569,570,572,574,576,578,581],{"class":86,"line":336},[84,571,130],{"class":90},[84,573,133],{"class":111},[84,575,308],{"class":90},[84,577,37],{"class":94},[84,579,580],{"class":90}," c);      ",[84,582,583],{"class":123},"\u002F\u002F true — both the pooled literal (coincidence!)\n",[15,585,586,587,589,590,593,594,597,598,601,602,605],{},"This is the most common Java beginner bug, and the fix has a twist: calling ",[18,588,465],{}," on\na ",[18,591,592],{},"null"," reference throws ",[18,595,596],{},"NullPointerException",". The idiomatic guard is the ",[23,599,600],{},"Yoda\ncondition"," — put the known constant on the left — or reach for ",[18,603,604],{},"Objects.equals"," when\neither side might be null.",[75,607,609],{"className":77,"code":608,"language":79,"meta":80,"style":80},"String input = maybeNull();\n\"yes\".equals(input);          \u002F\u002F safe — returns false when input is null\nObjects.equals(input, \"yes\"); \u002F\u002F safe both ways\n",[18,610,611,624,640],{"__ignoreMap":80},[84,612,613,616,618,621],{"class":86,"line":87},[84,614,615],{"class":90},"String input ",[84,617,95],{"class":94},[84,619,620],{"class":111}," maybeNull",[84,622,623],{"class":90},"();\n",[84,625,626,629,632,634,637],{"class":86,"line":105},[84,627,628],{"class":98},"\"yes\"",[84,630,631],{"class":90},".",[84,633,346],{"class":111},[84,635,636],{"class":90},"(input);          ",[84,638,639],{"class":123},"\u002F\u002F safe — returns false when input is null\n",[84,641,642,645,647,650,652,655],{"class":86,"line":127},[84,643,644],{"class":90},"Objects.",[84,646,346],{"class":111},[84,648,649],{"class":90},"(input, ",[84,651,628],{"class":98},[84,653,654],{"class":90},"); ",[84,656,657],{"class":123},"\u002F\u002F safe both ways\n",[10,659,661],{"id":660},"string-vs-stringbuilder-vs-stringbuffer","String vs StringBuilder vs StringBuffer",[15,663,664,665,667],{},"Because ",[18,666,20],{}," is immutable, building text by repeated concatenation is wasteful. The\nmutable companions exist for exactly this:",[669,670,671,690],"table",{},[672,673,674],"thead",{},[675,676,677,681,684,687],"tr",{},[678,679,680],"th",{},"Type",[678,682,683],{},"Mutable?",[678,685,686],{},"Thread-safe?",[678,688,689],{},"Use when",[691,692,693,709,723],"tbody",{},[675,694,695,700,703,706],{},[696,697,698],"td",{},[18,699,20],{},[696,701,702],{},"no",[696,704,705],{},"yes (immutable)",[696,707,708],{},"fixed text, map keys",[675,710,711,715,718,720],{},[696,712,713],{},[18,714,41],{},[696,716,717],{},"yes",[696,719,702],{},[696,721,722],{},"building strings (the default)",[675,724,725,730,732,735],{},[696,726,727],{},[18,728,729],{},"StringBuffer",[696,731,717],{},[696,733,734],{},"yes (synchronized)",[696,736,737],{},"shared across threads (rare)",[15,739,740,742,743,745,746,749,750,752,753,756,757,759],{},[18,741,41],{}," and ",[18,744,729],{}," share an ",[23,747,748],{},"identical API"," — the only difference is that\n",[18,751,729],{},"'s methods are ",[18,754,755],{},"synchronized",", which makes it slower. Since shared mutable\nstring state across threads is uncommon (and usually better handled with a local builder\nper thread), ",[18,758,41],{}," is the right default.",[75,761,763],{"className":77,"code":762,"language":79,"meta":80,"style":80},"StringBuilder sb = new StringBuilder();\nsb.append(\"a\").append(1).append(true); \u002F\u002F fluent, mutates one buffer in place\nString result = sb.toString();         \u002F\u002F \"a1true\"\n",[18,764,765,779,817],{"__ignoreMap":80},[84,766,767,770,772,774,777],{"class":86,"line":87},[84,768,769],{"class":90},"StringBuilder sb ",[84,771,95],{"class":94},[84,773,279],{"class":94},[84,775,776],{"class":111}," StringBuilder",[84,778,623],{"class":90},[84,780,781,784,787,789,792,795,797,799,803,805,807,809,812,814],{"class":86,"line":105},[84,782,783],{"class":90},"sb.",[84,785,786],{"class":111},"append",[84,788,114],{"class":90},[84,790,791],{"class":98},"\"a\"",[84,793,794],{"class":90},").",[84,796,786],{"class":111},[84,798,114],{"class":90},[84,800,802],{"class":801},"sj4cs","1",[84,804,794],{"class":90},[84,806,786],{"class":111},[84,808,114],{"class":90},[84,810,811],{"class":801},"true",[84,813,654],{"class":90},[84,815,816],{"class":123},"\u002F\u002F fluent, mutates one buffer in place\n",[84,818,819,822,824,827,830,833],{"class":86,"line":127},[84,820,821],{"class":90},"String result ",[84,823,95],{"class":94},[84,825,826],{"class":90}," sb.",[84,828,829],{"class":111},"toString",[84,831,832],{"class":90},"();         ",[84,834,835],{"class":123},"\u002F\u002F \"a1true\"\n",[10,837,839,840,843],{"id":838},"concatenation-performance-and-how-compiles","Concatenation performance and how ",[18,841,842],{},"+"," compiles",[15,845,846,847,850,851,854,856,857,860,861,864,865,867,868,631],{},"Here's where immutability bites. Writing ",[18,848,849],{},"s += part"," inside a loop allocates a ",[23,852,853],{},"new",[18,855,20],{}," and copies all existing characters on ",[23,858,859],{},"every iteration"," — overall ",[23,862,863],{},"O(n²)"," work\nplus a mountain of garbage. A single ",[18,866,41],{}," reuses one growing buffer for\namortized ",[23,869,870],{},"O(n)",[75,872,874],{"className":77,"code":873,"language":79,"meta":80,"style":80},"\u002F\u002F O(n²) — a new String allocated and copied 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",[18,875,876,881,893,913,917,922,934,950],{"__ignoreMap":80},[84,877,878],{"class":86,"line":87},[84,879,880],{"class":123},"\u002F\u002F O(n²) — a new String allocated and copied each iteration\n",[84,882,883,886,888,891],{"class":86,"line":105},[84,884,885],{"class":90},"String r ",[84,887,95],{"class":94},[84,889,890],{"class":98}," \"\"",[84,892,102],{"class":90},[84,894,895,898,901,904,907,910],{"class":86,"line":127},[84,896,897],{"class":94},"for",[84,899,900],{"class":90}," (String p ",[84,902,903],{"class":94},":",[84,905,906],{"class":90}," parts) r ",[84,908,909],{"class":94},"+=",[84,911,912],{"class":90}," p;\n",[84,914,915],{"class":86,"line":142},[84,916,299],{"emptyLinePlaceholder":298},[84,918,919],{"class":86,"line":161},[84,920,921],{"class":123},"\u002F\u002F O(n) — one reused buffer\n",[84,923,924,926,928,930,932],{"class":86,"line":319},[84,925,769],{"class":90},[84,927,95],{"class":94},[84,929,279],{"class":94},[84,931,776],{"class":111},[84,933,623],{"class":90},[84,935,936,938,940,942,945,947],{"class":86,"line":336},[84,937,897],{"class":94},[84,939,900],{"class":90},[84,941,903],{"class":94},[84,943,944],{"class":90}," parts) sb.",[84,946,786],{"class":111},[84,948,949],{"class":90},"(p);\n",[84,951,953,956,958,960,962],{"class":86,"line":952},8,[84,954,955],{"class":90},"String r2 ",[84,957,95],{"class":94},[84,959,826],{"class":90},[84,961,829],{"class":111},[84,963,623],{"class":90},[15,965,966,967,70,970,973,974,977,978,981,982,984,985,988,989,992,993,996,997,1000,1001,1003,1004,1007,1008,1010],{},"The subtlety interviewers probe: a ",[23,968,969],{},"single",[18,971,972],{},"a + b + c"," expression is ",[219,975,976],{},"not"," a problem.\nHistorically ",[18,979,980],{},"javac"," rewrote it into a ",[18,983,41],{}," chain; since ",[23,986,987],{},"Java 9"," it emits an\n",[18,990,991],{},"invokedynamic"," call bound to ",[18,994,995],{},"StringConcatFactory"," (\"indified\" string concatenation),\nletting the JVM choose the optimal strategy at runtime. Either way the compiler can only\noptimize one expression — it ",[23,998,999],{},"cannot"," collapse a ",[18,1002,909],{}," accumulation ",[23,1005,1006],{},"across loop\niterations",". That loop is the only case where you must reach for ",[18,1009,41],{}," yourself.",[75,1012,1014],{"className":77,"code":1013,"language":79,"meta":80,"style":80},"String 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",[18,1015,1016,1037,1042],{"__ignoreMap":80},[84,1017,1018,1020,1022,1024,1027,1030,1032,1035],{"class":86,"line":87},[84,1019,91],{"class":90},[84,1021,95],{"class":94},[84,1023,249],{"class":98},[84,1025,1026],{"class":94}," +",[84,1028,1029],{"class":90}," n ",[84,1031,842],{"class":94},[84,1033,1034],{"class":98}," \"y\"",[84,1036,102],{"class":90},[84,1038,1039],{"class":86,"line":105},[84,1040,1041],{"class":123},"\u002F\u002F pre-9 : new StringBuilder().append(\"x\").append(n).append(\"y\").toString()\n",[84,1043,1044],{"class":86,"line":127},[84,1045,1046],{"class":123},"\u002F\u002F 9+    : invokedynamic -> StringConcatFactory.makeConcatWithConstants(...)\n",[10,1048,1050],{"id":1049},"the-string-api-youll-actually-use","The String API you'll actually use",[15,1052,1053,1054,1057,1058,1061,1062,1065,1066,1069,1070,1073,1074,1077,1078,1081,1082,1085,1086,1089],{},"Most day-to-day string work is a handful of methods, each with a gotcha worth knowing.\n",[23,1055,1056],{},"Searching",": ",[18,1059,1060],{},"charAt(i)"," throws out of range, ",[18,1063,1064],{},"indexOf"," returns ",[23,1067,1068],{},"−1"," (not an\nexception) when nothing matches, and ",[18,1071,1072],{},"contains"," returns a ",[18,1075,1076],{},"boolean",". ",[23,1079,1080],{},"Slicing",":\n",[18,1083,1084],{},"substring(begin, end)"," is ",[23,1087,1088],{},"end-exclusive",", and since Java 7u6 it copies the chars into a\nnew array, so there's no longer a memory-retention trap.",[75,1091,1093],{"className":77,"code":1092,"language":79,"meta":80,"style":80},"String s = \"interview\";\ns.charAt(1);          \u002F\u002F 'n'\ns.indexOf('e');       \u002F\u002F 3   (-1 if absent — guard with if (i >= 0))\ns.substring(0, 5);    \u002F\u002F \"inter\" — chars 0..4, end is exclusive\ns.contains(\"view\");   \u002F\u002F true\n",[18,1094,1095,1106,1123,1140,1163],{"__ignoreMap":80},[84,1096,1097,1099,1101,1104],{"class":86,"line":87},[84,1098,91],{"class":90},[84,1100,95],{"class":94},[84,1102,1103],{"class":98}," \"interview\"",[84,1105,102],{"class":90},[84,1107,1108,1110,1113,1115,1117,1120],{"class":86,"line":105},[84,1109,108],{"class":90},[84,1111,1112],{"class":111},"charAt",[84,1114,114],{"class":90},[84,1116,802],{"class":801},[84,1118,1119],{"class":90},");          ",[84,1121,1122],{"class":123},"\u002F\u002F 'n'\n",[84,1124,1125,1127,1129,1131,1134,1137],{"class":86,"line":127},[84,1126,108],{"class":90},[84,1128,1064],{"class":111},[84,1130,114],{"class":90},[84,1132,1133],{"class":98},"'e'",[84,1135,1136],{"class":90},");       ",[84,1138,1139],{"class":123},"\u002F\u002F 3   (-1 if absent — guard with if (i >= 0))\n",[84,1141,1142,1144,1147,1149,1152,1154,1157,1160],{"class":86,"line":142},[84,1143,108],{"class":90},[84,1145,1146],{"class":111},"substring",[84,1148,114],{"class":90},[84,1150,1151],{"class":801},"0",[84,1153,26],{"class":90},[84,1155,1156],{"class":801},"5",[84,1158,1159],{"class":90},");    ",[84,1161,1162],{"class":123},"\u002F\u002F \"inter\" — chars 0..4, end is exclusive\n",[84,1164,1165,1167,1169,1171,1174,1177],{"class":86,"line":161},[84,1166,108],{"class":90},[84,1168,1072],{"class":111},[84,1170,114],{"class":90},[84,1172,1173],{"class":98},"\"view\"",[84,1175,1176],{"class":90},");   ",[84,1178,1179],{"class":123},"\u002F\u002F true\n",[15,1181,1182,1183,1186,1187,742,1190,1193,1194,1197,1198,1200,1201,1204,1205,1207,1208,1211,1212,1214],{},"The biggest trap lives in the ",[23,1184,1185],{},"regex-based"," methods. ",[18,1188,1189],{},"split",[18,1191,1192],{},"replaceAll"," take a\n",[23,1195,1196],{},"regular expression",", not literal text, so an unescaped ",[18,1199,631],{}," matches ",[219,1202,1203],{},"any"," character.\nUse ",[18,1206,65],{}," (literal) when you don't need regex, and pass a negative ",[18,1209,1210],{},"limit"," to ",[18,1213,1189],{},"\nto keep trailing empties.",[75,1216,1218],{"className":77,"code":1217,"language":79,"meta":80,"style":80},"\"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\".split(\"\\\\.\");           \u002F\u002F [\"a\",\"b\",\"c\"] — escaped dot\n\"a,b,,\".split(\",\", -1);          \u002F\u002F [\"a\",\"b\",\"\",\"\"] — limit -1 keeps trailing blanks\n",[18,1219,1220,1244,1265,1290],{"__ignoreMap":80},[84,1221,1222,1225,1227,1229,1231,1234,1236,1239,1241],{"class":86,"line":87},[84,1223,1224],{"class":98},"\"a.b.c\"",[84,1226,631],{"class":90},[84,1228,65],{"class":111},[84,1230,114],{"class":90},[84,1232,1233],{"class":98},"\".\"",[84,1235,26],{"class":90},[84,1237,1238],{"class":98},"\"-\"",[84,1240,120],{"class":90},[84,1242,1243],{"class":123},"\u002F\u002F \"a-b-c\"  — literal dot\n",[84,1245,1246,1248,1250,1252,1254,1256,1258,1260,1262],{"class":86,"line":105},[84,1247,1224],{"class":98},[84,1249,631],{"class":90},[84,1251,1192],{"class":111},[84,1253,114],{"class":90},[84,1255,1233],{"class":98},[84,1257,26],{"class":90},[84,1259,1238],{"class":98},[84,1261,1176],{"class":90},[84,1263,1264],{"class":123},"\u002F\u002F \"-----\"  — \".\" is regex \"any char\"!\n",[84,1266,1267,1269,1271,1273,1275,1278,1281,1284,1287],{"class":86,"line":127},[84,1268,1224],{"class":98},[84,1270,631],{"class":90},[84,1272,1189],{"class":111},[84,1274,114],{"class":90},[84,1276,1277],{"class":98},"\"",[84,1279,1280],{"class":801},"\\\\",[84,1282,1283],{"class":98},".\"",[84,1285,1286],{"class":90},");           ",[84,1288,1289],{"class":123},"\u002F\u002F [\"a\",\"b\",\"c\"] — escaped dot\n",[84,1291,1292,1295,1297,1299,1301,1304,1306,1309,1311,1313],{"class":86,"line":142},[84,1293,1294],{"class":98},"\"a,b,,\"",[84,1296,631],{"class":90},[84,1298,1189],{"class":111},[84,1300,114],{"class":90},[84,1302,1303],{"class":98},"\",\"",[84,1305,26],{"class":90},[84,1307,1308],{"class":94},"-",[84,1310,802],{"class":801},[84,1312,1119],{"class":90},[84,1314,1315],{"class":123},"\u002F\u002F [\"a\",\"b\",\"\",\"\"] — limit -1 keeps trailing blanks\n",[15,1317,1318,1319,1324,1325,1330,1331,1334,1335,1340,1341,26,1344,1347,1348,1351],{},"For trimming, prefer ",[23,1320,1321],{},[18,1322,1323],{},"strip"," (Java 11+, Unicode-aware) over the legacy ",[23,1326,1327],{},[18,1328,1329],{},"trim",", which\nonly removes characters ",[18,1332,1333],{},"\u003C= U+0020",". Use ",[23,1336,1337],{},[18,1338,1339],{},"isBlank"," when an all-whitespace string should\ncount as empty, and lean on the builder helpers — ",[18,1342,1343],{},"String.join",[18,1345,1346],{},"String.format",", and\n",[18,1349,1350],{},"repeat"," — instead of hand-rolled delimiter loops.",[75,1353,1355],{"className":77,"code":1354,"language":79,"meta":80,"style":80},"\" hi \".strip();                 \u002F\u002F \"hi\" — handles non-ASCII whitespace\n\"   \".isBlank();                \u002F\u002F true (isEmpty() would be false)\nString.join(\"-\", \"a\", \"b\", \"c\"); \u002F\u002F \"a-b-c\"\n\"ab\".repeat(3);                  \u002F\u002F \"ababab\"\n",[18,1356,1357,1372,1387,1418],{"__ignoreMap":80},[84,1358,1359,1362,1364,1366,1369],{"class":86,"line":87},[84,1360,1361],{"class":98},"\" hi \"",[84,1363,631],{"class":90},[84,1365,1323],{"class":111},[84,1367,1368],{"class":90},"();                 ",[84,1370,1371],{"class":123},"\u002F\u002F \"hi\" — handles non-ASCII whitespace\n",[84,1373,1374,1377,1379,1381,1384],{"class":86,"line":105},[84,1375,1376],{"class":98},"\"   \"",[84,1378,631],{"class":90},[84,1380,1339],{"class":111},[84,1382,1383],{"class":90},"();                ",[84,1385,1386],{"class":123},"\u002F\u002F true (isEmpty() would be false)\n",[84,1388,1389,1392,1395,1397,1399,1401,1403,1405,1408,1410,1413,1415],{"class":86,"line":127},[84,1390,1391],{"class":90},"String.",[84,1393,1394],{"class":111},"join",[84,1396,114],{"class":90},[84,1398,1238],{"class":98},[84,1400,26],{"class":90},[84,1402,791],{"class":98},[84,1404,26],{"class":90},[84,1406,1407],{"class":98},"\"b\"",[84,1409,26],{"class":90},[84,1411,1412],{"class":98},"\"c\"",[84,1414,654],{"class":90},[84,1416,1417],{"class":123},"\u002F\u002F \"a-b-c\"\n",[84,1419,1420,1423,1425,1427,1429,1432,1435],{"class":86,"line":142},[84,1421,1422],{"class":98},"\"ab\"",[84,1424,631],{"class":90},[84,1426,1350],{"class":111},[84,1428,114],{"class":90},[84,1430,1431],{"class":801},"3",[84,1433,1434],{"class":90},");                  ",[84,1436,1437],{"class":123},"\u002F\u002F \"ababab\"\n",[10,1439,1441],{"id":1440},"text-blocks-for-multi-line-literals","Text blocks for multi-line literals",[15,1443,1444,1445,1448,1449,1454],{},"Text blocks (Java 15+) are multi-line string literals delimited by triple quotes ",[18,1446,1447],{},"\"\"\"",".\nThey preserve line breaks and let you embed JSON, SQL, or HTML without escaping quotes or\nconcatenating lines. Incidental leading whitespace is stripped relative to the closing\ndelimiter's indentation, and the result is an ",[23,1450,1451,1452],{},"ordinary ",[18,1453,20],{}," — pooled, with the full\nAPI available.",[75,1456,1458],{"className":77,"code":1457,"language":79,"meta":80,"style":80},"String json = \"\"\"\n    {\n      \"name\": \"Ada\",\n      \"role\": \"engineer\"\n    }\n    \"\"\";                  \u002F\u002F a normal String; use \\ at line end to suppress a newline\n",[18,1459,1460,1470,1475,1480,1485,1490],{"__ignoreMap":80},[84,1461,1462,1465,1467],{"class":86,"line":87},[84,1463,1464],{"class":90},"String json ",[84,1466,95],{"class":94},[84,1468,1469],{"class":98}," \"\"\"\n",[84,1471,1472],{"class":86,"line":105},[84,1473,1474],{"class":98},"    {\n",[84,1476,1477],{"class":86,"line":127},[84,1478,1479],{"class":98},"      \"name\": \"Ada\",\n",[84,1481,1482],{"class":86,"line":142},[84,1483,1484],{"class":98},"      \"role\": \"engineer\"\n",[84,1486,1487],{"class":86,"line":161},[84,1488,1489],{"class":98},"    }\n",[84,1491,1492,1495,1498],{"class":86,"line":319},[84,1493,1494],{"class":98},"    \"\"\"",[84,1496,1497],{"class":90},";                  ",[84,1499,1500],{"class":123},"\u002F\u002F a normal String; use \\ at line end to suppress a newline\n",[10,1502,1504,1505,1508,1509,1511],{"id":1503},"why-char-beats-string-for-passwords","Why ",[18,1506,1507],{},"char[]"," beats ",[18,1510,20],{}," for passwords",[15,1513,1514,1515,1517,1518,1521,1522,1526],{},"Immutability has a security downside for secrets. A ",[18,1516,20],{}," can't be cleared — it may sit\nin the pool and lingers in memory until garbage collected, where it could surface in a heap\ndump or get logged accidentally via ",[18,1519,1520],{},"toString()",". A ",[23,1523,1524],{},[18,1525,1507],{}," can be explicitly\noverwritten the instant you're done with it.",[75,1528,1530],{"className":77,"code":1529,"language":79,"meta":80,"style":80},"char[] pw = readPassword();\ntry {\n    authenticate(pw);\n} finally {\n    Arrays.fill(pw, '\\0'); \u002F\u002F wipe it — minimizes the exposure window\n}\n",[18,1531,1532,1547,1555,1563,1573,1597],{"__ignoreMap":80},[84,1533,1534,1537,1540,1542,1545],{"class":86,"line":87},[84,1535,1536],{"class":94},"char",[84,1538,1539],{"class":90},"[] pw ",[84,1541,95],{"class":94},[84,1543,1544],{"class":111}," readPassword",[84,1546,623],{"class":90},[84,1548,1549,1552],{"class":86,"line":105},[84,1550,1551],{"class":94},"try",[84,1553,1554],{"class":90}," {\n",[84,1556,1557,1560],{"class":86,"line":127},[84,1558,1559],{"class":111},"    authenticate",[84,1561,1562],{"class":90},"(pw);\n",[84,1564,1565,1568,1571],{"class":86,"line":142},[84,1566,1567],{"class":90},"} ",[84,1569,1570],{"class":94},"finally",[84,1572,1554],{"class":90},[84,1574,1575,1578,1581,1584,1587,1590,1592,1594],{"class":86,"line":161},[84,1576,1577],{"class":90},"    Arrays.",[84,1579,1580],{"class":111},"fill",[84,1582,1583],{"class":90},"(pw, ",[84,1585,1586],{"class":98},"'",[84,1588,1589],{"class":801},"\\0",[84,1591,1586],{"class":98},[84,1593,654],{"class":90},[84,1595,1596],{"class":123},"\u002F\u002F wipe it — minimizes the exposure window\n",[84,1598,1599],{"class":86,"line":319},[84,1600,1601],{"class":90},"}\n",[15,1603,1604,1605,742,1608,1611,1612,1614,1615,1617,1618,1621,1622,1625,1626,1629],{},"This is precisely why ",[18,1606,1607],{},"Console.readPassword()",[18,1609,1610],{},"JPasswordField.getPassword()"," return\n",[18,1613,1507],{}," rather than ",[18,1616,20],{},". The same charset-awareness applies to byte conversions:\nalways pass an explicit charset (",[18,1619,1620],{},"StandardCharsets.UTF_8",") to ",[18,1623,1624],{},"getBytes"," and the\n",[18,1627,1628],{},"String(byte[], charset)"," constructor, or you'll hit the classic \"works on my machine\"\nmojibake bug from the platform default encoding.",[10,1631,1633],{"id":1632},"recap","Recap",[15,1635,1636,1638,1639,1641,1642,1644,1645,1648,1649,1655,1656,1659,1660,1662,1663,1665,1666,1669,1670,1674,1675,1677,1678,1680,1681,1683,1684,1686,1687,1689,1690,1692,1693,1508,1695,1697,1698,1701,1702,1704,1705,1707],{},[18,1637,20],{}," is immutable: its backing ",[18,1640,1536],{}," array is ",[18,1643,55],{},", and every \"modifying\"\nmethod returns a new object. That one fact powers the ",[23,1646,1647],{},"string pool"," (shared literals,\nmade safe by immutability), justifies the ",[23,1650,1651,462,1653],{},[18,1652,37],{},[18,1654,465],{}," distinction (compare\n",[219,1657,1658],{},"content",", guard against ",[18,1661,592],{}," with the Yoda trick or ",[18,1664,604],{},"), and explains why\n",[23,1667,1668],{},"concatenation in a loop"," is O(n²) garbage that ",[23,1671,1672],{},[18,1673,41],{}," turns into O(n).\nReach for ",[18,1676,41],{}," by default and ",[18,1679,729],{}," only when truly sharing across\nthreads. Know the API gotchas — ",[18,1682,1189],{},"\u002F",[18,1685,1192],{}," take regex, ",[18,1688,1146],{}," is\nend-exclusive, ",[18,1691,1064],{}," returns −1, ",[18,1694,1323],{},[18,1696,1329],{}," — use ",[23,1699,1700],{},"text blocks"," for\nmulti-line literals, and remember ",[18,1703,1507],{}," over ",[18,1706,20],{}," for passwords. Understand\nimmutability and the rest of the String story falls into place.",[1709,1710,1711],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":80,"searchDepth":105,"depth":105,"links":1713},[1714,1715,1716,1718,1720,1721,1723,1724,1725,1727],{"id":12,"depth":105,"text":13},{"id":45,"depth":105,"text":46},{"id":206,"depth":105,"text":1717},"The string pool and literals vs new String()",{"id":459,"depth":105,"text":1719},"== vs .equals() — the classic bug",{"id":660,"depth":105,"text":661},{"id":838,"depth":105,"text":1722},"Concatenation performance and how + compiles",{"id":1049,"depth":105,"text":1050},{"id":1440,"depth":105,"text":1441},{"id":1503,"depth":105,"text":1726},"Why char[] beats String for passwords",{"id":1632,"depth":105,"text":1633},"How Java Strings really work — why they're immutable, the string pool and intern(), == vs equals, String vs StringBuilder vs StringBuffer, concatenation performance, key API methods, text blocks, and char[] for passwords.","easy","md","Java",{},"\u002Fblog\u002Fjava-strings-immutability-stringbuilder","\u002Fjava\u002Ffundamentals\u002Fstrings",{"title":5,"description":1728},"blog\u002Fjava-strings-immutability-stringbuilder","Strings","Fundamentals","fundamentals","2026-06-20","PyD5HLvPFhLbook0DBAzmlQKFBcKOU68XfMLsSJBeVM",1782244089917]