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
Plain-English First
A char array in Java is a sequence of individual characters. A String is an immutable object wrapping character data. Converting between them is a basic operation you'll need constantly — parsing input, processing text from legacy APIs, working with cryptography code that deals in char[] rather than String for security reasons.
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.
String constructor calls Arrays.copyOf internally — a full copy.
String.valueOf(char[]) delegates to the same constructor.
StringBuilder.append(char[]) copies into its own buffer first, then toString() copies again.
String.copyValueOf() is identical to valueOf — legacy alias.
Production Insight
You might think StringBuilder.append(char[]) creates an intermediate copy into StringBuilder's internal buffer — it does, but that's actually a second copy.
The String constructor already copies the char[] once. Using StringBuilder adds another allocation and copy, doubling memory overhead for large arrays.
Rule: for direct char[] → String, use the constructor or valueOf. Reserve StringBuilder for incremental builds.
Key Takeaway
new String(chars) and String.valueOf(chars) share the same implementation under the hood.
String.valueOf(null) returns "null" string, new String(null) throws NPE.
Use valueOf when null-safety with a fallback token is acceptable; use constructor when null should fail fast.
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.
We had a service that validated credit card numbers. The upstream returned a char[] but sometimes null for invalid requests. The developer used String.valueOf(chars) and downstream checked for null. The string "null" passed validation (character matching) and caused cryptic failures.
The fix: explicit null check before conversion, or catch the NPE from new String() and handle null separately.
Lesson: String.valueOf(null) does not propagate null. It creates a "null" token. Document this behaviour if your code relies on it.
Key Takeaway
String.valueOf(char[]) internally calls new String(char[]) — identical performance.
Null handling is the only behavioural difference.
Choose based on whether incoming null is a valid state or an error condition.
Which Conversion Method to Use?
Ifchar[] is guaranteed non-null
→
UseUse new String(chars) — fail fast on unexpected null, no ambiguity
Ifchar[] could be null, and you need a safe string representation
IfYou are building a string incrementally from multiple char[] sources
→
UseUse StringBuilder.append(chars) — but be aware of double copy
IfYou are working in legacy code that uses String.copyValueOf()
→
UseKeep it for consistency — but it's identical to valueOf and deprecated in spirit
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.
PartialConversionExample.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package io.thecodeforge.strings;
publicclassPartialConversionExample {
publicstaticvoidmain(String[] args) {
char[] buffer = {'S', 'T', 'A', 'R', 'T', 't', 'h', 'e', 'C', 'o', 'd', 'e', 'F', 'o', 'r', 'g', 'e', 'E', 'N', 'D'};
int headerSize = 5; // 'START'
int trailerSize = 3; // 'END'int dataOffset = headerSize;
int dataLength = buffer.length - headerSize - trailerSize;
// Convert only the data portionString data = newString(buffer, dataOffset, dataLength);
System.out.println("Data portion: " + data); // theCodeForge// Unsafe — this throws StringIndexOutOfBoundsException// String bad = new String(buffer, 0, 100);// Safe conversion with validationint offset = 0;
int length = 100;
if (offset >= 0 && length >= 0 && offset + length <= buffer.length) {
String safe = newString(buffer, offset, length);
} else {
thrownewIllegalArgumentException("Invalid offset/length");
}
}
}
Output
Data portion: theCodeForge
Production Insight
In a file parser processing millions of records, using substring on a full copy of the char[] created O(n^2) memory overhead. Switching to new String(char[], offset, count) eliminated the extra copy and cut GC pressure by 30%.
The bounds check is cheap but critical. In high-throughput code, the JVM may inline the check. Still, always validate offset and count when they come from external input.
Rule: use partial constructor whenever your data lives in a larger buffer — avoid copying the whole buffer just to discard parts.
Key Takeaway
new String(chars, offset, count) creates a string from a subrange without extra copy.
Always validate bounds in production code to avoid StringIndexOutOfBoundsException.
Prefer this over copying the full array and calling substring for large buffers.
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).
SecurePasswordConversion.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package io.thecodeforge.security;
import java.util.Arrays;
publicclassSecurePasswordConversion {
publicstaticvoidmain(String[] args) {
char[] password = retrievePasswordFromSecureStore();
// Only convert when absolutely necessaryString passwordString = null;
try {
passwordString = newString(password);
authenticate(passwordString);
} finally {
// Zero original char arrayArrays.fill(password, '\0');
// Nullify the String reference to help GC
passwordString = null;
}
}
privatestaticchar[] retrievePasswordFromSecureStore() {
// Simulating a secure storereturnnewchar[]{'s', '3', 'c', 'r', '3', 't'};
}
privatestaticvoidauthenticate(String password) {
System.out.println("Authenticating with: " + password);
}
}
Output
Authenticating with: s3cr3t
(Original char[] zeroed, String reference nulled)
Production Insight
A major e-commerce platform had a security audit that found customer passwords in a heap dump taken for performance analysis. The passwords were stored as char[] in memory but had been converted to String for logging purposes. The char[] was zeroed after conversion, but the String objects persisted in the heap dump.
The fix: remove the conversion entirely and pass char[] directly to the authentication API (which accepted char[]). Also, disable heap dumps on production unless absolutely necessary and ensure they are immediately destroyed.
Lesson: zeroing the source char[] does not protect the copy inside the String. Avoid converting sensitive data to String if possible.
Key Takeaway
Zeroing a char[] after conversion to String does NOT clear the String's internal copy.
Null the String reference after use to help GC, but understand the copy still exists temporarily.
Prefer char[] for sensitive data end-to-end — only convert when forced by legacy APIs.
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.
StringBuilder Overhead for Direct Conversion
Never use StringBuilder.append(char[]) as the primary method for converting a single char[] to String. It creates two copies of the data. Use new String(chars) or String.valueOf(chars) instead. StringBuilder is only beneficial when you're appending multiple char[] or other values incrementally.
Production Insight
A log aggregation service processed char[] buffers containing up to 500,000 characters per event. The original code used StringBuilder.append(chars).toString(), causing 2x memory per conversion. With 10,000 concurrent requests, memory spiked to 20 GB for the conversion alone. Switching to new String(chars) cut memory usage in half and eliminated GC pressure.
Rule: for single-array conversion, always prefer the String constructor or valueOf. StringBuilder is for building strings, not direct conversion.
Key Takeaway
new String(chars) and String.valueOf(chars) are equivalent in performance: one copy.
StringBuilder.append(chars) makes two copies — avoid for direct conversion.
Use partial constructor for subrange conversion — no extra copy needed.
● Production incidentPOST-MORTEMseverity: high
The Password Leak That Could Have Been Avoided
Symptom
Heap dump from a production JVM revealed plaintext passwords in String objects that should have been ephemeral char[] values.
Assumption
The team assumed that converting char[] to String with new String(password) was safe because the char[] was zeroed immediately after conversion.
Root cause
The char[] was zeroed, but the String constructor copies the characters into a new internal array. That String object persisted in the heap until GC. The zeroed char[] did nothing to the copy inside the String. A heap dump captured the String with the password intact.
Fix
Avoid converting sensitive char[] data to String if possible. If conversion is unavoidable, use a minimal scope and explicitly null the String reference after use. For production, consider using a SecretKey or a dedicated secure byte array wrapper.
Key lesson
Zeroing a char[] after conversion to String does NOT protect the copy inside the String object.
If you must convert a password char[] to String, minimise its lifetime and null the reference immediately after use.
Prefer char[] for sensitive data throughout the entire code path when possible.
Production debug guideSymptom → Action matrix for common conversion pitfalls in production5 entries
Symptom · 01
NullPointerException when calling new String(chars)
→
Fix
Check if chars is null. Use String.valueOf(chars) instead — it returns "null" string rather than throwing NPE. If you need null propagation, add explicit null check before conversion.
Symptom · 02
String contains unexpected literal "null" text
→
Fix
Verify that the char[] variable was not null. String.valueOf(null) returns "null" as a string. Use a ternary: chars == null ? null : new String(chars).
Symptom · 03
String output shows brackets and commas like [a, b, c]
→
Fix
You called Arrays.toString(chars) instead of new String(chars). Replace with new String(chars) or String.valueOf(chars).
Symptom · 04
OutOfMemoryError when converting a very large char[]
→
Fix
The String constructor creates an exact copy of the char[]. For large arrays (e.g., >10 MB), consider processing in chunks or using StringBuilder with incremental append to reduce peak memory.
Symptom · 05
Partial conversion produces wrong output
→
Fix
Check offset and count parameters in new String(chars, offset, count). Ensure offset + count ≤ chars.length. The result will only include characters from offset to offset+count-1.
★ char[] to String Quick Debug Cheat SheetFive common symptoms and immediate commands to diagnose char[] to String conversion issues in production.
new String(chars) and String.valueOf(chars) are the two most common approaches
both produce identical results for non-null arrays.
2
String.valueOf(null) returns the string 'null' rather than null
use explicit null check if null propagation matters.
3
new String(chars, offset, count) handles partial array conversion
useful when your char[] contains more data than you want.
4
Always zero out char[] password arrays with Arrays.fill(password, '\0') after use
this is why security APIs return char[] instead of String.
5
Prefer direct constructor or valueOf over StringBuilder for single char[] to String conversion
avoids double memory copy.
6
String.copyValueOf() is a legacy alias for valueOf
no performance or behavioural difference in modern Java.
Common mistakes to avoid
5 patterns
×
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 new IllegalArgumentException();
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
Q01JUNIOR
What are the different ways to convert a char array to a String in Java?
Q02SENIOR
Why do some security APIs return char[] instead of String for passwords?
Q03SENIOR
What happens if you pass null to String.valueOf(char[]) vs new String(ch...
Q04SENIOR
Is there any performance difference between the four conversion methods ...
Q05JUNIOR
How do you convert only a portion of a char array to a String?
Q01 of 05JUNIOR
What are the different ways to convert a char array to a String in Java?
ANSWER
The four primary ways are: 1) new String(chars) — most common, throws NPE on null; 2) String.valueOf(chars) — returns 'null' string for null; 3) StringBuilder.append(chars).toString() — useful when building incrementally; 4) String.copyValueOf(chars) — legacy method identical to valueOf. All produce the same string for non-null input. The key differences are null handling and intent signalling.
Q02 of 05SENIOR
Why do some security APIs return char[] instead of String for passwords?
ANSWER
String is immutable and cannot be explicitly cleared — its backing array persists on the heap until garbage collected, potentially appearing in heap dumps. char[] can be zeroed after use via Arrays.fill(password, '\0'), reducing the window during which sensitive data is accessible in memory. However, converting char[] to String defeats this protection because the String creates a separate immutable copy. Prefer keeping sensitive data as char[] throughout.
Q03 of 05SENIOR
What happens if you pass null to String.valueOf(char[]) vs new String(char[])?
ANSWER
new String((char[]) null) throws NullPointerException because the constructor does not handle null pointers. String.valueOf(null) returns the literal string 'null', consistent with other valueOf methods in Java. This can be a source of subtle bugs if downstream code expects null.
Q04 of 05SENIOR
Is there any performance difference between the four conversion methods for large char arrays?
ANSWER
Yes. new String(chars) and String.valueOf(chars) each make exactly one copy of the array. StringBuilder.append(chars) makes two copies: one into its internal buffer and another when toString() is called. String.copyValueOf() is identical to valueOf. For large arrays (millions of characters), the StringBuilder approach can double memory usage and increase GC pressure. Always prefer the constructor or valueOf for direct conversion.
Q05 of 05JUNIOR
How do you convert only a portion of a char array to a String?
ANSWER
Use the constructor new String(chars, offset, count). This creates a String using only count characters starting at offset. It does not require extracting a subarray first. Always validate that offset + count ≤ chars.length to avoid StringIndexOutOfBoundsException. This is more efficient than copying the entire array and calling substring.
01
What are the different ways to convert a char array to a String in Java?
JUNIOR
02
Why do some security APIs return char[] instead of String for passwords?
SENIOR
03
What happens if you pass null to String.valueOf(char[]) vs new String(char[])?
SENIOR
04
Is there any performance difference between the four conversion methods for large char arrays?
SENIOR
05
How do you convert only a portion of a char array to a String?
JUNIOR
FAQ · 5 QUESTIONS
Frequently Asked Questions
01
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).
Was this helpful?
02
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.
Was this helpful?
03
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.
Was this helpful?
04
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.
Was this helpful?
05
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.