Como todo programador sabe String é um tipo de dado/estrutura/objeto que é utilizada para armazenar texto dentro de um programa sendo composta por uma série de caracteres, mas como estamos falando de Java vamos nos ater a String como um objeto, na verdade um objeto muito singular, pois ela se comporta um pouco diferente.

Começamos com a criação, como todo objeto ele pode ser criado utilizando a instrução new.

String s = new String();

O código acima criará uma String vazia, então vamos criar um com algum conteúdo.

String s = new String("Algum texto");

Ok, mas a sintaxe acima ainda é diferente de como você deve estar acostumado a utilizar, como utilizamos objetos String o tempo todo existe um modo mais fácil de fazer isso!

String s = "Algum texto";

A pergunta que não quer calar “qual a diferença?”, a String criada com a instrução new cria um novo objeto na memória como estamos acostumados e as criadas quando escrevemos um literal String(simplesmente colocando caracteres entre aspas dupla) utilizam um recurso para otimização de String muito utilizadas camada de Pool de Strings que reaproveita um objeto na memória.

Pool de Strings
String s1 = "João"; 
String s2 = "João"; 
String s3 = new String("João"); 
System.out.println("s1 == s2 : "+( s1 == s2 ) );//true 
System.out.println("s1 == s3 : "+( s1 == s3 ) );//false

Quando comparamos s1 e s2 obtemos o resultado true(verdadeiro) pois os dois objetos String estão no mesmo endereço de memória pois o objeto “João” foi criado no Pool de String e quando fazemos a atribuição s2 = "João" objeto atribuído à s2 é o mesmo que está em s1, já quando comparamos s1 e s3 obtemos o resultado false(falso) já que quando criamos o objeto s3 o fizemos com a instrução new então não foi utilizado o Pool de String e sim criado um novo objeto em outra posição da memória, lembrando que quando utilizamos o operador de comparação "==" com variáveis de referência a objetos estamos comparando na verdade os endereços de memória e não os valores contidos lá. Por isso não devemos utiliza o operador de comparação "==" para comparar Strings e sim o método equals fornecido pela classe String que compara valor dela.

Scanner scan = new Scanner(System.in);
String s = scan.nextLine();

System.out.println("s == \"texto\" : " + ( s == "texto") );//false
System.out.println("s.equals(\"texto\") : " +  s.equals("texto")  );//true

Quando comparamos com o operador "==" ele irá comparar o endereço de “s” que foi criado dentro do método nextLine com o endereço da String que está no Pool de String “texto”. Já na comparação feita com o método equals ele irá comprar o caracteres contido em cada uma das Strings.

Imutabilidade

Algo que muitos programadores iniciantes não sabem é que os objetos String são imutáveis, ou seja, depois de serem criados eles não podem ser alterados. Quando concatenamos(juntamos) suas Strings na verdade estamos criando um novo objeto em um outro local da memória.

concatenação de strings
Concatenando Strings
String s1 = "João";
s1 = s1 + " Antonio";

No código acima criamos um objeto no Pool(ou pegamos um já armazenado não sabemos) e colocamos sua referência em s1, na linha 2 criamos um objeto no Pool “Antonio”, não armazenamos esta referência e sim utilizamos para concatenar com o objeto referenciado por s1, criando um novo objeto String("João Antonio") que será armazenado em algum outro lugar na memória e sua referência será armazenada em s1, fazendo assim perdemos a referência a String “João”, mas o objeto não foi destruído ou modificado.

Métodos comuns da classe String

Os métodos a seguir são alguns utilizados da classe String.

charAt(int index): char Retorna o caractere localizado no índice especificado, lembrando que o primeiro caractere tem índice 0.

String s1 = "Rodrigo"; 
System.out.println( s1.charAt(2) );//retorna o char 'd'

concat(String s): String Anexa uma String passada por parâmetro ao final da String que chamou o método, os operadores "+" e "+=" executam uma funções semelhantes.

String s2 = "Boteco"; 
System.out.println( s2.concat(" Digital") ); //retorna "Boteco Digital"

equals(): boolean Compara a String que chamou o método com outra passada por parâmetro diferenciando maiúsculas de minusculas.

String s3 = "João"; 
System.out.println( s3.equals( "João" ) );//retorna true 
System.out.println( s3.equals( "Pedro" ) );//retorna false
System.out.println( s3.equals( "joão" ) );//retorna false (j em minusculo)

equalsIgnoreCase(): boolean Compara a String que chamou o método com outra passada por parâmetro “não” diferenciando maiúsculas de minusculas.

String s4 = "João"; 
System.out.println( s4.equalsIgnoreCase( "João" ) );//retorna true
System.out.println( s4.equalsIgnoreCase( "Pedro" ) );//retorna false
System.out.println( s4.equalsIgnoreCase( "joão" ) );//retorna true (j em minusculo)

length(): int Retorna o número de caracteres da String que chamou o método.

String s5 = "Java"; 
System.out.println( s5.length() );// retorna 4

replace(char oldChar, char newChar): String Substitui as ocorrências de um caractere(oldChar) por um outro caractere(newChar) na String que chamou o método.

String s6 = "rodrigo"; 
s6 = s6.replace("o", "*");//retorna r*drig* 
System.out.println(s6);

subtring(int index): String , subtring(int index, int end): String Retorna uma parte da String que chamou método, o primeiro parâmetro representa a posição a partir da qual a substring começa(iniciando em 0) e terminará no final. Se houver um segundo parâmetro a substring terminará nesta posição incluindo este caractere nela.

String s7 = "0123456789"; 
System.out.println(s7.substring(5)); // 56789 
System.out.println(s7.substring(5,7)); // 56

toLowerCase(String s): String Retorna uma String com todos os caracteres em minuscula.

String s8 = "AbCdEfGh"; 
System.out.println( s8.toLowerCase() );// retorna abcdefgh

toLowerCase(String s): String Retorna uma String com todos os caracteres em maiúscula.

String s9 = "AbCdEfGh"; 
System.out.println( s8.toUpperCase() );// retorna ABCDEFGH

split(String regex) Retorna um array de String sendo que cada um de seus elementos deste array é um pedaço da String que chamou o método dividida pelo caractere passo por parâmetro(também pode ser uma expressão regular)

String s10 = "teste1;teste2;teste3"; 
String[] array = s10.split(";"); 
System.out.println( array[0] ); // teste1 
System.out.println( array[1] ); // teste2 
System.out.println( array[2] ); // teste3

trim() Retorna uma String que é a String utilizada para chamar o método mas sem os espaços em brando iniciais ou finais.

String s11 = " XXXX "; 
System.out.println( "|"+s11.trim() +"|"); // retorna |XXXX|

StringBuilder

Levando o conceito de imutabilidade em consideração, quando temos que modificar uma String muitas vezes iremos criar muitos objetos String que irão ser abandonados no Pool de String como no exemplo abaixo.

String s = "";
for(int i = 0 ; i < 10 ; i++){
	s = s + i;
}

Se você notar foram criadas na memória mais ou menos as seguintes String: “0” , “01” , “012”, “0123”, “01234”, 012345″, “0123456”, “01234567”, “012345678”, “0123456789”. Sendo que a maioria delas será abandonado no Pool desperdiçando memória.

Para corrigir este problema exite a classe StringBuilder que permitir modificar uma String sem deixar objeto perdidos no Pool, segue um exemplo igual ao anterior utilizando a classe StringBuilder.

StringBuilder sb = new StringBuilder();
for(int i = 0 ; i < 10 ; i++){
	sb.append(i);
}
System.out.println( sb.toString() );

Alguns métodos de StringBuilder:

append(valor) Este método adiciona o parâmetro(podendo ser int, char, double, float, long, etc) ao final do objeto StringBuilder que chamou o método.

delete(int star, int end) Este método atualiza o objeto StringBuilder retirando dele uma substring, sendo que o primeiro parâmetro é o inicio da substring e o segundo o final, sendo este caractere removido também.

StringBuilder sb2 = new StringBuilder("0123456789"); 
sb2.delete(3, 5); // atualiza para 01256789

insert(int offset, String s) Este método insere um valor dentro do objeto StringBuilder que chamou, sendo o primeiro parâmetro a posição em que o valor vai ser inserido e o segundo próprio valor(podendo ser int, char, double, float, long, etc) a ser inserido.

StringBuilder sb3 = new StringBuilder("0123456789");
sb3.insert(5, "XXX"); // atualiza para 01234XXX56789

reverse() Este método inverte os caracteres dentro do objeto StringBuilder, deixando o primeiro caractere como último e o último como primeiro.

StringBuilder sb4 = new StringBuilder("Rodrigo"); 
sb4.reverse(); // atualiza para "ogirdoR"

toString() Este método retorna o valor do objeto StringBuilder ou seja a String que estávamos construindo.

Lembrando que os métodos append() , delete(), insert(), reverse() retornam o próprio objeto que o chamou permitindo encadear suas chamadas.

StringBuilder sb5 = new StringBuilder(); 
sb5.append("Rodrigo").append("Aramburu").reverse();// atualiza para "urubmarAogirdoR"

Bom era isso a classe String tem vários métodos que valem a pena ver consulte a documentação para mais detalhes.

Classe String

Class StringBuilder