---
title: "Apex String methods worth knowing"
url: "https://trailassembly.com/apex-string-methods/"
topic: "Apex and Development"
published: "2026-09-18T06:48:06+00:00"
updated: "2026-10-06T13:57:51+00:00"
---

# Apex String methods worth knowing

**Short answer:** most Apex string bugs come from three places. Using `isEmpty` when you meant `isBlank`, calling `split` without a limit and silently losing trailing empty values, and concatenating user input into dynamic SOQL without `escapeSingleQuotes`. The `String` class has about ninety methods. You need roughly a dozen, and knowing them removes a surprising amount of hand written loop code.

## isBlank against isEmpty

They are not synonyms.

- `String.isEmpty(s)` is true when `s` is null or has zero length.

- `String.isBlank(s)` is true when `s` is null, zero length, or contains only whitespace.

A field that a user filled with a single space passes `isEmpty` and fails `isBlank`. Since a space is almost never meaningful data, `isBlank` is the one you want in nearly every validation. Use `isNotBlank` for the positive form rather than negating.

```apex
if (String.isBlank(acc.BillingCountry)) {
    acc.BillingCountry.addError('Country is required');
}
```

Both are static methods, so they handle null without a guard. That is the point of them. `s.isEmpty()` as an instance call throws a null pointer exception on a null string, which defeats the purpose.

## escapeSingleQuotes, and what it does not do

`String.escapeSingleQuotes(s)` puts a backslash in front of every single quote, so a value cannot close the literal it sits inside and start a new clause. Apply it to every user supplied value that ends up inside quotes in a dynamic query.

```apex
String term = String.escapeSingleQuotes(searchInput);
String soql = 'SELECT Id, Name FROM Account WHERE Name LIKE \'%' + term + '%\' WITH USER_MODE';
List<Account> results = Database.query(soql);
```

It protects string literals and nothing else. An object name, a field name, an `ORDER BY` clause or a `LIMIT` value built from input is still wide open, because none of those are quoted. Those need an allow list checked against describe information. Better still, use bind variables through `Database.queryWithBinds` and skip the escaping entirely.

## split, and the trailing empties

This is the one that quietly corrupts CSV parsing. `split(regex)` discards trailing empty strings. A row ending with three empty columns comes back short, and your column indexes shift.

```apex
String row = 'Acme,London,,,';

row.split(',');       // 2 elements
row.split(',', -1);   // 5 elements, empties kept
row.split(',', 2);    // 2 elements: 'Acme' and 'London,,,'
```

A negative limit applies the pattern as many times as possible and keeps every trailing empty. A positive limit applies the pattern at most limit minus one times, which is how you split on the first delimiter only and keep the rest intact. Use the negative form for delimited data and the positive form for “key: value” style parsing.

The first argument is a [regular expression](https://trailassembly.com/regex-too-complicated/), so splitting on a dot or a pipe needs escaping: `split('\\|')`. Use `splitByCharacterType` or `remove` when you only want literal handling.

## join

`String.join(iterable, separator)` takes any List or Set and gives you a delimited string. It is the correct way to build a field list, a bound IN clause for logging, or a readable error message.

```apex
String fieldList = String.join(FIELDS, ', ');
String failures = String.join(failedNames, '; ');
```

Building the same thing with a loop and a trailing comma trim is three lines longer and has an off by one bug in it about a third of the time.

## abbreviate

Custom fields have hard length limits, and a long text area caps at 131,072 characters. Writing past the limit throws `STRING_TOO_LONG` and, in a trigger, takes the whole save down. `abbreviate(maxWidth)` truncates and appends an ellipsis, so the result is never longer than the width you gave it.

```apex
log.Stack_Trace__c = e.getStackTraceString().abbreviate(32000);
```

It throws if `maxWidth` is under 4, since there would be no room for the ellipsis. For an exact cut with no ellipsis use `left(n)` instead.

## A few more worth having

- `String.format('{0} of {1}', new List<String>{a, b})` for messages, especially with custom labels.

- `escapeHtml4()` before putting anything user supplied into markup.

- `containsIgnoreCase()` and `equalsIgnoreCase()`, because Apex comparison of strings with `==` is case insensitive but `contains()` is not.

- `removeStart()` and `removeEnd()` for prefixes, which are clearer than substring arithmetic.

- `String.valueOf()` on anything, including sObjects and collections, for logging.

Verified in a Summer ’26 scratch org on 14 September 2026: for a two-space string, `String.isBlank()` returns **true** and `String.isEmpty()` returns **false**. That difference is the whole reason to prefer `isBlank` on user input. `String.escapeSingleQuotes('O\'Brien')` returns `O\\'Brien`.

## Frequently asked

**Is `==` on strings case sensitive in Apex?** No. String comparison with `==` ignores case, which surprises people coming from Java. Map keys and `contains()` are case sensitive, so the language is inconsistent here and you have to remember which is which.

**Does string concatenation in a loop hurt?** It costs heap and CPU, and building a large string by repeated concatenation across thousands of iterations is a real way to run out of the 6 MB synchronous heap. Collect into a List and call `String.join` once.

**What about null in concatenation?** A null String concatenated with `+` produces the literal text “null” in the result rather than throwing. Check first if that would reach a user.

---

Source: https://trailassembly.com/apex-string-methods/
Trail Assembly — independent Salesforce write-ups.
