Java Métodos de String

Na aula Strings você criou textos, mediu com length() e comparou com equals. Agora vai conhecer as "ferramentas" que toda String traz: deixar em maiúsculas, pegar uma letra, procurar um pedaço, cortar, trocar, limpar espaços, quebrar em partes. São os métodos que você vai usar em praticamente todo programa.

Você vai aprender
  • Como as posições (índices) de uma String são contadas, a partir de zero.
  • toUpperCase/toLowerCase, charAt, indexOf/lastIndexOf e substring.
  • contains, startsWith/endsWith, replace, trim/strip, isEmpty/isBlank.
  • split, String.join, repeat e chars.
  • Como encadear métodos (texto.strip().toLowerCase()).
  • O que a StringIndexOutOfBoundsException quer dizer e como evitá-la.
Lembre: Strings são imutáveis

Nenhum método desta aula altera a String original. Os que "transformam" o texto devolvem uma String nova; para usar o resultado, guarde-o numa variável ou imprima direto. Veja a prova em Strings.


Antes de tudo: as posições começam em 0

Cada caractere de uma String tem uma posição, chamada índice (index). Em Java, como na maioria das linguagens, a contagem começa em zero: o primeiro caractere está na posição 0, o segundo na 1, e assim por diante. O último está na posição length() - 1.

Índice012345
"banana"banana

Pense numa fila de seis cadeiras numeradas a partir do zero: a sexta cadeira tem o número 5. Não existe cadeira número 6. Guarde essa imagem: quase todos os erros desta aula vêm de esquecer que a última posição é o tamanho menos um.


Maiúsculas e minúsculas: toUpperCase e toLowerCase

toUpperCase() (upper case = "caixa alta", maiúsculas) devolve uma cópia com todas as letras em maiúsculas. toLowerCase() (lower case = "caixa baixa") faz o contrário. Números, espaços e pontuação ficam como estão.

public class Main {
    public static void main(String[] args) {
        String nome = "Ana Souza";
        System.out.println(nome.toUpperCase());
        System.out.println(nome.toLowerCase());
        System.out.println("Maçã e Pão".toUpperCase());
        System.out.println(nome);
    }
}
$ java Main.java
ANA SOUZA
ana souza
MAÇÃ E PÃO      « acentos e ç também viram maiúsculos
Ana Souza       « a original não mudou

Uso típico: padronizar um texto antes de comparar ou guardar (por exemplo, e-mails sempre em minúsculas).


Um caractere: charAt

charAt(índice) (char at = "caractere na posição") devolve o caractere que está naquela posição. O resultado é um char, não uma String.

public class Main {
    public static void main(String[] args) {
        String fruta = "banana";
        System.out.println(fruta.charAt(0));
        System.out.println(fruta.charAt(1));
        System.out.println(fruta.charAt(5));
        System.out.println(fruta.charAt(fruta.length() - 1));
        char primeira = fruta.charAt(0);
        System.out.println(primeira);
    }
}
$ java Main.java
b      « posição 0: o primeiro
a      « posição 1: o segundo
a      « posição 5: o último de "banana"
a      « length() - 1 = 5: o jeito de pegar o último em qualquer String
b
fruta.charAt(0)
O primeiro caractere. Sempre 0, nunca 1.
fruta.length() - 1
O índice do último caractere. Funciona para Strings de qualquer tamanho, sem você precisar contar.
char primeira
O resultado de charAt é um char, então a variável que o recebe tem de ser char.

Posição que não existe: StringIndexOutOfBoundsException

Pedir uma posição fora da String derruba o programa. Este código tem um erro de propósito: "banana" tem posições de 0 a 5, e o código pede a 6.

public class Main {
    public static void main(String[] args) {
        String fruta = "banana";
        System.out.println(fruta.charAt(6));
    }
}
$ java Main.java
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: Index 6 out of bounds for length 6
	at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:55)
	at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:52)
	at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:213)
	at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:210)
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:106)
	at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:302)
	at java.base/java.lang.String.checkIndex(String.java:4904)
	at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:45)
	at java.base/java.lang.String.charAt(String.java:1624)
	at Main.main(Main.java:4)

Assusta, mas é simples de ler:

StringIndexOutOfBoundsException
"Exceção de índice de String fora dos limites".
Index 6 out of bounds for length 6
"Índice 6 fora dos limites para o tamanho 6". Com tamanho 6, os índices válidos vão de 0 a 5.
at java.base/…
As linhas com java.base/ são o caminho que o erro fez dentro da biblioteca do Java, até chegar ao charAt. Você não precisa lê-las.
at Main.main(Main.java:4)
A linha que interessa: a primeira com o nome do seu arquivo. O problema está na linha 4 do Main.java.

Procurar: indexOf e lastIndexOf

indexOf ("índice de") procura um caractere ou um pedaço de texto e devolve a posição onde ele aparece pela primeira vez. lastIndexOf devolve a posição da última vez. Se não achar, os dois devolvem -1 (uma posição impossível, que quer dizer "não está aí").

public class Main {
    public static void main(String[] args) {
        String fruta = "banana";
        System.out.println(fruta.indexOf('a'));
        System.out.println(fruta.lastIndexOf('a'));
        System.out.println(fruta.indexOf("na"));
        System.out.println(fruta.lastIndexOf("na"));
        System.out.println(fruta.indexOf('a', 2));
        System.out.println(fruta.indexOf("uva"));
        System.out.println(fruta.indexOf('B'));
    }
}
$ java Main.java
1      « o primeiro 'a' está na posição 1
5      « o último 'a' está na posição 5
2      « "na" começa na posição 2
4      « o último "na" começa na posição 4
3      « procurando 'a' a partir da posição 2: achou na 3
-1     « "uva" não aparece
-1     « 'B' maiúsculo não aparece: a busca diferencia maiúsculas
ChamadaO que faz
s.indexOf('a')Posição do primeiro caractere 'a'
s.indexOf("na")Posição onde começa o primeiro "na"
s.indexOf('a', 2)Mesma busca, mas começando na posição 2
s.lastIndexOf('a')Posição do último 'a'

O indexOf brilha junto com o próximo método, o substring: um acha onde cortar, o outro corta.


Cortar um pedaço: substring

substring ("sub-texto") devolve um pedaço da String. Tem duas formas:

FormaDevolve
s.substring(início)Do início até o fim
s.substring(início, fim)Do início até antes do fim (o fim não entra)

A regra do "fim não entra" confunde no começo, mas tem uma vantagem: o tamanho do pedaço é sempre fim - início. Veja com "Ana Souza":

Índice012345678
"Ana Souza"Ana(espaço)Souza
public class Main {
    public static void main(String[] args) {
        String nome = "Ana Souza";
        System.out.println(nome.substring(4));
        System.out.println(nome.substring(0, 3));
        System.out.println(nome.substring(1, 3));
        System.out.println(nome.substring(4, 4).isEmpty());
        int espaco = nome.indexOf(' ');
        String primeiro = nome.substring(0, espaco);
        String sobrenome = nome.substring(espaco + 1);
        System.out.println(primeiro);
        System.out.println(sobrenome);
    }
}
$ java Main.java
Souza   « da posição 4 até o fim
Ana     « posições 0, 1 e 2 (a 3 não entra): 3 - 0 = 3 caracteres
na      « posições 1 e 2: 3 - 1 = 2 caracteres
true    « início igual ao fim: pedaço vazio
Ana     « do começo até antes do espaço
Souza   « depois do espaço até o fim
int espaco = nome.indexOf(' ');
Acha a posição do espaço (3) e guarda numa variável int.
nome.substring(0, espaco)
Do começo até antes do espaço: Ana. O espaço não entra, graças à regra do fim.
nome.substring(espaco + 1)
Do caractere depois do espaço até o fim: Souza.

Esse par indexOf + substring resolve muitos problemas reais: separar nome e sobrenome, pegar o usuário de um e-mail (antes do @), a extensão de um arquivo (depois do último .).

Missão: mostre só o sobrenome de "Bruno Lima", usando indexOf para achar o espaço e substring para cortar. Não escreva o número 6 à mão: o programa deve funcionar com qualquer nome de duas palavras.

public class Main {
    public static void main(String[] args) {
        String nome = "Bruno Lima";
        // ache o espaço e mostre o que vem depois dele
    }
}
public class Main {
    public static void main(String[] args) {
        String nome = "Bruno Lima";
        int espaco = nome.indexOf(' ');
        String sobrenome = nome.substring(espaco + 1);
        System.out.println(sobrenome);
    }
}

Índices errados no substring

Se o fim passa do tamanho, ou se o início é maior que o fim, vem a mesma exceção do charAt, com outra mensagem. Este código tem um erro de propósito.

public class Main {
    public static void main(String[] args) {
        String nome = "Ana Souza";
        System.out.println(nome.substring(4, 20));
    }
}
$ java Main.java
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: Range [4, 20) out of bounds for length 9
	at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:55)
	at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:52)
	at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:213)
	at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:210)
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromToIndex(Preconditions.java:112)
	at java.base/jdk.internal.util.Preconditions.checkFromToIndex(Preconditions.java:349)
	at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4937)
	at java.base/java.lang.String.substring(String.java:2899)
	at Main.main(Main.java:4)

Range [4, 20) out of bounds for length 9: "o intervalo de 4 até 20 está fora dos limites para o tamanho 9". Repare na notação [4, 20): o colchete [ quer dizer "o 4 entra" e o parêntese ) quer dizer "o 20 não entra", exatamente a regra do substring. Com substring(2, 1) em "Ana", a mensagem seria Range [2, 1) out of bounds for length 3.


Perguntas de sim ou não: contains, startsWith, endsWith

Estes três devolvem um boolean (true ou false):

  • contains(x): "contém x em algum lugar?"
  • startsWith(x): "começa com x?" (starts with)
  • endsWith(x): "termina com x?" (ends with)
public class Main {
    public static void main(String[] args) {
        String arquivo = "relatorio-2026.pdf";
        System.out.println(arquivo.contains("2026"));
        System.out.println(arquivo.contains("2025"));
        System.out.println(arquivo.startsWith("relatorio"));
        System.out.println(arquivo.endsWith(".pdf"));
        System.out.println(arquivo.endsWith(".PDF"));
        System.out.println(arquivo.contains(""));
    }
}
$ java Main.java
true
false
true
true
false    « diferencia maiúsculas: ".PDF" não é ".pdf"
true     « toda String "contém" a String vazia

Para ignorar a caixa, padronize antes: arquivo.toLowerCase().endsWith(".pdf"). Se você precisa da posição, use indexOf; se só quer saber se está lá, contains deixa o código mais claro.


Trocar: replace

replace(velho, novo) ("substituir") devolve uma cópia em que todas as ocorrências de velho viram novo. Funciona com caracteres ou com pedaços de texto.

public class Main {
    public static void main(String[] args) {
        String fruta = "banana";
        System.out.println(fruta.replace('a', 'o'));
        System.out.println(fruta.replace("na", "NA"));
        System.out.println(fruta.replace("x", "y"));
        String data = "16/10/2026";
        System.out.println(data.replace("/", "-"));
        System.out.println("R$ 1.250,90".replace(".", "").replace(",", "."));
        System.out.println(fruta);
    }
}
$ java Main.java
bonono       « todos os 'a' viraram 'o'
baNANA       « os dois "na" foram trocados
banana       « não achou "x": devolve igual, sem erro
16-10-2026
R$ 1250.90   « dois replace seguidos: tira o ponto, troca a vírgula
banana       « a original continua igual

Para trocar texto usando padrões (como "qualquer dígito"), existem replaceAll e replaceFirst, que usam expressões regulares; veja Expressões Regulares.


Limpar espaços: trim e strip

Texto digitado por pessoas vem com espaços sobrando nas pontas. strip() ("descascar") e trim() ("aparar") devolvem uma cópia sem os espaços do começo e do fim. Os espaços do meio ficam.

public class Main {
    public static void main(String[] args) {
        String digitado = "   Ana   ";
        System.out.println("[" + digitado + "]");
        System.out.println("[" + digitado.trim() + "]");
        System.out.println("[" + digitado.strip() + "]");
        System.out.println("[" + digitado.stripLeading() + "]");
        System.out.println("[" + digitado.stripTrailing() + "]");
        String comEspacoLargo = "\u2003Ana\u2003";
        System.out.println("[" + comEspacoLargo.trim() + "]");
        System.out.println("[" + comEspacoLargo.strip() + "]");
    }
}
$ java Main.java
[   Ana   ]
[Ana]
[Ana]
[Ana   ]    « stripLeading: só o começo (leading = "da frente")
[   Ana]    « stripTrailing: só o fim (trailing = "de trás")
[ Ana ]     « trim NÃO tirou o "espaço largo" do Unicode
[Ana]       « strip tirou

Qual a diferença? O trim() é antigo e só conhece os espaços "clássicos" (o espaço comum, o tab, a quebra de linha). O strip(), criado no Java 11, conhece todos os tipos de espaço do Unicode, como o "espaço largo" \u2003 que aparece em textos copiados de sites e documentos (o \u2003 no código é um jeito de escrever esse caractere; veja Caracteres Especiais). Prefira strip().


Vazia ou em branco: isEmpty e isBlank

isEmpty() ("está vazia?") só dá true se a String tem zero caracteres. isBlank() ("está em branco?", Java 11+) dá true também quando só há espaços. Para validar um campo digitado, isBlank() é o que você quer.

public class Main {
    public static void main(String[] args) {
        String vazia = "";
        String espacos = "   ";
        String nome = "Ana";
        System.out.println(vazia.isEmpty() + " " + vazia.isBlank());
        System.out.println(espacos.isEmpty() + " " + espacos.isBlank());
        System.out.println(nome.isEmpty() + " " + nome.isBlank());
    }
}
$ java Main.java
true true      « "": vazia e em branco
false true     « "   ": não é vazia (tem 3 caracteres), mas está em branco
false false    « "Ana": nem uma coisa nem outra

Os dois quebram com NullPointerException se a variável for null; veja Strings.


Quebrar em partes: split

split(separador) ("dividir") corta a String em vários pedaços, onde aparecer o separador, e devolve todos eles num array: uma fileira de Strings numeradas a partir do 0. Arrays têm aula própria (Arrays); aqui basta saber três coisas: o tipo é String[], o pedaço número 0 é partes[0] e a quantidade de pedaços é partes.length (sem parênteses).

public class Main {
    public static void main(String[] args) {
        String linha = "maçã,banana,uva";
        String[] frutas = linha.split(",");
        System.out.println(frutas.length);
        System.out.println(frutas[0]);
        System.out.println(frutas[1]);
        System.out.println(frutas[2]);
    }
}
$ java Main.java
3
maçã
banana
uva
String[] frutas
Uma variável que guarda várias Strings (um array). Os colchetes [] no tipo indicam isso.
linha.split(",")
Corta nas vírgulas. As vírgulas somem; ficam só os pedaços.
frutas.length
Quantos pedaços: 3.
frutas[0]
O primeiro pedaço. De novo, a contagem começa em zero.

Duas armadilhas do split

O separador do split não é um texto comum: é uma expressão regular (um "padrão"). Na maioria dos casos (vírgula, ponto e vírgula, espaço) isso não faz diferença, mas veja:

public class Main {
    public static void main(String[] args) {
        String frase = "Ana  gosta de Java";
        String[] palavras = frase.split(" ");
        System.out.println(palavras.length);
        System.out.println("[" + palavras[1] + "]");
        String[] certas = frase.split(" +");
        System.out.println(certas.length);
        String[] partes = "16.10.2026".split(".");
        System.out.println(partes.length);
        String[] data = "16.10.2026".split("\\.");
        System.out.println(data.length + " " + data[2]);
    }
}
$ java Main.java
5          « 4 palavras, mas 5 pedaços…
[]         « …porque entre os dois espaços havia um pedaço vazio
4          « " +" quer dizer "um ou mais espaços": agora sim, 4
0          « "." no padrão quer dizer "qualquer caractere": sobrou nada!
3 2026     « "\\." quer dizer "um ponto de verdade"
  • Espaços repetidos geram pedaços vazios. Use split(" +") (o + quer dizer "um ou mais") ou, melhor, frase.strip().split("\\s+") (\s = qualquer espaço).
  • Ponto, barra vertical, asterisco e outros símbolos têm significado especial em padrões. Para cortar num ponto, escreva "\\."; num |, "\\|". As duas barras são explicadas em Caracteres Especiais e Expressões Regulares.

Juntar e repetir: String.join e repeat

String.join(separador, partes…) faz o contrário do split: junta vários textos, colocando o separador entre eles. Repare que ele é chamado na própria classe (String.join), não numa variável, porque não "pertence" a nenhum texto em particular. repeat(n) (Java 11+) devolve o texto repetido n vezes.

public class Main {
    public static void main(String[] args) {
        String lista = String.join(", ", "maçã", "banana", "uva");
        System.out.println(lista);
        String[] partes = {"16", "10", "2026"};
        System.out.println(String.join("/", partes));
        System.out.println("=".repeat(20));
        System.out.println("ab".repeat(3));
        System.out.println("[" + "x".repeat(0) + "]");
    }
}
$ java Main.java
maçã, banana, uva       « o separador vai só ENTRE os itens, não no fim
16/10/2026              « também aceita um array
====================    « uma linha de 20 sinais de igual
ababab
[]                      « repetir 0 vezes dá a String vazia

A aula Concatenação compara o join com outras formas de juntar texto.


Os códigos dos caracteres: chars

chars() devolve os caracteres da String como uma sequência de números (o código de cada caractere), num formato chamado stream ("fluxo"). Streams são assunto do Nível 3 (Streams), mas dois usos simples já aparecem no dia a dia: contar e examinar caractere por caractere.

public class Main {
    public static void main(String[] args) {
        String fruta = "banana";
        System.out.println(fruta.chars().count());
        System.out.println(fruta.chars().filter(c -> c == 'a').count());
        fruta.chars().forEach(c -> System.out.println(c + " " + (char) c));
    }
}
$ java Main.java
6          « quantos caracteres (o mesmo que length())
3          « quantos são 'a'
98 b       « cada caractere: o código e, convertido de volta, a letra
97 a
110 n
97 a
110 n
97 a
fruta.chars()
A sequência de códigos: 98, 97, 110, 97, 110, 97.
.filter(c -> c == 'a')
"Filtre": deixa passar só os códigos iguais ao de 'a'. O c -> … é uma lambda, uma função curtinha escrita no meio do código; você vai aprendê-las em Lambdas.
.count()
Conta quantos passaram.
(char) c
Converte o número de volta em caractere (um cast; veja Conversão de Tipos).

Por enquanto, guarde o uso pronto: texto.chars().filter(c -> c == 'x').count() conta quantas vezes o caractere x aparece. Para percorrer letra por letra, a forma mais comum para iniciantes é um for com charAt(i).


Encadear métodos

Como cada método devolve uma String nova, você pode chamar outro método direto no resultado, com mais um ponto. O Java executa da esquerda para a direita:

public class Main {
    public static void main(String[] args) {
        String digitado = "  ANA souza ";
        String limpo = digitado.strip().toLowerCase();
        System.out.println("[" + limpo + "]");
        String email = "  Ana.Souza@Exemplo.com ";
        System.out.println(email.strip().toLowerCase().endsWith("@exemplo.com"));
    }
}
$ java Main.java
[ana souza]
true

Leia digitado.strip().toLowerCase() como uma linha de montagem: " ANA souza " → strip() → "ANA souza" → toLowerCase() → "ana souza". Encadear é ótimo, mas com moderação: se a linha ficar comprida demais, guarde os passos em variáveis com nomes claros.


Tabela: os métodos desta aula

MétodoDevolveExemplo → resultado
length()int"banana".length() → 6
toUpperCase()String"Ana".toUpperCase() → "ANA"
toLowerCase()String"Ana".toLowerCase() → "ana"
charAt(i)char"banana".charAt(0) → 'b'
indexOf(x)int (ou -1)"banana".indexOf("na") → 2
indexOf(x, desde)int"banana".indexOf('a', 2) → 3
lastIndexOf(x)int"banana".lastIndexOf('a') → 5
substring(i)String"Ana Souza".substring(4) → "Souza"
substring(i, f)String (o f não entra)"Ana Souza".substring(0, 3) → "Ana"
contains(x)boolean"banana".contains("nan") → true
startsWith(x)boolean"banana".startsWith("ba") → true
endsWith(x)boolean"foto.png".endsWith(".png") → true
replace(a, b)String"banana".replace('a', 'o') → "bonono"
strip() / trim()String" Ana ".strip() → "Ana"
stripLeading() / stripTrailing()StringSó o começo / só o fim
isEmpty()boolean" ".isEmpty() → false
isBlank()boolean" ".isBlank() → true
split(sep)String[]"a,b,c".split(",") → 3 pedaços
String.join(sep, …)StringString.join("-", "a", "b") → "a-b"
repeat(n)String"ab".repeat(3) → "ababab"
chars()Stream de códigos"banana".chars().count() → 6

Comparações (equals, equalsIgnoreCase, compareTo) estão em Strings. A lista completa, com todos os métodos da classe, está em Referência: Métodos de String.

Brinque com o código: troque a frase e experimente outros métodos da tabela. O que frase.indexOf("Java") devolve? E frase.replace(" ", "")?

public class Main {
    public static void main(String[] args) {
        String frase = "Eu estou aprendendo Java";
        System.out.println(frase.toUpperCase());
        System.out.println(frase.indexOf("Java"));
        System.out.println(frase.substring(9));
        System.out.println(frase.split(" ").length + " palavras");
    }
}

Quando dá errado

error: cannot find symbol … symbol: variable length

Este código tem um erro de propósito.

public class Main {
    public static void main(String[] args) {
        String nome = "Ana";
        System.out.println(nome.length);
    }
}
$ javac Main.java
Main.java:4: error: cannot find symbol
        System.out.println(nome.length);
                               ^
  symbol:   variable length
  location: variable nome of type String
1 error

O que aconteceu: sem parênteses, o javac procurou uma variável chamada length dentro da String (symbol: variable length) e não achou. Em String, length é um método.

Como resolver: nome.length(). (Em arrays é o contrário: frutas.length, sem parênteses.)

error: incompatible types: char cannot be converted to String

Este código tem um erro de propósito.

public class Main {
    public static void main(String[] args) {
        String nome = "Ana";
        String inicial = nome.charAt(0);
        System.out.println(inicial);
    }
}
$ javac Main.java
Main.java:4: error: incompatible types: char cannot be converted to String
        String inicial = nome.charAt(0);
                                    ^
1 error

O que aconteceu: charAt devolve um char, e o javac não transforma char em String sozinho ("tipos incompatíveis").

Como resolver: guarde num char (char inicial = nome.charAt(0);) ou, se precisa de uma String, use nome.substring(0, 1) ou String.valueOf(nome.charAt(0)).

StringIndexOutOfBoundsException: Index 6 out of bounds for length 6

O que aconteceu: o charAt recebeu uma posição que não existe. Quase sempre é o "mais um": usar length() como índice em vez de length() - 1, ou contar a partir de 1.

Como resolver: lembre que as posições vão de 0 a length() - 1. Se o índice vem de um indexOf, confira antes se ele não é -1.

StringIndexOutOfBoundsException: Range [2, 1) out of bounds for length 3
$ java Main.java
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: Range [2, 1) out of bounds for length 3
	at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:55)
	at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:52)
	at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:213)
	at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:210)
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromToIndex(Preconditions.java:112)
	at java.base/jdk.internal.util.Preconditions.checkFromToIndex(Preconditions.java:349)
	at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4937)
	at java.base/java.lang.String.substring(String.java:2899)
	at Main.main(Main.java:4)

O que aconteceu: foi um "Ana".substring(2, 1): o início (2) ficou depois do fim (1). Também acontece quando o fim passa do tamanho, ou quando um indexOf devolveu -1 (não achou) e esse -1 foi usado como índice.

Como resolver: confira os dois números. O início tem de ser >= 0, o fim <= length() e o início <= fim.

O split devolveu pedaços vazios ou nenhum pedaço

O que aconteceu: o separador é um padrão. Espaços repetidos criam pedaços vazios; "." quer dizer "qualquer caractere" e "come" o texto inteiro (0 pedaços).

Como resolver: para espaços, split("\\s+") depois de um strip(); para símbolos, escape com \\: split("\\."), split("\\|").


Exercícios

Missão final: o e-mail foi digitado com espaços e maiúsculas. Mostre o e-mail limpo (sem espaços nas pontas e todo em minúsculas) e, na linha de baixo, Usuário: seguido da parte antes do @. A saída deve ser:
ana.souza@exemplo.com
Usuário: ana.souza

public class Main {
    public static void main(String[] args) {
        String email = "  Ana.Souza@Exemplo.COM  ";
        // limpe, mostre, e depois mostre o usuário
    }
}
public class Main {
    public static void main(String[] args) {
        String email = "  Ana.Souza@Exemplo.COM  ";
        String limpo = email.strip().toLowerCase();
        System.out.println(limpo);
        String usuario = limpo.substring(0, limpo.indexOf('@'));
        System.out.println("Usuário: " + usuario);
    }
}

Missão extra: a lista de compras veio separada por ponto e vírgula. Use split para contar os itens (mostre 4 itens), String.join para mostrá-los separados por " | " e repeat para desenhar uma linha de 10 traços.

public class Main {
    public static void main(String[] args) {
        String compras = "arroz;feijão;café;leite";
        // escreva aqui
    }
}
public class Main {
    public static void main(String[] args) {
        String compras = "arroz;feijão;café;leite";
        String[] itens = compras.split(";");
        System.out.println(itens.length + " itens");
        System.out.println(String.join(" | ", itens));
        System.out.println("-".repeat(10));
    }
}

1. A primeira letra

Complete para guardar a primeira letra de nome:

char inicial = nome.;

2. Só o começo

Complete para pegar os 3 primeiros caracteres de "Recife" ("Rec"):

String inicio = "Recife".substring();

3. Campo em branco

Complete para saber se campo está vazio ou só com espaços:

boolean semNada = campo.;

4. O que "programa".indexOf("r") devolve?

5. O que "Java".substring(1, 3) devolve?

6. Depois de String s = "oi"; s.replace("oi", "olá");, quanto vale s?

7. Qual é a diferença entre trim() e strip()?


Resumo

  • As posições começam em 0; a última é length() - 1. Posição inválida dá StringIndexOutOfBoundsException; leia a linha com o nome do seu arquivo no fim.
  • charAt pega um char; indexOf/lastIndexOf acham posições (-1 = não achou); substring(início, fim) corta, e o fim não entra.
  • contains, startsWith, endsWith, isEmpty, isBlank respondem true/false.
  • toUpperCase, toLowerCase, replace, strip, repeat devolvem uma String nova; a original não muda.
  • split quebra em um array (cuidado: o separador é um padrão); String.join junta.
  • Métodos podem ser encadeados: texto.strip().toLowerCase().