Uma boa parte dos programas
de computador precisam representar e manipular números e também seqüências
de caracteres. As necessidades numéricas são, grosso modo, atendidas pelos
tipos primitivos tais como long, int e double. As
linguagens de alto-nível têm diferentes abordagens para lidar com a necessidade
de representar e manipular as seqüências de caracteres. Na linguagem Java as
seqüências de caracteres podem ser representadas utilizando arranjos de
caracteres (char arrays) ou então as cadeias de caracteres (strings).
Aqui iremos dar ênfase no suporte de processamento de texto utilizando cadeias
de caracteres.
Quando discutimos arranjos
vimos que eles são objetos especiais. Os arranjos possuem
"inicializadores" e arranjos podem ser indexados. As cadeias de
caracteres são objetos especiais também, mas em um sentido um pouco diferente. Uma
cadeia de caractere é o único objeto que possui uma representação no
código fonte do programa, ou seja uma cadeia de caractere é o único objeto que
tem um literal na linguagem Java.
Considere o seguinte trecho
de programa:
System.out.println(123);
System.out.println(3.1415);
System.out.println("Ola!");
O tipo do literal 123 é int.
o tipo do literal 3.1415 é double. Em Java int e double
são tipos primitivos. O tipo da cadeia de caracteres "Ola!" é String.
Em Java String é uma classe(class). Uma classe é o
elemento da Linguagem Java que define o estado e o comportamento de seus
objetos. Mais a frente na disciplina iremos apresentar o conceito classe com mais detalhes. Neste ponto
da disciplina iremos apenas estudar uma classe especial denominada String (uma
classe pré-definida) cujos objetos ou intâncias contêm cadeias de caracteres. Uma
variável do tipo String é uma variável do tipo referência, ou seja,
contém um endereço de objeto. Uma variável de tipo primitivo P deverá conter um
valor do tipo P, uma variável do tipo referência deverá conter um endereço de
um objeto do tipo referenciado, em particular uma variável do tipo String
deverá conter um endereço de um objeto do tipo String.
O trecho de programa:
String s;
corresponde a definir uma variável com nome s e tipo String, mas o conteúdo de
s não é definido. O trecho de programa:
String s="abcde";
Corresponde a definir uma variável com nome s, tipo String e conteúdo correspondendo
a um endereço de memória onde o compilador montou um objeto contendo a cadeia
de caracteres "abcde".
Uma variável do tipo String
pode ser iniciada ou inicializada de forma semelhante a uma variável de tipo
primitivo. A iniciação é feita com literais do tipo String . Um literal
do tipo String é uma seqüência de caracteres iniciada e terminada com aspas. Podemos
utilizar o operador de concatenação de cadeias (+). O operador de concatenação
permite representar uma cadeia em diferentes linhas do texto do programa. Exemplo
de declaração de variáveis do tipo cadeia de caracteres já inicializadas:
String s1="Universidade ";
String s2="Federal de";
String s3=" Minas "+
"Gerais";
String universidade=s1+s2+s3;
No trecho de programa acima
a variável universidade
é inicializada utilizando o operador de concatenação de cadeias de caracteres. Os
literais de tipo String podem ser usados em comandos de atribuição:
String universidade;
universidade="Universidade Federal de Minas Gerais";
No trecho acima a variável
universidade recebe o endereço de memória onde fica um objeto contendo a cadeia
de caracteres "Universidade Federal de Minas Gerais". A figura abaixo
mostra uma representação gráfica simplificada para o que discutimos acima:

Observe que a organização
do bloco de memória correspondente a uma cadeia de caracteres é diferente da
organização de um bloco de memória correspondente a um arranjo de caracteres. No
caso de arranjo de caracteres cada caractere pode ser indexado através do
operador [] (abre e fecha colchete), o operador de indexação não pode ser usado em cadeias de
caracteres. A manipulação de caracteres individuais de uma cadeia de caracteres
tem que ser feita através de métodos conforme explicado abaixo. Os métodos
da classe String permitem vários tipos de operações sobre os caracteres e substrings (trechos de cadeias de
caracteres).
O conteúdo da variável universidade é um endereço de um bloco de
memória. Este bloco de memória é o objeto que contém a cadeia de caracteres. A
distinção entre a cadeia de caracteres (seqüência de caracteres) e o objeto (o
bloco de memória onde fica a seqüência de caracteres) não é muito reforçada em
alguns textos. Um objeto é também referido como uma instância, o objeto
acima é uma instância da classe String. Não são mostrados todos os
campos do objeto, apenas um campo (este campo com uma referência para a classe
String não é disponível para o programador!) e que contém uma referência
para a classe String. A classe String não é detalhada, mas uma classe pode
definir campos e métodos (methods), dentre outros elementos.
Um método corresponde a um
código que implementa alguma funcionalidade relacionada à classe ou a um
objeto. Os métodos que implementam uma funcionalidade relacionada à classe são
denominados métodos estáticos (static) ou métodos de classe e os
métodos que implementam uma funcionalidade relacionada ao objeto são
denominados não-estáticos ou métodos de instância. Uma chamada
ou invocação de método corresponde a um comando que altera o fluxo de
controle. Uma chamada ou invocação de método desvia o fluxo de controle para o
código de máquina do método, um mecanismo, denominado mecanismo de retorno,
permite, terminada a execução do método, que o controle retorne ao comando
seguinte à chamada. Os métodos estáticos são invocados utilizando o nome da
classe seguido do operador '.' seguido do nome do método. Os métodos
não-estáticos são invocados utilizando uma referência para o objeto seguido do
operador '.', seguido do nome do método. Abaixo são mostrados alguns dos
métodos da classe String:
O trecho de programa abaixo
imprime o comprimento, em termos de números de caracteres, de uma cadeia de
caracteres referenciada pela variável cordao:
String cordao="abcdefg";
System.out.println(cordao.length());
No trecho acima o método não-estático length() da classe String é invocado.
Invocar um método consiste em desviar o fluxo de controle para o código
correspondente a este método, mas antes que ocorra o desvio, de alguma forma, é
salvo o ponto no qual o fluxo de controle deverá retornar. O println() também é um método. A ordem de
invocação é a seguinte: o compilador primeiramente gera código para invocar o
método length(), após a invocação de length() é gerado o código para invocar println().
A execução do código
correspondente a length()determina o número de caracteres e retorna este valor. O valor retornado
por um método tem que ter um tipo. O método length() da classe String é um método do tipo
int, ou seja retorna valores inteiros. Mais a frente na disciplina
iremos ver como definir classes e em particular como definir métodos. Observe
que os objetos correspondentes a arranjos têm um campo length enquanto que objetos
correspondentes a cadeias de caracteres compartilham o método length() da classe String. A execução
de um método pode receber valores através de parâmetros ou argumentos.
Os parâmetros são sempre de um determinado tipo. O método length() não tem parâmetros. O parâmetro do
método println()
deve ser do tipo String, print() e println() serão discutidos mais a frente na
disciplina. O valor retornado pelo método length() é do tipo int, o compilador
cuida para que seja feita a conversão do valor de tipo int para tipo String.
O trecho de programa abaixo
utiliza o método charAt(). Este método possui um parâmetro do tipo int e retorna um
valor do tipo char:
String cordao="abcdefg";
for(int i=0; i<cordao.length(); i++)
System.out.print(cordao.charAt(i));
System.out.println();
O trecho acima tem efeito
semelhante ao seguinte trecho:
String cordao="abcdefg";
System.out.println(cordao);
O método charAt() recebe como parâmetro um valor
inteiro que deve estar entre 0 e length()-1. Se o valor do parâmetro não atender a estes
limites haverá um erro em tempo de execução. O método devolve o (valor do
parâmetro+1)-ésimo caractere da cadeia de caracteres. Se o argumento de
charAt() for menor que 0 ou maior ou igual ao comprimento da cadeia ocorre um
erro de execução: IndexOutOfBoundsException.
Um objeto,
diretamente ou através de sua classe, é constituido de campos(fields),
métodos(methods) e constructores( constructors). Os campos e
métodos já foram parcialmente discutidos: os campos correspondem a posições de
memória e os métodos são códigos que recebem 0 ou mais valores e devolvem 0 ou
1 valor. O trecho de programa abaixo ilustra como que podemos utilizar um
arranjo de caracteres para construir uma cadeia de caracteres
correspondente à seqüência do arranjo:
char[] arranjoCar;
arranjoCar= new char[]{'a', 'b', 'c ', 'd ', 'e ', 'f '};
String cordao;
cordao= new String(arranjoCar);
No trecho acima é utilizado o operador new no contexto de arranjos e no
contexto de classes. A expressão new char[] aloca um bloco de memória contendo um arranjo
do tipo char inicializado conforme especificado acima. A expressão new String irá alocar um objeto do tipo String
e retornar o endereço do bloco de memória onde foi alocado este objeto. A
expressão String(arranjoCar) junto ao operador new corresponde a um construtor (constructor).
Um construtor instrui o operador new em como deve ser construído o novo
objeto, ou seja, um construtor serve para definir a iniciação ou a
inicialização de um objeto. A classe String tem vários construtores. Os
contrutores têm o mesmo nome da classe e só diferem entre si pelo número e tipo
de parâmetros. Um construtor com apenas um parâmetro do tipo arranjo de
caractere aloca um objeto do tipo String onde a cadeia de caractere é formada
pela concatenação dos caracteres do arranjo correspondente.
Podemos definir arranjos de
String, ou sem vícios, arranjos de referências para objetos do tipo String:
String[] meses={"", "Janeiro",
"Fevereiro", "Março",...,"Dezembro"};
No arranjo acima foi incluído, na posição de índice 0, um string de comprimento
zero. Uma outra solução seria o uso de null. Abaixo exemplificamos a
utilização de um método estático da classe String. Considere que temos
uma variável do tipo int contendo o dia do mês e uma variável também do
tipo int contendo o número do mês.. No trecho abaixo é construido um
string do dia do mês utilizando o método estático valueOf() da classe String:
int numDoDia=...;
int numDoMes=...;
String data="dia " + String.valueOf(numDoDia)+
" de " + meses[numDoMes];
No trecho acima invocamos o método valueOf() da classe String. Este
método cria um objeto contendo uma cadeia de caracteres correspondente à
conversão do valor inteiro do parâmetro numDoDia e retorna a referência para
este objeto. Descrito de outra maneira: valueOf() retorna a representação em
cadeia de caracteres do argumento numDoDia.
Uma chamada de método que
retorne uma referência pode ser qualificada, utilizando o operador '.', com
nova chamada de método. Considere o trecho de programa abaixo:
String universidade= "Universidade Federal de Minas Gerais";
String s=universidade.substring(0,20);
A invocação do método substring(0,20) retorna uma referência para um objeto contendo
a subcadeia "Universidade Federal". O primeiro parâmetro é o índice
de início (inclusive) e o segundo parâmetro é o índice final da subcadeia
(exclusivo) que é copiada da instância. Como o método substring() devolve uma referência para uma
instância de String, podemos invocar qualquer método de instância utilizando
esta referência:
String universidade= "Universidade Federal de Minas Gerais";
String x=universidade.substring(24,36).substring(0,5);
O trecho acima mostra duas
chamadas do método substring(). A primeira chamada retorna a cadeia "Minas
Gerais" e a segunda chamada retorna a cadeia "Minas".
Para comparar cadeias
existem os métodos: equals(), equalsIgnorecase(), compareTo() e
compareToIgnoreCase(). Se for utilizada o operador == da Linguagem Java com
variáveis do tipo String a comparação será feita com as referências:
String ab="ab";
String bc="bc";
String s1=ab+"c";
String s2="a"+bc;
if(s1==s2) System.out.println("valores de s1 e s2 iguais!");
No trecho acima o conteúdo de s1 e s2 são duas referências diferentes (foi
preciso alguma prestidigitação - sem trocadilhos - porque o compilador tenta
reutilizar cadeias iguais!). Se quisermos comparar o conteúdo das intâncias
devemos utilizar os métodos mencionados:
String ab="ab";
String bc="bc";
String s1=ab+"c";
String s2="a"+bc;
if(s1==s2) System.out.println("Endereço em s1 igual ao endereço em
s2!");
if(s1.equals(s2)) System.out.println("Instancias com cadeias
iguais!")
O método compareTo() é um método de instância que tem um argumento do tipo
String e retorna um valor do tipo int. O valor retornado é (i) menor que
zero quando a cadeia do argumento é lexicograficamente maior que a cadeia da
instância, (ii) zero quando a cadeia do argumento é a mesma da instância e
(iii) maior que zero quando a cadeia do argumento é menor que a cadeia da
instância:
String abc="abc";
String bcd="bcd";
if(abc.compareTo(bcd)==0)
System.out.println("cadeias iguais:"+abc);
if(abc.compareTo(bcd)<0)
System.out.println(abc+ " precede lexicograficamente "+
bcd);
if(abc.compareTo(bcd)>0)
System.out.println(bcd+ "precede lexicograficamente
"+abc);
Os métodos
equalsIgnoreCase() e compareToIgnoreCase() são similares à descrição acima mas
ignoram se as letras são maiúsculas ou minúsculas.
O método toLowerCase() retorna uma (referência para uma) instância de String na qual todos
os caracteres foram convertidos para minúsculas. O método toUpperCase() retorna uma instância de String na
qual todos os caracteres (letras) foram convertidos para letras maiúsculas:
String universidade= "Universidade Federal de Minas Gerais";
String uniMinusculas=universidade.toLowerCase();
String unimaiusculas=universidade.toUpperCase();
A linguagem Java tem
a classe Character que possui vários métodos estáticos úteis no tratamento de
caracteres individuais. Os seguintes métodos recebem um caractere como
argumento e devolvem um valor booleano: isDigit(), isLetter(),
isLetterOrDigit(), isLowerCase(), isSpaceChar(), isUpperCase().
Os comandos abaixo testam
se o primeiro caractere de uma instância de String é letra ou dígito:
String s="...";
if(Character.isDigit(s.charAt(0)) | Character.isLetter(s.charAt(0)))
System.out.println(...
if(Character.isLetterOrDigit(s.charAt(0)))
System.out.println(...
Exercícios