Char Array to String — Zeroing Doesn't Protect Passwords
Heap dumps expose plaintext passwords after char[] to String — zeroing the array doesn't protect the copy.
- Core concept: four methods convert char[] to String, but differ in null handling and intent
- String constructor: most common, throws NPE on null
- String.valueOf(): returns literal "null" for null input — cleaner for safe defaults
- StringBuilder.append(): useful when building strings incrementally, not for direct conversion
- Performance insight: all four methods copy the char array — O(n) time, O(n) memory
- Production insight: String.valueOf(null) returns "null" string, not null — silent bug if you expect null propagation
char[] to String conversion comes up surprisingly often in real codebases. Security-conscious APIs (like Java's own KeyStore and JPasswordField) return passwords as char[] rather than String precisely because char arrays can be zeroed out after use, while String literals are interned and may linger in the heap. Understanding the conversion and knowing which method to use matters for correctness, not just convenience.
Four Methods for char[] to String Conversion
All four methods produce the same output for typical use cases. The differences matter at the margins: performance for large arrays, null-safety, and intent signalling. Here's a closer look at each one and where you'd use it in production.
Under the Hood: String Constructor vs String.valueOf()
Many engineers assume these two have different implementations. They don't. Open the OpenJDK source for String.class and you'll see valueOf(char data[]) simply returns new String(data). The only difference is how they handle null.
When you call new String((char[]) null), the JVM immediately throws NullPointerException before the constructor can even check the argument. But String.valueOf handles null differently: it returns the literal string "null". This is consistent with other valueOf methods in Java, but it often surprises developers expecting a null return.
So why have both? Intent. new String(chars) signals "I expect this to be non-null — fail early". String.valueOf(chars) signals "I accept null and want a safe string representation". Pick based on what your downstream code expects.
Partial Char Array Conversion Using Offset and Count
Sometimes you have a char[] that contains more data than you need. Maybe you're parsing a fixed-width record from a mainframe, or you've received a buffer that includes headers. The String constructor accepts an offset and count: new String(chars, offset, length). This creates a String using only a subset of the array, starting at offset and taking length characters.
This is not the same as copying the array and then calling substring. The constructor directly reads only the specified range, avoiding an intermediate copy. It's both memory-efficient and faster than the alternative.
Watch out: if offset + count exceeds chars.length, you'll get a StringIndexOutOfBoundsException. Always validate bounds in production code, especially when the offset comes from untrusted input.
Security: Why char[] and How to Zero Out After Conversion
Java's security APIs (JPasswordField, KeyStore, Console.readPassword) return passwords as char[] instead of String for a specific reason: char arrays are mutable and can be cleared explicitly after use. String is immutable — once created, the backing array lives on the heap until garbage collected. And because String objects are interned, they can survive long after you discard the reference.
When you convert a char[] password to a String, you create an immutable copy. Even if you zero the original char[], the String still holds the password in its private array. The only way to truly clear it is to ensure the String object is garbage collected quickly — but you can't zero it.
Best practice: avoid converting char[] to String for sensitive data. If you must, null the String reference right after use and trust the GC (it's not immediate). For additional protection, use a char[] throughout the sensitive operation and only convert at the exact point that requires a String (e.g., passing to a legacy API).
Performance Considerations: When Method Choice Matters
For most use cases, the performance difference between methods is negligible. All four methods ultimately copy the character data. But there are scenarios where method choice impacts memory and CPU.
For small arrays (< 1000 characters), any method works. For large arrays (millions of characters), use the direct constructor or valueOf — both make exactly one copy. StringBuilder.append makes two copies: first into its internal buffer (which may resize), then into the final String. String.copyValueOf is identical to valueOf but kept for legacy compatibility — no performance penalty.
Memory overhead: Each copy consumes 2 bytes per character (char in Java is UTF-16). A 1 million character array → 2 MB for the source + 2 MB for the String = 4 MB peak. StringBuilder adds another 2 MB during append (its buffer may be larger due to growth factor). Avoid StringBuilder for direct conversion.
Consider using the partial constructor (offset+count) when you only need a portion of the array. It avoids copying the entire input.
| Method | Syntax | Null-safe? | Use When | Copies |
|---|---|---|---|---|
| String constructor | new String(chars) | NPE on null | Default choice — clear intent, fail fast on null | 1 copy |
String.valueOf() | String.valueOf(chars) | Returns 'null' string | Null safety matters, safe fallback token | 1 copy |
StringBuilder.append() | sb.append(chars).toString() | No | Building incrementally from multiple sources | 2 copies |
String.copyValueOf() | String.copyValueOf(chars) | NPE on null | Legacy code compatibility, explicit copy semantics | 1 copy |
Key Takeaways
- new String(chars) and String.valueOf(chars) are the two most common approaches — both produce identical results for non-null arrays.
- String.valueOf(null) returns the string 'null' rather than null — use explicit null check if null propagation matters.
- new String(chars, offset, count) handles partial array conversion — useful when your char[] contains more data than you want.
- Always zero out char[] password arrays with Arrays.fill(password, '\0') after use — this is why security APIs return char[] instead of String.
- Prefer direct constructor or valueOf over StringBuilder for single char[] to String conversion — avoids double memory copy.
String.copyValueOf()is a legacy alias for valueOf — no performance or behavioural difference in modern Java.
Common Mistakes to Avoid
- Using Arrays.toString(chars) for conversion
Symptom: String output shows '[T, h, e, C, o, d, e, F, o, r, g, e]' with brackets and commas — not the concatenated string.
Fix: Replace Arrays.toString(chars) with new String(chars) or String.valueOf(chars). - Not zeroing the char array after converting a password
Symptom: Sensitive data remains in memory as a mutable char[] even after use, visible in heap dumps or memory analysis.
Fix: Call Arrays.fill(password, '\0') immediately after converting to String and using the password. Note: this does not clear the String's internal copy. - Calling String.valueOf(null) expecting null back
Symptom: Downstream code checks for null but receives the literal string 'null', leading to logic errors or false validation passes.
Fix: If you need null propagation, add explicit null check before conversion: chars == null ? null : new String(chars) or String.valueOf(chars) depending on intent. - Using StringBuilder.append(chars) for a single array conversion
Symptom: Unnecessary memory overhead — two copies instead of one. For large arrays, can cause OutOfMemoryError or excessive GC.
Fix: Use new String(chars) or String.valueOf(chars) for direct conversion. Reserve StringBuilder for when you're appending multiple pieces. - Forgetting to validate offset and count in partial conversion
Symptom: StringIndexOutOfBoundsException at runtime when offset + count exceeds array length.
Fix: Add precondition checks: if (offset < 0 || count < 0 || offset + count > chars.length) throw newIllegalArgumentException();
Interview Questions on This Topic
- QWhat are the different ways to convert a char array to a String in Java?JuniorReveal
- QWhy do some security APIs return char[] instead of String for passwords?SeniorReveal
- QWhat happens if you pass null to String.valueOf(char[]) vs new String(char[])?Mid-levelReveal
- QIs there any performance difference between the four conversion methods for large char arrays?SeniorReveal
- QHow do you convert only a portion of a char array to a String?JuniorReveal
Frequently Asked Questions
How do I convert a char array to a String in Java?
The most common approach is new String(chars) or String.valueOf(chars) — both produce the same result. Use String.valueOf() if you need null-safe behaviour (it returns the literal string 'null' for a null input rather than throwing NullPointerException).
Why does Arrays.toString(chars) not work for char array to String conversion?
Arrays.toString() formats the array as a string representation: '[T, h, e, C, o, d, e]' with brackets and commas between each character. Use new String(chars) or String.valueOf(chars) to concatenate the characters without any formatting.
Can I convert a char array to String without copying the data?
No. String is immutable and must have its own backing array. All methods copy the char data. The closest you can get is using StringBuilder or StringBuffer, but they also copy internally. For read-only access, you could create a custom CharSequence wrapper over the char[], but that is not a String.
Is String.copyValueOf() deprecated?
It is not formally deprecated in modern Java, but it is a legacy method. Its implementation is identical to String.valueOf(char[]). Oracle recommends using valueOf or the String constructor for new code. copyValueOf exists solely for backward compatibility with pre-Java 2 code.
Does zeroing a char[] after conversion protect the password in memory?
No. The String constructor copies the characters into its own array. Zeroing the original char[] does not affect the String's internal array. The String persists in the heap until garbage collected, and there is no safe way to explicitly clear it. For sensitive data, avoid conversion to String entirely.
That's Strings. Mark it forged?
3 min read · try the examples if you haven't