String immutability, the string pool and intern, StringBuilder, compact strings, concatenation, text blocks, Unicode and charsets, formatting, hashing, security, regex engine behaviour, ReDoS and tricky output questions.
Theory
Q1
Why is String immutable in Java?
basic
A String cannot change after creation because its value array is private and final and no method mutates it. Immutability makes strings safe to share across threads, safe as map keys, cacheable (hash code, pool) and safe to pass to security-sensitive APIs.
Thread safety without locking.
hashCode is computed once and cached in the hash field.
Class loading, file paths, URLs and DB credentials cannot be altered after validation (no TOCTOU change).
Enables the string pool and substring/concat optimizations.
⚠ Follow-up traps
Is final String s the same as an immutable String? No. final makes the reference non-reassignable; immutability is a property of the class.
Can reflection change a String? Historically yes by editing value; since Java 9 modules and strong encapsulation (enforced in 17) block it for java.lang by default.
#immutability#design
Q2
What is the string pool (string intern pool)?
basic
The string pool is a JVM-wide table of canonical String instances. String literals and compile-time constant expressions are interned, so identical literals share one object.
Since Java 7 the pool lives on the heap (it was in PermGen in Java 6), so pooled strings are garbage collectable.
The pool is a native hash table; default size is 65536 buckets in modern JDKs (-XX:StringTableSize).
new String("a") creates a separate heap object that is not the pooled instance.
⚠ Follow-up traps
Where is the pool in Java 8+? In the heap, not the metaspace.
Are pooled strings ever collected? Yes, once unreachable, since Java 7.
#string-pool#memory
Q3
What does String.intern() do?
intermediate
intern() returns the canonical pooled instance equal to this string, adding this string to the pool if no equal one exists.
Useful to dedupe millions of repeated values (country codes, tags) when parsed from external input.
Cost: a pool lookup that is slower than a HashMap of your own for large volumes, and contention on a shared table.
Prefer an application-level Map<String,String> or Interner (Guava) for controlled dedup, or G1's -XX:+UseStringDeduplication for the backing arrays.
⚠ Follow-up traps
Does intern() modify the original object? No, it returns a possibly different reference.
Does interning save memory with G1 deduplication already on? Dedup only shares byte[] values, not String objects, so they are complementary.
#intern#string-pool
Q4
What is the difference between == and equals() for strings?
basic
== compares references; equals compares character content. Two strings with the same content can be different objects, so use equals for logic.
equals first checks identity, then instanceof String, then coder (LATIN1/UTF16) and bytes.
Use equalsIgnoreCase for case-insensitive comparison and "lit".equals(x) to avoid NPE.
Objects.equals(a, b) is null-safe on both sides.
⚠ Follow-up traps
Why does "a" == "a" return true? Both literals resolve to the same pooled instance.
Why can == appear to work in tests but fail in production? Test data are literals; runtime data (input, DB, concatenation) are new objects.
#equals#identity
Q5
How many objects are created by new String("abc")?
basic
Up to two: the literal "abc" (pooled, created once when the class resolves it if not already present) and a new heap String from the constructor.
If "abc" is already in the pool, only one new object is created at this statement.
The copy constructor shares the internal byte array in modern JDKs, so the extra cost is the String header, not the data.
The constructor is almost never needed; it only matters to detach from a large parent string in old JDKs.
⚠ Follow-up traps
Is new String("abc") == "abc"? False.
Is new String("abc").intern() == "abc"? True.
#string-pool#new
Q6
Compare String, StringBuilder and StringBuffer.
basic
String is immutable; StringBuilder is a mutable, unsynchronized buffer; StringBuffer is the same API with synchronized methods.
StringBuilder is the default choice for single-threaded building.
StringBuffer (since 1.0) pays locking cost; even then compound operations across calls are not atomic.
Both share AbstractStringBuilder, doubling capacity (old * 2 + 2) when full.
Is StringBuffer useful for sharing between threads? Rarely; individual calls are safe but sequences like check-then-append are not.
Does sb.equals(other) compare content? No, use toString().equals or compareTo/contentEquals.
#stringbuilder#stringbuffer#thread-safety
Q7
How does StringBuilder grow, and how do you avoid reallocation?
intermediate
The default capacity is 16. When an append would exceed capacity, the new capacity becomes max(required, old * 2 + 2) and the array is copied.
Pass an expected size: new StringBuilder(expectedLength).
setLength(0) reuses the buffer; trimToSize() releases excess.
new StringBuilder(String s) allocates s.length() + 16.
⚠ Follow-up traps
Does new StringBuilder(10) limit length to 10? No, it is only the initial capacity.
Does setLength(0) free memory? No, capacity remains.
#stringbuilder#capacity#performance
Q8
What are compact strings (Java 9+)?
intermediate
Since Java 9 (JEP 254) a String stores a byte[] plus a coder flag: LATIN1 (1 byte per char) when all chars are <= 0xFF, otherwise UTF16 (2 bytes per char). Java 8 always used char[] (2 bytes).
Typical heaps shrink 10-25% because most strings are Latin-1.
A single non-Latin-1 character makes the entire string UTF16.
-XX:-CompactStrings disables it.
length() and charAt() remain O(1); charAt branches on coder.
⚠ Follow-up traps
Is the internal encoding UTF-8? No, only Latin-1 or UTF-16.
Does "héllo" use one byte per char? Yes, é is U+00E9, within Latin-1.
#compact-strings#memory#jep-254
Q9
How does string concatenation with + work, and what changed in Java 9?
intermediate
Up to Java 8, javac compiled a + b into new StringBuilder().append(a).append(b).toString(). Since Java 9 (JEP 280) it emits an invokedynamic call to StringConcatFactory, which picks an optimal strategy at runtime.
The bootstrap computes the exact final length and allocates once, avoiding intermediate buffers.
Compile-time constants ("a" + "b", final locals) are folded by javac and become one pooled literal.
Concatenation in a loop still creates a new string per iteration, so use StringBuilder there.
⚠ Follow-up traps
Is + in a loop fine after Java 9? Each iteration still copies the whole accumulated string, giving O(n^2).
Can code compiled for 8 benefit from the new strategy? Only if recompiled with -release 9 or newer.
#concatenation#invokedynamic#jep-280
Q10
When does the compiler fold string concatenation at compile time?
intermediate
When every operand is a compile-time constant expression: literals, and final variables initialized with constants. The result is interned like a literal.
final String a = "ja";String b = "ja";System.out.println(("ja" + "va") == "java"); // trueSystem.out.println((a + "va") == "java"); // trueSystem.out.println((b + "va") == "java"); // false
A final field or local assigned from a method call is not a constant.
⚠ Follow-up traps
Does making b effectively final change the result? No; constant variables need the explicit final modifier and constant initializer.
Is (b + "va").intern() == "java"? True.
#constants#concatenation#string-pool
Q11
Can you use String in a switch statement, and how is it implemented?
basic
Yes since Java 7. javac switches on hashCode() first, then confirms with equals inside each hash bucket, and finally switches on an int index.
A null selector throws NullPointerException (unless case null in Java 21 pattern switch).
Case labels must be constant expressions and unique.
Hash collisions such as "Aa" and "BB" are handled by the equals check.
⚠ Follow-up traps
Is the switch case-insensitive? No, normalize first with toLowerCase(Locale.ROOT).
What if two case labels collide on hashCode? Legal; the generated code disambiguates with equals.
#switch#hashcode
Q12
What is a switch expression on strings and how does it differ from the old switch?
basic
Java 14+ switch expressions use -> arms that yield a value, never fall through and must be exhaustive (a default is required for strings).
int days = switch (month) { case "feb" -> 28; case "apr", "jun", "sep", "nov" -> 30; default -> 31;};
⚠ Follow-up traps
Does -> need break? No, there is no fall-through.
Can one arm list several labels? Yes, comma separated.
#switch#java14
Q13
What are the commonly used String methods added in Java 11 and later?
basic
Java 11 added isBlank(), strip(), stripLeading(), stripTrailing(), lines() and repeat(int). Java 12 added indent/transform; Java 15 added formatted and stripIndent/translateEscapes.
"x=%d".formatted(5) is String.format as an instance method.
⚠ Follow-up traps
Is isBlank() the same as isEmpty()? No, " ".isEmpty() is false while " ".isBlank() is true.
Does lines() return a trailing empty element for a trailing newline? No.
#api#java11
Q14
How do strip() and trim() differ?
intermediate
trim() removes characters with code <= U+0020 (space, tab, control chars). strip() removes characters for which Character.isWhitespace(int) is true, including Unicode spaces such as U+2003 (em space).
Neither removes the non-breaking space U+00A0, because isWhitespace excludes it.
Both return the same instance if nothing needs stripping.
⚠ Follow-up traps
Does strip() remove ? No. Use a regex like \p{Z} or replace(' ', ' ').
Does trim() remove ? No.
#strip#trim#unicode
Q15
What are text blocks and what are their rules?
basic
Text blocks (standard in Java 15) are multi-line literals delimited by """. The opening delimiter must be followed by a line terminator.
String json = """ { "name": "Ann" } """;
Incidental indentation is removed using the least-indented line (including the closing delimiter line).
Trailing spaces on each line are stripped; line endings are normalized to \n.
\ at line end suppresses the newline; \s keeps a trailing space.
Quotes need no escaping except a sequence of three.
⚠ Follow-up traps
Does a text block end with a newline? Yes if the closing """ is on its own line; put it right after the last text to avoid it.
Is a text block a different type? No, it is a normal String (pooled like a literal).
#text-blocks#java15
Q16
What is the difference between char, code unit, code point and grapheme?
intermediate
A Java char is a 16-bit UTF-16 code unit. A code point is a Unicode scalar value (0 to 0x10FFFF); those above 0xFFFF need two chars (a surrogate pair). A grapheme is what users perceive as one character and may be several code points.
User-perceived characters (flag emoji, "e" plus combining accent) need BreakIterator or regex \X (Java 9+).
⚠ Follow-up traps
Does new StringBuilder("😀").reverse() break it? No, reverse treats surrogate pairs correctly; naive char reversal would not.
Is charAt(0) of an emoji a valid character? It is a lone high surrogate.
#unicode#code-points
Q17
What is the difference between Unicode and UTF-8, UTF-16, ISO-8859-1?
basic
Unicode is the catalogue of code points; UTF-8, UTF-16 and ISO-8859-1 are encodings turning them into bytes.
UTF-8: 1-4 bytes, ASCII compatible, no endianness issue; the web default.
UTF-16: 2 or 4 bytes per code point; Java's in-memory form.
ISO-8859-1: 1 byte, covers U+0000-U+00FF only; unmappable characters become ?.
Always pass a charset explicitly: getBytes(StandardCharsets.UTF_8).
⚠ Follow-up traps
What does "é".getBytes(UTF_8).length return? 2.
Why is String(byte[]) risky? It uses the platform default charset (before Java 18).
#charset#utf-8#unicode
Q18
What changed about the default charset in Java 18?
intermediate
JEP 400 makes UTF-8 the default charset for the JVM (Charset.defaultCharset()) regardless of OS locale. Before 18 it came from file.encoding/locale (e.g. Cp1252 on Windows).
System.out encoding and console use stdout.encoding, which can still differ.
-Dfile.encoding=COMPAT restores the old behaviour.
Best practice stays: specify the charset explicitly in Files, InputStreamReader, getBytes.
⚠ Follow-up traps
Does Java 18 make new String(bytes) safe? For default charset, yes UTF-8, but explicit is still clearer.
Do Files.readString/writeString default to UTF-8? Yes, since Java 11, independent of the default.
#charset#jep-400
Q19
What happens when bytes are decoded with the wrong charset?
A mismatch throws IllegalFormatException subclasses at runtime (MissingFormatArgumentException, IllegalFormatConversionException).
⚠ Follow-up traps
Is String.format locale-dependent? Yes, it uses Locale.getDefault(FORMAT); pass Locale.ROOT/US for machine-readable output.
Is %n the same as \n?%n is the platform line separator.
#format#formatter
Q21
Why is String.format slow and when should you avoid it?
intermediate
Every call parses the format string with a regex-free but nontrivial parser, creates a Formatter, boxes primitives and allocates a StringBuilder. It is several times slower than concatenation or StringBuilder.
Avoid in hot paths and in log statements; use parameterized logging (log.debug("x={}", x)).
Fine for infrequent messages and reports.
"x".formatted(...) has the same cost, only a different syntax.
⚠ Follow-up traps
Is log.debug(String.format(...)) lazy? No, the string is built even if debug is disabled.
Does the JIT remove the cost? Not the allocation and parsing.
#format#performance
Q22
Why should passwords be stored in char[] rather than String?
intermediate
A String is immutable and stays in memory (and possibly pooled or in heap dumps) until GC; you cannot wipe it. A char[] can be overwritten with Arrays.fill(chars, '\0') immediately after use.
Console.readPassword() and Swing JPasswordField.getPassword() return char[].
It does not protect against everything: heap dumps taken earlier, GC copying, and frameworks that convert to String (HTTP params, JSON) defeat it.
Prefer hashed verification (bcrypt, Argon2) so the plaintext lives briefly.
⚠ Follow-up traps
Does zeroing a char[] guarantee no copy remains? No; the GC may have copied it and swap/core dumps can hold it.
Does toString() on a char[] print contents? No, it prints [C@hash.
#security#password#char-array
Q23
How is String.hashCode computed?
basic
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] in int arithmetic with overflow, over the chars (code units). The result is cached in the hash field (with a hashIsZero flag since Java 13 so zero hashes are also cached).
31 is an odd prime and 31 * i == (i << 5) - i, cheap for the JIT.
The algorithm is specified, so it is stable across JVMs and runs, unlike Object.hashCode.
Collisions are easy to build: "Aa" and "BB" both give 2112.
⚠ Follow-up traps
What is "".hashCode()? 0.
Can attackers exploit collisions? Yes (hash flooding); HashMap mitigates by treeifying buckets of Comparable keys.
#hashcode#internals
Q24
What are the gotchas of String.split?
intermediate
split(regex) takes a regular expression, not a literal, and drops trailing empty strings by default.
"a.b".split(".") returns an empty array because . matches every char; use "\\." or Pattern.quote(".").
"a,b,,".split(",") gives [a, b]; use split(",", -1) to keep trailing empties.
A leading empty string is kept when there is a positive-width match at index 0: ",a".split(",") is ["", "a"]; a zero-width match at the start never produces one (Java 8+).
"".split(",") returns [""] (length 1), while " ".split(" ") returns an empty array (length 0).
split has a fast path (no regex) for single non-metachar and escaped single char.
⚠ Follow-up traps
What does "a|b".split("|") return?[a, |, b] because empty regex alternation matches between every char.
How to split on a literal pipe?split("\\|").
#split#regex
Q25
What does the limit argument of split do?
intermediate
With limit n > 0 the array has at most n elements and the last holds the unsplit remainder; n == 0 drops trailing empties and is the default; n < 0 keeps all, including trailing empties.
Is limit = 1 useful? It returns the whole string as the only element.
Does limit apply to the Scanner/StringTokenizer? No; StringTokenizer is legacy and ignores regex.
#split#limit
Q26
What is the difference between replace, replaceAll and replaceFirst?
basic
replace(CharSequence, CharSequence) and replace(char, char) are literal and replace all occurrences. replaceAll and replaceFirst take a regex and treat $ and \ in the replacement specially.
Replacement with $1 refers to group 1; use Matcher.quoteReplacement for literal text.
replaceAll compiles a new Pattern each call, so precompile for hot loops.
⚠ Follow-up traps
Does replace use regex internally? No, since Java 9 it uses a plain indexOf-based loop.
What happens with "cost".replaceAll("o", "$")?IllegalArgumentException: Illegal group reference.
#replace#regex
Q27
What are the main classes of java.util.regex?
basic
Pattern is the compiled, immutable, thread-safe regex. Matcher is a stateful engine for one input and is not thread-safe. PatternSyntaxException reports bad syntax; MatchResult is a snapshot of a match.
Pattern.compile(regex, flags) once, reuse; p.matcher(input) per use.
matches() requires the full region to match; find() searches for the next match; lookingAt() anchors at the start only.
Pattern.matches(regex, s) and String.matches recompile every time.
⚠ Follow-up traps
Is Matcher thread-safe? No; share the Pattern, not the Matcher.
Does matches() need ^ and $? No, it already matches the entire input.
#regex#pattern#matcher
Q28
How do capturing groups, named groups and backreferences work?
intermediate
Parentheses capture. Groups are numbered by their opening parenthesis from 1; group 0 is the whole match. Named groups use (?<name>...), backreferences use \1 or \k<name>.
Group names must be alphanumeric and start with a letter (no underscores).
Replacement refs: $1 or ${name}.
⚠ Follow-up traps
What does group(1) return for an optional group that did not participate?null.
Is group() before find()/matches() allowed? No, IllegalStateException.
#regex#groups
Q29
What is the difference between greedy, lazy and possessive quantifiers?
intermediate
Greedy (*, +, ?, {n,m}) takes as much as possible then backtracks. Lazy (*?) takes as little as possible and expands. Possessive (*+) takes as much as possible and never gives back.
On <a><b>: <.*> matches the entire string; <.*?> matches <a>; <.*+> fails because it consumes > and cannot backtrack.
Possessive quantifiers and atomic groups (?>...) prevent catastrophic backtracking.
⚠ Follow-up traps
Does lazy mean faster? No, it just changes match choice; it can backtrack heavily too.
Does . match a newline? Not unless DOTALL ((?s)).
#regex#quantifiers
Q30
Explain lookahead and lookbehind.
intermediate
Lookarounds are zero-width assertions: (?=x) positive lookahead, (?!x) negative lookahead, (?<=x) positive lookbehind, (?<!x) negative lookbehind. They check context without consuming characters.
Java lookbehind needs a bounded maximum length: (?<=ab{1,3}) is fine, while unbounded (?<=a+) is rejected or unreliable, so avoid * and + inside it.
⚠ Follow-up traps
Does a lookahead appear in group(0)? No, it consumes nothing.
Can lookahead contain capturing groups? Yes, and they are retained after a positive lookahead.
#regex#lookahead#lookbehind
Q31
What are the common Pattern flags?
basic
CASE_INSENSITIVE ((?i), ASCII only unless UNICODE_CASE), MULTILINE ((?m), ^/$ per line), DOTALL ((?s)), COMMENTS ((?x)), UNICODE_CASE ((?u)), LITERAL, UNICODE_CHARACTER_CLASS ((?U), makes \w, \d, \b Unicode aware).
By default \w is [a-zA-Z_0-9] and \d is [0-9], so é does not match \w.
Use \p{L} or \p{IsAlphabetic} for letters in any script.
Flags combine with |.
⚠ Follow-up traps
Does (?i) match ß and SS? No; no full case folding in Java regex.
Does $ match before a final newline? Yes by default $ matches at the end and before a final line terminator.
#regex#flags
Q32
Why do you need double backslashes in Java regex strings?
basic
Backslash is an escape in Java string literals and again in the regex syntax. \d in regex must be written "\\d"; a literal backslash in regex is "\\\\".
Text blocks do not remove this need (the escape is still processed) but are readable with \\d.
Pattern.quote(s) wraps text in \Q...\E to match it literally.
Matcher.quoteReplacement is for the replacement side.
⚠ Follow-up traps
How to match a literal dot?"\\." or "[.]".
Is "\d" valid Java? No, compile error: illegal escape character.
#regex#escaping
Q33
What is catastrophic backtracking and ReDoS?
advanced
Java's regex engine is a backtracking NFA. A pattern with nested or overlapping quantifiers can take exponential time on a crafted non-matching input; used on user data that is a Regular Expression Denial of Service.
Classic: (a+)+$ on "aaaaaaaaaaaaaaaaaaaaaaaa!". Others: (a|aa)+, (.*a){20}, ^(\w+\s?)*$.
Mitigations: avoid nested quantifiers, make alternatives disjoint, use possessive quantifiers or atomic groups, bound input length, avoid user-supplied patterns.
Java 9+ added optimizations (e.g. memoization for some loops in 9+; more in later releases) but they are not a guarantee.
Use a timeout wrapper (a CharSequence that checks a deadline) or a linear-time engine such as RE2/J for untrusted patterns.
⚠ Follow-up traps
Does Pattern.compile itself risk ReDoS? Compilation is safe; matching is the problem.
Does matches() fail faster than find()? Not necessarily; both backtrack.
#regex#redos#security
Q34
How do you add a timeout to a regex match in Java?
advanced
java.util.regex has no built-in timeout. Wrap the input in a CharSequence whose charAt checks a deadline and throws.
class Deadline implements CharSequence { private final CharSequence s; private final long end; Deadline(CharSequence s, long endNanos) { this.s = s; this.end = endNanos; } public char charAt(int i) { if (System.nanoTime() > end) throw new IllegalStateException("regex timeout"); return s.charAt(i); } public int length() { return s.length(); } public CharSequence subSequence(int a, int b) { return new Deadline(s.subSequence(a, b), end); } public String toString() { return s.toString(); }}
Simpler alternatives: run in an executor with Future.get(timeout) (the thread keeps running though), or limit input length.
⚠ Follow-up traps
Can Future.cancel(true) stop a regex? No; the engine does not check interruption.
Why is the thread still burning CPU after a timeout? Because only the wait stops, not the matcher.
#regex#timeout#redos
Q35
How does Matcher.find, appendReplacement and results() work?
intermediate
find() advances through successive matches; start(), end(), group() describe the current one. results() (Java 9) returns a Stream<MatchResult>; replaceAll(Function<MatchResult,String>) (Java 9) computes replacements.
Pattern p = Pattern.compile("\\d+");String out = p.matcher("a1 b22").replaceAll(r -> "<" + r.group() + ">");System.out.println(out); // a<1> b<22>
appendReplacement/appendTail with StringBuilder overloads exist since Java 9.
reset() reuses the matcher on new input.
⚠ Follow-up traps
Does find() restart from the beginning on each call? No, it continues after the previous match unless reset().
What does an empty match do to find() loops? The engine advances one char, so the loop terminates.
#regex#matcher#streams
Q36
How can you use regex predicates and streams from Pattern?
intermediate
Pattern.asPredicate() (find semantics) and asMatchPredicate() (Java 11, full match) produce predicates; splitAsStream splits lazily.
Pattern p = Pattern.compile("^[A-Z]{2}\\d+$");List<String> ok = ids.stream().filter(p.asMatchPredicate()).toList();
asPredicate uses find(), so it succeeds on partial matches.
⚠ Follow-up traps
Which predicate equals String.matches?asMatchPredicate.
Is the predicate thread-safe? Yes; each test creates its own Matcher.
#regex#streams
Q37
What are the performance considerations of using strings heavily?
intermediate
Strings dominate heap in most services. Reduce allocation, copying and retention.
Precompile regexes; avoid split/replaceAll/matches in loops.
Presize StringBuilder; avoid += in loops.
Avoid substring of large inputs kept long-term in Java 6 (shared array); since 7u6 substring copies, so the parent can be freed.
Prefer char-based indexOf/startsWith over regex for simple checks.
Consider G1 string deduplication and compact strings for memory.
Avoid intern() on unbounded data.
⚠ Follow-up traps
Does substring share the array in Java 8? No, it copies since 7u6.
Is String.join faster than a loop with +? Yes, it computes size once and builds one result.
#performance#gc
Q38
How does String.join and Collectors.joining work?
basic
String.join(delimiter, elements) (Java 8) concatenates an Iterable<CharSequence> or varargs. Collectors.joining(delim, prefix, suffix) does the same for streams, using a StringJoiner.
StringJoiner supports setEmptyValue.
null elements print as "null".
Joining does not escape the delimiter in the elements.
⚠ Follow-up traps
Does String.join(",", (List<String>) null) throw? Yes, NPE.
Is a null element skipped? No, it appears as null.
#join#streams
Q39
How do String comparison and sorting work (compareTo, Collator)?
intermediate
compareTo compares UTF-16 code units lexicographically; the result is the difference of the first differing chars or of the lengths. It is not locale-aware, so uppercase sorts before lowercase and accented letters sort after z.
Use java.text.Collator.getInstance(locale) for human-correct ordering.
Sort with Comparator.comparing(String::toLowerCase) only for simple cases.
⚠ Follow-up traps
Is the sign of compareTo guaranteed to be -1/0/1? No, only the sign matters.
Does "Z".compareTo("a") return negative? Yes (90 - 97 = -7).
#compareto#collator#locale
Q40
Why does toUpperCase/toLowerCase need a Locale?
intermediate
Case mapping is locale-sensitive. In Turkish, "I".toLowerCase() becomes dotless ı, breaking comparisons such as "TITLE".toLowerCase().equals("title").
Use toLowerCase(Locale.ROOT) or Locale.ENGLISH for protocol keywords, identifiers and map keys.
Length can change: "ß".toUpperCase() is "SS".
equalsIgnoreCase is locale-independent (per char), preferred for simple comparisons.
⚠ Follow-up traps
Does toUpperCase().length() always equal the original? No (ß to SS).
Is equalsIgnoreCase correct for all Unicode? It compares upper then lower per char, not full case folding.
#locale#case
Q41
What is the difference between String, CharSequence and Character-based APIs?
basic
CharSequence is the read-only interface (length, charAt, subSequence, toString, chars, isEmpty since 15) implemented by String, StringBuilder, StringBuffer, CharBuffer.
Accept CharSequence in utility methods to avoid forced toString() copies.
Regex Pattern.matcher(CharSequence) works on any of them.
Mutable implementations must not be used as map keys.
⚠ Follow-up traps
Does "a".contentEquals(sb) work? Yes, it compares to any CharSequence.
Is "a".equals(sb) true for the same content? No, equals requires a String.
#charsequence#api
Q42
How do you convert between String, char[] and byte[] correctly?
basic
toCharArray() copies the characters; new String(chars) copies them back; bytes require a charset.
s.getBytes(StandardCharsets.UTF_8) and new String(bytes, StandardCharsets.UTF_8).
String.valueOf(char[]) equals new String(char[]); String.valueOf((Object) null) gives "null".
s.chars() yields an IntStream of UTF-16 units; s.codePoints() yields code points.
⚠ Follow-up traps
Does toCharArray expose the internal array? No, it returns a copy.
Does String.valueOf(null) compile? It chooses the char[] overload and throws NPE.
#conversion#charset
Q43
What is Base64 and how is it used with strings in Java?
basic
java.util.Base64 (Java 8) converts bytes to ASCII text; it is an encoding, not encryption. Variants: basic, URL-safe (- and _, no + and /) and MIME.
String enc = Base64.getEncoder().encodeToString("hi".getBytes(StandardCharsets.UTF_8));byte[] raw = Base64.getDecoder().decode(enc);
Output is about 33% larger; padding = can be dropped with withoutPadding().
Always go through bytes and an explicit charset for text.
⚠ Follow-up traps
Is Base64 secure for passwords? No, it is trivially reversible.
Which variant belongs in a URL or JWT? URL-safe without padding.
#base64#encoding
Q44
How do StringBuilder operations like insert, deleteCharAt and reverse behave?
basic
append is amortized O(1); insert and delete shift the tail, O(n); reverse is in-place and surrogate-pair aware; setCharAt replaces one unit.
sb.append(char) vs sb.append(int): append('a' + 1) appends the number 98 because the expression is int.
sb.insert(0, x) repeatedly is O(n^2); use ArrayDeque or build in reverse.
Methods return this, enabling chaining.
⚠ Follow-up traps
Does sb.append(null) throw? For a String-typed null it appends "null"; append((char[]) null) throws NPE.
What does sb.append('a' + 'b') append?195.
#stringbuilder#api
Q45
What are StringTokenizer and Scanner and when are they appropriate?
basic
StringTokenizer is a legacy class (no regex, skips empty tokens, discouraged by its own Javadoc). Scanner parses typed tokens from a source using regex delimiters, slower but convenient.
Prefer String.split or Pattern.splitAsStream for splitting.
Scanner default delimiter is whitespace; nextInt followed by nextLine leaves the newline unread.
For large inputs use BufferedReader and manual parsing.
⚠ Follow-up traps
Why does nextLine() return empty after nextInt()? It reads the rest of the current line.
Is Scanner thread-safe? No.
#tokenizer#scanner
Q46
How do you safely compare secrets and why does equals leak information?
advanced
String.equals returns at the first mismatch, so response time can reveal how many leading characters are correct. Use MessageDigest.isEqual(a, b) (constant-time since Java 6u17) on byte arrays.
Compare hashes or HMACs rather than raw secrets.
Do not log secrets; override toString of credential objects to mask.
char[] plus Arrays.fill for short lifetime; avoid String for keys.
⚠ Follow-up traps
Is timing attack realistic over a network? Yes with enough samples, especially on a LAN or co-located hosts.
Does Arrays.equals run in constant time? No.
#security#timing
Q47
How do you prevent injection when building strings (SQL, logs, shell)?
intermediate
Never concatenate untrusted input into an interpreter's language. Use parameters (PreparedStatement), allow-lists, and context-specific encoders.
SQL: ? placeholders; identifiers cannot be bound, so allow-list them.
Logs: strip or encode CR/LF to avoid log forging; parameterized logging does not sanitize newlines.
Shell: pass argument arrays to ProcessBuilder, never a joined command string.
Regex: Pattern.quote for user text used inside a pattern.
⚠ Follow-up traps
Does String.format into a SQL string make it safe? No, it is still concatenation.
Are prepared statements safe for ORDER BY columns? Not if you concatenate the column name.
#security#injection
Q48
How are strings handled in Java serialization, JSON and databases with respect to encoding (utf8mb4, surrogate pairs)?
advanced
Strings are UTF-16 in memory but must be encoded at every boundary. A mismatch between layers corrupts data.
MySQL utf8 is only 3 bytes; use utf8mb4 for emoji (4 bytes, a surrogate pair in Java).
Size limits (VARCHAR(255)) count chars in the DB but length() counts UTF-16 units; emoji count as 2 in Java and 1 in the DB.
HTTP: set Content-Type: ...; charset=UTF-8; Spring defaults to UTF-8 for JSON.
Lone surrogates cannot be encoded in UTF-8 and become ?/U+FFFD.
⚠ Follow-up traps
Why does an emoji fail to insert into MySQL utf8? 4-byte code point exceeds the 3-byte limit.
Is length() <= 255 a safe check against a VARCHAR(255)? Not exactly; count code points or bytes per column semantics.
#unicode#database#encoding
Q49
What does String.valueOf, Integer.toString and + "" do differently?
basic
All turn values into strings. String.valueOf(Object) returns "null" for a null reference; obj.toString() throws NPE; "" + obj also yields "null".
Integer.toString(i) and String.valueOf(i) are equivalent; "" + i compiles to concat machinery.
Objects.toString(o, "default") supplies a fallback.
For char[], String.valueOf(char[]) copies content.
⚠ Follow-up traps
What does "" + null give?"null".
What does String.valueOf(new char[]{'a'}) vs String.valueOf((Object) new char[]{'a'}) give?"a" and [C@....
#conversion#null
Q50
What are string deduplication and the memory footprint of a String?
advanced
A String object has a 12-16 byte header plus value reference, hash, coder, hashIsZero fields (about 24 bytes), plus a separate byte[] (16-byte header plus data). A 10-char Latin-1 string takes about 24 + 32 bytes.
G1/Shenandoah/ZGC support -XX:+UseStringDeduplication, which merges identical byte[] of live strings in the background.
Dedup helps with many repeated long values; it does not help short-lived strings.
Measure with a heap histogram (jcmd GC.class_histogram) before tuning.
⚠ Follow-up traps
Does dedup replace the String object? No, only the backing array.
Is it on by default? No.
#memory#g1#deduplication
Q51
How do records, enums and Optional interplay with string representation (toString, valueOf)?
basic
Records generate toString as Name[field=value, ...]. Enum name() is the identifier and toString() defaults to it; Enum.valueOf is case-sensitive and throws IllegalArgumentException for unknown names.
Do not use toString() for persistence or protocols; its format is not a contract.
Optional.toString() gives Optional[x] or Optional.empty.
Override toString in enums for display only; keep name() for storage.
⚠ Follow-up traps
Does Enum.valueOf(null) throw NPE? Yes (NullPointerException: Name is null).
Should you persist ordinal() or name()?name(), ordinals shift when constants are reordered.
#tostring#enum#record
Q52
How do string literals and the constant pool relate to class files?
advanced
Each class file has a constant pool of CONSTANT_String entries pointing to UTF-8 (modified) entries. On first resolution of an ldc instruction the JVM interns that string and caches the reference in the runtime constant pool.
Literal length in a class file is limited to 65535 bytes of modified UTF-8, so very long literals do not compile.
Modified UTF-8 encodes U+0000 as two bytes and supplementary characters as surrogate pairs of 3 bytes each.
This is why two classes using "x" get the same instance.
⚠ Follow-up traps
Is a literal interned at compile time or at runtime? At class resolution/first ldc, by the JVM.
What happens with a literal larger than 64 KB?javac error: constant string too long; use a resource file.
#constant-pool#jvm
Scenarios
Q53
What does this print?
basic
Prints a. Both calls return new strings that are discarded; s still references the original.
Fix: s = s.concat("b").toUpperCase();.
IDEs flag this as "result of method call ignored".
⚠ Follow-up traps
Would a StringBuilder behave the same? No, sb.append("b") mutates it.
Does final String s change the output? No.
#immutability#output
Q54
What are the results of these comparisons?
basic
true, false, true, true. "hel" + "lo" is a compile-time constant folded into the pooled "hello"; new String always allocates; intern() returns the pooled instance.
⚠ Follow-up traps
What if s2 is built from a final String variable set to a literal? Still true, it is a constant variable.
What if built from a non-final variable?false.
#string-pool#output
Q55
What is the output?
basic
false then true. a is not a constant variable, so the concatenation happens at runtime and creates a new object; intern() yields the pooled literal.
⚠ Follow-up traps
Does the result differ on Java 8 vs 17? No, only the mechanism of concatenation differs.
Is b.equals("hello") true? Yes.
#string-pool#concatenation#output
Q56
Predict the output of mixed + operations.
basic
195c, cab, 33, 123. + is evaluated left to right: two chars (or ints) add numerically until a String operand appears.
⚠ Follow-up traps
Why 195?'a' is 97 and 'b' is 98.
How to force string concatenation first? Start with "" or use parentheses.
#concatenation#output
Q57
What does this char arithmetic print?
basic
98, b, b, c. c + 1 promotes to int; compound assignment and ++ include an implicit narrowing cast.
⚠ Follow-up traps
Does c = c + 1; compile? No, possible lossy conversion from int to char.
Does final char c = 'a'; char d = c + 1; compile? Yes, constant expression that fits in char.
#char#output
Q58
What does this StringBuilder constructor call print?
intermediate
Prints BC and capacity 65. The char'A' widens to int 65 and selects the StringBuilder(int capacity) constructor, so the builder starts empty with capacity 65 and the A never appears.
Use new StringBuilder("A") or String.valueOf('A').
⚠ Follow-up traps
What would new StringBuilder('a') give as capacity? 97, the code of 'a'.
Does the compiler warn? Usually only IDE inspections do.
#stringbuilder#output
Q59
What does split with a dot return?
intermediate
0, 3, 3. . matches any char so every element is empty and trailing empties are removed. "|" is an empty alternation matching between every char, giving [a, |, b].
⚠ Follow-up traps
How to split on "." safely?Pattern.quote(".") or "\\.".
Would split(".", -1) return empties? Yes, 6 empty strings.
#split#regex#output
Q60
How many elements does each split return?
intermediate
2, 4, 1, 0. Default limit removes trailing empties; an empty input yields one element [""] because no match was found; "," produces two empty strings, both trailing and removed.
⚠ Follow-up traps
What about ",a".split(",").length? 2 (leading empty kept).
What about "a,b".split(",", 0)? Same as no limit.
#split#output
Q61
What does this switch on a null string do?
basic
Throws NullPointerException at the switch (via s.hashCode()). The default does not catch null.
Java 21 allows case null (or case null, default) in pattern-style switches.
Otherwise guard: if (s == null) ....
⚠ Follow-up traps
Does switch (s.toLowerCase()) change this? It still NPEs earlier.
Is case null allowed in Java 17? Only as a preview feature.
#switch#npe
Q62
How does a text block render?
intermediate
Output is Hello, newline, then World! and a final newline. Indentation of 4 spaces is stripped; \ at the end of the World line joins it with the next line, whose own indentation was already stripped.
⚠ Follow-up traps
What if the closing """ were moved 2 spaces left? Two extra spaces of indentation remain on every line.
How to keep a trailing space on a line? End it with \s.
#text-blocks#output
Q63
What do trim and strip return here?
intermediate
6, 2, 3. trim only removes chars <= U+0020, so the em space stays (total 6: em space, space, h, i, space, em space). strip removes Unicode whitespace. The non-breaking space is not "whitespace" per Character.isWhitespace, so it remains.
⚠ Follow-up traps
How to strip NBSP too?s.replaceAll("^[\\s\\u00A0]+|[\\s\\u00A0]+$", "") or \\p{Z}.
Does isBlank treat NBSP as blank? No.
#strip#trim#unicode#output
Q64
What are length and code point count of an emoji string?
intermediate
4, 3, b😀a, and the last line does not compile. '😀' is not a valid char literal because it needs two UTF-16 units. StringBuilder.reverse() keeps surrogate pairs intact.
Replace the last line with s.codePointAt(1) == 0x1F600.
⚠ Follow-up traps
What does s.charAt(1) return? The high surrogate \uD83D.
Does s.substring(0, 2) produce a valid string? It cuts the pair in half, producing a malformed string.
#unicode#surrogates#output
Q65
How many bytes does each encoding give?
intermediate
6, 5, 12, h?llo. UTF-8 uses 2 bytes for é; Latin-1 uses 1 each; UTF_16 adds a 2-byte BOM then 2 per char (2 + 10); ASCII cannot encode é and writes ?.
⚠ Follow-up traps
Why not 10 for UTF-16? The BOM adds 2 bytes; UTF_16BE gives 10.
Does getBytes throw on unmappable characters? No, it substitutes.
#charset#output
Q66
What happens here when formatting with the wrong specifier?
basic
Throws IllegalFormatConversionException: d != java.lang.String at runtime. Format strings are not checked at compile time (some static analyzers do).
String.format("%s items", 5) works since %s accepts any object.
Too few arguments throw MissingFormatArgumentException; extra arguments are ignored.
⚠ Follow-up traps
What if a double is passed to %d? The same exception (d != java.lang.Double).
What happens with %.2f on an Integer?IllegalFormatConversionException: f != java.lang.Integer.
#format#exceptions
Q67
What is the output of these format calls?
intermediate
3.1|42 |000421,234,567 ff -0003.50b a
Grouping separator and decimal mark assume an English-like default locale; others (e.g. de) print 1.234.567 and -0003,50.
⚠ Follow-up traps
Which rounding does %f use?RoundingMode.HALF_UP on the decimal expansion of the double.
How to make output locale-independent?String.format(Locale.ROOT, ...).
#format#output
Q68
replace vs replaceAll with special characters.
basic
xxxxx, axbxc, a11b. replaceAll treats . as any char and $0 as the whole match.
⚠ Follow-up traps
How to insert a literal $?Matcher.quoteReplacement("$") or "\\$".
What does "p".replaceAll("p", "$1") throw?IndexOutOfBoundsException: No group 1.
#replace#regex#output
Q69
Greedy vs lazy: what does each match?
intermediate
<b>x</b><b>y</b> and <b>x</b>. Greedy .* runs to the end then backtracks to the last </b>; lazy stops at the first.
Alternative without laziness: <b>[^<]*</b>.
Do not parse HTML with regex beyond trivial extraction.
⚠ Follow-up traps
Does . match across lines here? Not without DOTALL.
What would <b>.*+</b> do? Fail to match: possessive .*+ swallows </b>.
#regex#greedy#lazy#output
Q70
matches vs find vs lookingAt.
basic
false, false, true. matches needs the whole input; lookingAt needs a prefix match; find scans. The failed matches and lookingAt calls do not move the search position, so find starts at index 0.
⚠ Follow-up traps
What does m.group() give after the find?123.
What if you call find() twice? The second returns false.
#regex#matcher#output
Q71
How are group numbers assigned?
intermediate
4, bc, c, null. Groups are numbered by opening parenthesis; group 4 is optional and did not participate, so it returns null (while groupCount counts it).
⚠ Follow-up traps
What does m.start(4) return?-1.
Does an ? group matching empty return "" or null?null if it did not participate, "" if it matched empty.
#regex#groups#output
Q72
What exception occurs here?
basic
IllegalStateException: No match found. find() returned false and the result was ignored.
Always test: if (m.find()) { ... }.
⚠ Follow-up traps
What about calling group() on a fresh matcher? The same exception.
What about group(5) on a pattern with one group?IndexOutOfBoundsException.
#regex#matcher#exceptions
Q73
A signup form regex hangs your service at 100% CPU. How do you diagnose and fix it?
advanced
Take 3-5 thread dumps (jcmd <pid> Thread.print); threads stuck in java.util.regex.Pattern$...match frames, often Pattern$Loop or Branch, point to ReDoS. The stack's caller shows which pattern and input.
Reproduce with the offending input; time with increasing length (exponential growth confirms).
Fix the pattern: remove nested quantifiers ((\w+\s?)*), use possessive quantifiers or atomic groups, or tighten classes.
Add input length limits before matching and a deadline CharSequence.
Consider replacing with a parser or RE2/J for untrusted patterns.
⚠ Follow-up traps
Will restarting the pod help? Only until the next malicious request.
Can a bigger thread pool absorb it? No, each stuck thread burns a core indefinitely.
#regex#redos#debugging
Q74
Why is this pattern dangerous?
advanced
It returns false but only after exponential backtracking (2^n ways to partition the as between inner and outer +). With 30 characters it can take seconds to minutes on older JDKs.
Safe rewrite: ^a+$.
Or prevent backtracking: ^(?>a+)+$ or ^(a++)++$.
Modern JDKs memoize some constructs, but you cannot rely on it.
⚠ Follow-up traps
Does the input "aaaa" (no !) cause the problem? No, it matches immediately.
Does anchoring help? Not against the nested quantifier itself.
#regex#redos#output
Q75
How does case-insensitive matching handle non-ASCII?
intermediate
false then true. CASE_INSENSITIVE is ASCII-only unless UNICODE_CASE is also set.
⚠ Follow-up traps
Does "é".equalsIgnoreCase("É") work? Yes, it handles Unicode per char.
Does (?iu) do the same as the flags? Yes.
#regex#flags#unicode#output
Q76
Does \w match accented letters?
intermediate
false, true, true. By default \w is ASCII [a-zA-Z_0-9]; (?U) enables Unicode semantics; \p{L} is the Unicode letter category.
⚠ Follow-up traps
Does \b follow the same rule? Yes, it follows \w unless (?U).
Does \s match NBSP? Not by default.
#regex#unicode#output
Q77
A user enters Turkish locale settings and your keyword lookup fails. Why?
intermediate
With default locale tr, "TITLE".toLowerCase() gives "tıtle" (dotless i), so the key is not found.
Always use toLowerCase(Locale.ROOT) / toUpperCase(Locale.ROOT) for identifiers, headers, enum lookups, and map keys.
The reverse bug exists: "title".toUpperCase() in Turkish yields TİTLE.
⚠ Follow-up traps
Does equalsIgnoreCase have this issue? No, it is locale-independent.
Can the JVM default locale be changed per request? Not by default, but -Duser.language=tr or container settings can.
#locale#case#debugging
Q78
A password ends up in logs and heap dumps. What went wrong and how do you fix it?
intermediate
The password was held in a String (immutable, lives until GC, may be interned or logged by toString) or inside a DTO whose toString() printed it.
Use char[] or byte[] for secrets that you control and zero them in finally.
Exclude secrets from toString, Lombok @ToString.Exclude, and logging filters; mask in request logging.
Disable heap dumps with secrets or encrypt them; restrict /actuator/heapdump.
Hash with bcrypt/Argon2 and discard the plaintext immediately.
⚠ Follow-up traps
Does char[] fix a framework that binds JSON to a String? No, the String already exists. Limit exposure elsewhere.
Is Arrays.fill guaranteed to run? The JIT may not eliminate it for arrays that escape; still, it cannot be relied on against all copies.
#security#password#logging
Q79
Your batch job builds a 200 KB report with += in a loop and is slow. Why?
basic
Each iteration allocates a new string and copies all previous content, giving O(n^2) copying and heavy GC. JIT does not turn this loop into a single builder.
Use StringBuilder (presized) or String.join("\n", lines) / Collectors.joining.
A single expression a + b + c is fine; the problem is accumulation across iterations.
⚠ Follow-up traps
Did Java 9 indy concat fix this? No, it optimizes one expression, not the loop.
Is StringBuffer better here? No, only slower.
#performance#concatenation
Q80
Interning UUIDs from requests made the heap grow. Why?
advanced
intern() puts every distinct string into the global table. Unbounded unique values (UUIDs, session IDs) fill it; entries are collectable only when unreachable and the table needs cleanup passes, so it adds GC and lookup cost without any sharing benefit.
Intern only a small closed set of repeated values.
Check with -XX:+PrintStringTableStatistics (Java 8) or jcmd VM.stringtable.
A bounded ConcurrentHashMap cache or enums fit better.
⚠ Follow-up traps
Does intern() guarantee a smaller footprint? Only if duplicates are really common.
Is a large StringTableSize a fix? It reduces chain length, not retention.
#intern#memory#string-pool
Q81
Why is sharing a StringBuffer between threads still buggy?
intermediate
Each method is synchronized, but the check-then-act sequence is not atomic, so init; can be appended twice. Interleaved multi-step appends also mix content.
Synchronize the whole compound action on an external lock, or build per-thread StringBuilders and merge.
StringBuffer is rarely the right tool; it only guards individual calls.
⚠ Follow-up traps
Can sb.append(a).append(b) interleave with another thread's append? Yes, between the two calls.
Would StringBuilder be worse? It can corrupt internal state; use confinement instead.
#stringbuffer#concurrency
Q82
Why does parsing CSV with split(",") fail on real files?
intermediate
Prints 4, not 3: the comma inside the quoted field is also a separator, giving 1, "Doe, Jane", NY. Quoted fields, escaped quotes and embedded newlines need a real parser (Apache Commons CSV, OpenCSV, Jackson CSV).
split(",", -1) at least preserves trailing empty columns.
Never hand-roll CSV for external input.
⚠ Follow-up traps
Does a lookahead regex solve it? Partially (,(?=(?:[^"]*"[^"]*")*[^"]*$)), but is slow and breaks on newlines and escaped quotes.
What about whitespace around fields?split keeps it; strip() each field.
#split#csv
Q83
How do you prevent log forging?
intermediate
If username is bob\nINFO login ok for admin, the attacker adds a fake log line. Parameterized logging does not strip newlines by itself.
Sanitize CR/LF (replace with _) or use a JSON log encoder that escapes control characters.
Cap the length of logged user input.
Prefer log.info("login failed for {}", username) for performance, plus the encoder for safety.
⚠ Follow-up traps
Is {} placeholders enough? No, it avoids concatenation cost, not injection.
What other characters matter? ANSI escapes and Unicode line separators (U+2028) in some viewers.
#security#logging#injection
Q84
What is the memory behaviour of substring?
intermediate
In Java 6 substring shared the parent's char[], so id kept 50 MB alive. Since Java 7u6 it copies just the range, so the parent is collectable and the leak is gone.
On modern JDKs substring costs O(length of the result).
Old advice new String(s.substring(...)) is obsolete.
⚠ Follow-up traps
Is substring(0) a copy? It returns this when the range is the whole string.
Is subSequence different? It delegates to substring.
#substring#memory#java6
Q85
What is the output of sorting strings naturally?
basic
[10, 9, A, B, a, b]. Natural order is by UTF-16 code unit: digits (48-57) before uppercase (65-90) before lowercase (97-122); "10" precedes "9" since '1' < '9'.
For case-insensitive order use String.CASE_INSENSITIVE_ORDER; for language-correct order use Collator.
For numeric order parse to numbers or use a natural-order comparator.
⚠ Follow-up traps
Where does é sort? After z (U+00E9 > U+007A) under compareTo.
Does CASE_INSENSITIVE_ORDER keep stable output for a and A?List.sort is stable, so equal keys keep input order.
#compareto#sorting#output
Q86
What does passing strings and builders to a method do?
basic
a ax. References are passed by value; s += "x" rebinds the local parameter to a new string, while sb.append mutates the shared object.
⚠ Follow-up traps
Is Java pass-by-reference for objects? No, the reference is copied by value.
What if f did sb = new StringBuilder("z")? Nothing visible to the caller: the output stays a a, since only the local reference was rebound.
#pass-by-value#immutability#output
Q87
How does null behave in string concatenation and methods?
basic
nulla, 4, then NullPointerException. Concatenation converts a null reference to "null"; valueOf((Object) null) also returns "null" (length 4); invoking a method on null throws.
⚠ Follow-up traps
What does String.valueOf(null) do? NPE: the char[] overload is chosen.
What does Objects.toString(null) return?"null".
#null#output
Q88
What do println(char[]) and string concatenation with char[] print?
intermediate
hi, then something like [C@1b6d3586, then hi. PrintStream.println(char[]) prints characters; concatenation uses Object.toString() (type, @, hash).
This is why logging a password char[] with "" + pw leaks nothing useful but still signals a bug; always handle explicitly.
⚠ Follow-up traps
Does System.out.println((Object) a) print hi? No, [C@....
Does Arrays.toString(a) print hi? It prints [h, i].
#char-array#output
Q89
Count lines in mixed line-ending text.
intermediate
3, 3, 3. lines() handles \n, \r, \r\n and drops the trailing terminator. split("\n") yields a, b\r, c (note the leftover \r). \R matches any linebreak sequence.
⚠ Follow-up traps
What does "a\n\n".lines().count() give? 2 (a and an empty line).
Is lines() lazy? Yes, it streams without building all substrings first.
#lines#split#output
Q90
substring boundaries.
basic
true, true, then StringIndexOutOfBoundsException (begin 4, length 3). beginIndex == length is allowed and yields the empty string.
⚠ Follow-up traps
Is substring(2, 1) valid? No, begin > end throws.
Is the end index inclusive? No, exclusive.
#substring#exceptions#output
Q91
What does replace do with overlapping matches?
intermediate
ba, bb, -a-b-c-. Matching scans left to right without overlap; replacing an empty target inserts the replacement between every char and at both ends.
⚠ Follow-up traps
Does replace re-scan replaced text? No.
Does "abc".replaceAll("", "-") differ? No, same result.
#replace#output
Q92
indexOf, contains and empty strings.
intermediate
0, 3, true, -1. The empty string is found at index 0 (and at length for lastIndexOf); an out-of-range start index is not an error for indexOf.
⚠ Follow-up traps
Does startsWith("") return true? Yes.
What does indexOf(int) take? A code point, so supplementary characters work.
#indexof#output
Q93
Counting characters with streams.
intermediate
2, [h, e, l, l, o], h-e-l-l-o. chars() is an IntStream; casting to char is required to get characters instead of numbers (mapToObj(c -> c) would produce integers).
⚠ Follow-up traps
Does chars() handle emoji correctly? No, it yields surrogates; use codePoints().
Can you collect Stream<Character> into a String directly? Not without StringBuilder/Collectors.joining after mapping to String.
#streams#chars#output
Q94
How do the common parse methods treat whitespace and signs?
basic
5, then NumberFormatException: For input string: " 5". A leading + or - is accepted; whitespace is not, so strip() before parsing.
Integer.parseInt("") and null both throw NumberFormatException.
Use Integer.valueOf for boxed values, and Integer.parseInt(s, radix) for other bases.
⚠ Follow-up traps
Does "2147483648" parse? No, NumberFormatException; use Long.parseLong.
Does Double.parseDouble(" 5 ") work? Yes, it trims whitespace.
#parsing#exceptions#output
Q95
Does null-safety differ between equals variants?
basic
false, true, then NullPointerException. Put the known non-null literal on the left, or use Objects.equals.
⚠ Follow-up traps
What does "x".equalsIgnoreCase(null) return?false.
What does "".equals(null) return?false.
#equals#null#output
Q96
Two visually identical strings are not equal. Why?
advanced
false 1 2. They are canonically equivalent but different code unit sequences. Normalize before comparing or indexing: Normalizer.normalize(s, Normalizer.Form.NFC).
Apply normalization on input boundaries (usernames, search keys).
Use Collator with Collator.CANONICAL_DECOMPOSITION for comparison respecting locale.
⚠ Follow-up traps
Which form should be stored? Commonly NFC; use NFKC for compatibility folding such as full-width digits (with care for security-sensitive IDs).
Does equalsIgnoreCase normalize? No.
#unicode#normalization#output
Q97
Why is the result of this floating-point string conversion surprising?
intermediate
0.30000000000000004, 0.13, 0.3. Binary floating point cannot represent 0.1 or 0.2 exactly; Double.toString prints the shortest string that round-trips. %f rounds HALF_UP on the decimal value of the double (0.125 and 0.25 are exact), unlike BigDecimal's HALF_EVEN defaults elsewhere.
Use BigDecimal (constructed from strings) for money.
⚠ Follow-up traps
What does new BigDecimal(0.1) show? The long exact binary expansion; use BigDecimal.valueOf(0.1) or a string.
Is Math.round the same as %.0f? Not for negative halves: Math.round(-2.5) is -2; %.0f gives -3.
#format#double#output
Q98
How do you reuse a Pattern efficiently in a hot path?
intermediate
String.matches compiles the regex on every call. Hoist the Pattern into a static final field and create a Matcher per call (or use asMatchPredicate()).
private static final Pattern ID = Pattern.compile("[A-Z]{3}-\\d{4}");boolean valid(String s) { return ID.matcher(s).matches(); }
For trivial checks (prefix, one char) skip regex: startsWith, indexOf, a loop.
⚠ Follow-up traps
Is the static Pattern safe across threads? Yes, it is immutable.
Is the static Matcher safe? No, it holds match state.
#regex#performance
Q99
Design an email validation rule. What should you check?
intermediate
A complete RFC 5322 regex is huge and still not proof that a mailbox exists. Use a simple sanity check and verify by sending a confirmation link.
A pragmatic pattern: ^[^@\s]+@[^@\s]+\.[^@\s]+$, length-bounded (254 total, 64 for local part).
Bean Validation @Email is lenient; Hibernate Validator accepts a@b.
Normalize with strip() and lowercase the domain only.
Beware nested quantifiers in copy-pasted "perfect" regexes (ReDoS).
⚠ Follow-up traps
Is the local part case-insensitive? Technically case-sensitive per RFC, but nearly all providers ignore case.
Does a passing regex prove deliverability? No.
#regex#validation#design
Q100
Which approach is right to extract fields from a log line?
intermediate
For fixed-format lines, a precompiled regex with named groups is clear and fast enough; for very high volume or strict structure, manual indexOf/substring parsing is faster.
Pattern P = Pattern.compile( "(?<date>\\S+) (?<time>\\S+) (?<level>\\w+) user=(?<user>\\d+) msg=(?<msg>.*)");Matcher m = P.matcher(line);if (m.matches()) System.out.println(m.group("user")); // 42
Prefer structured (JSON) logs to avoid parsing at all.
Anchoring with matches() and specific classes (\\S+) limits backtracking.
⚠ Follow-up traps
Why \\S+ rather than .+? It cannot span fields, avoiding wrong greedy captures and backtracking.
What if the message can contain newlines? Add (?s) so . matches them.
#regex#parsing#design
Q101
What are Matcher.appendReplacement pitfalls?
advanced
a<2>b<44>. Omitting appendTail would lose the trailing text. The replacement string is parsed for $ and \, so computed text containing those must go through Matcher.quoteReplacement.
The StringBuilder overloads exist since Java 9; before that only StringBuffer.
Simpler on Java 9+: m.replaceAll(r -> String.valueOf(Integer.parseInt(r.group()) * 2)).
⚠ Follow-up traps
Does the lambda form need escaping? No, its result is treated literally.
Why not String.replaceAll for this? It cannot compute replacements.
#regex#matcher#output
Q102
Why might a regex with lookbehind throw at compile time?
advanced
Java requires lookbehind to have an obvious maximum length. Unbounded + or * historically raised PatternSyntaxException: Look-behind group does not have an obvious maximum length. Newer releases accept some forms, but unbounded lookbehind is still a risk of failure and poor performance.
Use a bounded form: (?<=a{1,10})b.
Or capture the prefix in a group and use group(1) instead.
⚠ Follow-up traps
Is (?<=ab|c) fine? Yes, alternatives of bounded length are allowed.
Does lookbehind consume input? No.
#regex#lookbehind#exceptions
Q103
How can an `intern`-based == comparison silently break?
basic
The comparison is always false for runtime strings from the request because they are new objects, not the pooled literal. Use "ACTIVE".equals(status).
It can appear to work in unit tests that pass literals.
Static analysis (SpotBugs ES_COMPARING_STRINGS_WITH_EQ, Sonar S4973) flags it.
Better still: parse into an enum with a null-safe lookup.
⚠ Follow-up traps
Would status.intern() == "ACTIVE" work? Yes, but is slower and unidiomatic.
Does == ever beat equals? Only for a proven-interned closed set, rarely worth it.
#equals#identity#bugs
Q104
Base64 in URLs: why does decoding fail?
intermediate
Prints +/8= and then throws IllegalArgumentException: Illegal base64 character 2b. The basic alphabet uses + and /; the URL-safe alphabet uses - and _.
Encode and decode with the same variant; Base64.getUrlEncoder().withoutPadding() for tokens.
Remember it is encoding, not encryption.
⚠ Follow-up traps
What does the MIME decoder do with illegal characters? Ignores them.
Why do JWTs omit =? URL-safe Base64 without padding is specified by RFC 7515.
#base64#exceptions#output
Q105
What happens when a File is read with the wrong charset in a CI environment?
intermediate
Before Java 18 new String(byte[]) uses the platform default charset, so a UTF-8 file with é works on Linux CI (UTF-8) but turns into mojibake on Windows (Cp1252). Specify the charset.
String s = Files.readString(path, StandardCharsets.UTF_8);
Java 18+ makes UTF-8 the default but explicit is still better.
Files.readString throws MalformedInputException on invalid UTF-8, unlike new String.
⚠ Follow-up traps
Does a BOM matter? UTF-8 readers in Java do not strip it; U+FEFF appears as the first character.
How to detect the problem? Look for ? or U+FFFD in output.
#charset#debugging
Q106
What is the behaviour of strings as keys in a HashMap after mutation of the source builder?
basic
1 null. toString() copies the content, so the key is an independent immutable string; the later builder change does not affect it, and "kx" was never stored.
Using the StringBuilder itself as a key would be unsafe: identity hashCode/equals and mutability.
⚠ Follow-up traps
Would m.get(sb) find "k"? No, equals between String and StringBuilder is false.
Do keys need to be immutable? Yes, for stable hashCode.
#immutability#hashmap#output
Q107
How does a switch on strings behave with colliding hash codes?
advanced
"Aa" prints first, "BB" prints second, anything else none. Both labels hash to 2112, so the generated lookupswitch falls into one bucket where equals picks the right label.
Correctness never depends on hashes alone.
The compiler emits two switches: one on hashCode, one on a computed index.
⚠ Follow-up traps
Can duplicate labels exist? No, compile error.
Does the order of labels matter? Not for arrow-form arms, there is no fall-through.
#switch#hashcode#output
Q108
How do you reverse the words in a sentence robustly?
basic
Strip, split on one or more whitespace characters, reverse, and join with a single space.
List<String> w = Arrays.asList(s.strip().split("\\s+"));Collections.reverse(w);System.out.println(String.join(" ", w)); // blue is sky the
Without strip(), a leading separator would produce a leading empty element.
For huge input avoid regex; scan from the end with lastIndexOf.
⚠ Follow-up traps
What does split(" ") give for multiple spaces? Empty strings between them.
What does it return for an all-blank input after strip()?[""], so check isBlank() first.