Fator e Terminologia

Como você provavelmente sabe, o Java usa o UTF-16 para representar o String. O tipo de dados char e a classe Character são baseados na especificação Unicode original, que define caracteres como entidades de 16 bits de largura fixa. O padrão Unicode foi alterado desde então para permitir caracteres cuja representação requeira mais de 16 bits. Portanto, na representação UTF-16, há caracteres (Code Points) representados por 1 (um) e alguns outros caracteres representados por 2 (dois) valores char (Code Points).

Exemplo

Caracter: A “Representação UTF-16” em Java: "\u0041"

Caracter: A Duplo na matematica, letra utilizada na matemática. “Representação UTF-16” em Java: "\uD835\uDD38"

O primeiro é direto; o segundo é um pouco mais interessante; esse caracter único (Code Point) é representado por dois escapes Unicode. Isso significa algumas coisas:

Isso basicamente significa que você provavelmente não quer fazer nenhuma manipulação de caracteres.

A quebra ao inverter String

Neste ponto, você pode ter uma boa idéia do que está errado com esta solução (muito comumente utilizada) para inverter uma String:

static String reverse(String original) {
    String reversed = "";
    for (int i = original.length() - 1;  0 <= i; i--) {
        reversed += original.charAt(i);
    }
    return reversed;
}

Vamos ver isto em ação:

String str = "\uD835\uDD38BC"; // Three characters: A, B, C (4 chars)
System.out.println(str); // prints ABC (A is the double-struck A)
System.out.println(reverse(str)); // prints CB??

Se você executar o método acima, ele produzirá uma String como esta: "CB\uDD38\uD835". C e B estão corretos, mas \uDD38\uD835 é inválido, é por isto que você vÊ ?? quando você imprimi-lo. O método não deveria tê-los invertido; o resultado válido seria "CB\uD835\uDD38" (CBA (A Duplo na matematica)).

Solução

Normalmente, não escrever código para resolver problemas é uma boa ideia:

static String reverse(String original) {
    return new StringBuilder(original).reverse().toString();
}

Se você quiser dar um pequeno passo adiante, aqui está o exemplo utilizando Java 8+:

Function<String, String> reverse = s -> new StringBuilder(s).reverse().toString();

Se você está curioso para saber como isso é implementado, veja o que o método reverse() do StringBuild faz (está na classe AbstractStringBuilder).

Então, quantas manipulações de String incorretas você já viu? Deixe-nos saber nos comentários abaixo!