capítulo 1 - java - linguagem e plataforma 1.1. breve …vqv.com.br/uneb/apostilajava1-4.pdf ·...

26
Capítulo 1 - Java - Linguagem e Plataforma 1.1. Breve histórico da Linguagem Java A linguagem Java foi desenvolvida por um grupo de funcionários da Sun no início dos anos 90 para ser utilizado em pequenos dispositivos eletrônicos inteligentes e não sofreu um grande avanço, de imediato, em virtude de dificuldades de financiamento para esse setor à época. Já a partir de 1993, com a expansão da Internet, novas oportunidades apareceram e a Sun investiu pesado nessa área. A partir de 1995 a Sun anunciou o Java, não apenas como uma nova linguagem de programação, mas como uma nova plataforma de desenvolvimento, possibilitando a criação de páginas da web com conteúdo interativo e dinâmico, inicialmente com o uso de Applets com imagens em movimento. Desde 1996, a linguagem não pára de crescer, produzindo soluções desde pequenos aplicativos corporativos a controle de servidores www. Atualmente está sendo muito utilizada em celulares, pagers e PADs. Um fato interessante, já que a linguagem foi inicialmente concebida para ser utilizada em pequenos equipamentos. 1.2. A Linguagem Java Provavelmente o leitor já deve ter ouvido falar muito em Java, pois as revistas especializadas em informática estão sempre publicando artigos versando sobre essa linguagem. Pelo sucesso que alcançou no mercado, muitas empresas têm produzido ferramentas para manipular ou gerar códigos em Java, como foi o caso do Visual J++ da Microsoft, o que resultou num imediato processo judicial impetrado pela Sun, a criadora e detentora dos direitos da marca registrada Java. A principal característica do Java é que os programas criados nessa linguagem podem ser executados virtualmente em qualquer plataforma. Isso deixa os programadores muito à vontade, pois não precisam se preocupar com o tipo de máquina em que o software produzido irá rodar. Os programas gerados em Java funcionam como acessórios (applets) colocados no computador dos usuários da web quando eles acessam um site qualquer, ou seja, o programa armazenado no servidor é transferido para o computador do usuário e executado localmente nessa máquina. Outro aspecto interessante é que várias linguagens tiveram de ser aperfeiçoadas para apresentar versões voltadas para orientação a objetos, o atual padrão de mercado, e Java já nasceu assim, e, exatamente por isso, tem se destacado entre suas concorrentes. 1.3. As Características da Linguagem Java Orientação a objetos : Java já foi concebida para programação orientada a objetos. Atual padrão de programação adotado mundialmente. Portabilidade : sendo uma linguagem multiplataforma, Java permite criar programas que sejam executados em máquinas com diferentes sistemas operacionais sem necessidade de qualquer adaptação. Multithreading : meio que possibilita a utilização simultânea de vários eventos em um mesmo programa, essa característica permite a criação de servidores multiusuários, onde cada thread cuida da conexão de um usuário ao servidor e todos os usuários podem estar acessando, ao mesmo tempos, pontos diferentes de um mesmo programa. Suporte à comunicação : Java fornece uma quantidade expressiva de classes com funcionalidades específicas, e entre essas classes um conjunto desenvolvido para a implementação de sistemas multiusuários. Tais classes suportam tecnologias avançadas de comunicação, como protocolos TCP/IP, HTT/P, FTP e outros. Acesso remoto a banco de dados : essa característica permite o acesso, via web, a bancos de dados em qualquer ponto do planeta. 1.4. Criação de Programas em Java Como as demais linguagens de programação, o código pode ser produzido em qualquer editor de texto, sendo posteriormente, analisado pelo compilador Java, que traduz o código fonte para o Java ByteCodes, um código

Upload: duongdang

Post on 02-Oct-2018

213 views

Category:

Documents


0 download

TRANSCRIPT

Capítulo 1 - Java - Linguagem e Plataforma

1.1. Breve histórico da Linguagem Java A linguagem Java foi desenvolvida por um grupo de funcionários da Sun no início dos anos 90 para ser utilizado

em pequenos dispositivos eletrônicos inteligentes e não sofreu um grande avanço, de imediato, em virtude de dificuldades de financiamento para esse setor à época. Já a partir de 1993, com a expansão da Internet, novas oportunidades apareceram e a Sun investiu pesado nessa área.

A partir de 1995 a Sun anunciou o Java, não apenas como uma nova linguagem de programação, mas como uma nova plataforma de desenvolvimento, possibilitando a criação de páginas da web com conteúdo interativo e dinâmico, inicialmente com o uso de Applets com imagens em movimento.

Desde 1996, a linguagem não pára de crescer, produzindo soluções desde pequenos aplicativos corporativos a controle de servidores www. Atualmente está sendo muito utilizada em celulares, pagers e PADs. Um fato interessante, já que a linguagem foi inicialmente concebida para ser utilizada em pequenos equipamentos.

1.2. A Linguagem Java Provavelmente o leitor já deve ter ouvido falar muito em Java, pois as revistas especializadas em informática

estão sempre publicando artigos versando sobre essa linguagem. Pelo sucesso que alcançou no mercado, muitas empresas têm produzido ferramentas para manipular ou gerar códigos em Java, como foi o caso do Visual J++ da Microsoft, o que resultou num imediato processo judicial impetrado pela Sun, a criadora e detentora dos direitos da marca registrada Java.

A principal característica do Java é que os programas criados nessa linguagem podem ser executados virtualmente em qualquer plataforma. Isso deixa os programadores muito à vontade, pois não precisam se preocupar com o tipo de máquina em que o software produzido irá rodar. Os programas gerados em Java funcionam como acessórios (applets) colocados no computador dos usuários da web quando eles acessam um site qualquer, ou seja, o programa armazenado no servidor é transferido para o computador do usuário e executado localmente nessa máquina.

Outro aspecto interessante é que várias linguagens tiveram de ser aperfeiçoadas para apresentar versões voltadas para orientação a objetos, o atual padrão de mercado, e Java já nasceu assim, e, exatamente por isso, tem se destacado entre suas concorrentes.

1.3. As Características da Linguagem Java • Orientação a objetos: Java já foi concebida para programação orientada a objetos. Atual padrão de

programação adotado mundialmente.

• Portabilidade: sendo uma linguagem multiplataforma, Java permite criar programas que sejam executados em máquinas com diferentes sistemas operacionais sem necessidade de qualquer adaptação.

• Multithreading: meio que possibilita a utilização simultânea de vários eventos em um mesmo programa, essa característica permite a criação de servidores multiusuários, onde cada thread cuida da conexão de um usuário ao servidor e todos os usuários podem estar acessando, ao mesmo tempos, pontos diferentes de um mesmo programa.

• Suporte à comunicação: Java fornece uma quantidade expressiva de classes com funcionalidades específicas, e entre essas classes um conjunto desenvolvido para a implementação de sistemas multiusuários. Tais classes suportam tecnologias avançadas de comunicação, como protocolos TCP/IP, HTT/P, FTP e outros.

• Acesso remoto a banco de dados: essa característica permite o acesso, via web, a bancos de dados em qualquer ponto do planeta.

1.4. Criação de Programas em Java Como as demais linguagens de programação, o código pode ser produzido em qualquer editor de texto, sendo

posteriormente, analisado pelo compilador Java, que traduz o código fonte para o Java ByteCodes, um código

independente de plataforma que será interpretado por um Interpretador Java instalado no computador ou pelo browser, caso o programa esteja sendo utilizado na Internet.

1.5. A Plataforma Java A maioria das plataformas é formada por um conjunto, o hardware e um sistema operacional que o controla. A

Java é composta apenas pelo hardware, que pode operar sobre outra plataforma qualquer. Por essa característica, a Sun criou o seguinte slogan para sua plataforma: WORA “write once, run anywhere”, isto é, uma vez escrito, roda em qualquer lugar.

1.6. Ambiente de Desenvolvimento Os programas em Java podem ser escritos em qualquer editor de textos e depois deverão ser submetidos ao

Sun Development Kit (SDK), um kit composto por um compilador da linguagem (javac), um interpretador (Java), um visualizador de applets (appletviewer), bibliotecas de desenvolvimento (packages), um depurador de programas (JDB) e diversos documentos (javadoc).

1.7. Um Primeiro Contato com Java Como primeiro contato com a linguagem, será apresentada uma classe Java que escreve uma mensagem

qualquer na tela. Trata-se de um exemplo simples, mas que contém os itens fundamentais para a criação de qualquer aplicação em Java.

class Primeiro_programa { public static void main (String args [ ] ) { System.out.println("Este é meu primeiro contato com Java"); } }

Todo programa Java começa com a palavra reservada class seguida pelo nome da classe, por convenção, iniciado por uma letra maiúscula e contém um par de chaves envolvendo todo o código da classe.

As classes podem ser compostas por vários métodos (em outras linguagens chamados por sub-rotinas, procedures ou funções), os quais pode conter outros métodos ou outras estruturas de programa.

Toda classe executável deve possuir o método main. No exemplo, o método main contém uma única instrução, o comando System.out.println, que não é exatamente um comando, mas uma classe da linguagem que permite a saída de dados na tela.

A linha do método principal possui o seguinte formato: public static void main (String args [ ] ), e praticamente todas as aplicações deverão conter esta linha. A seguir são feitas considerações sobre cada palavra reservada dessa linha.

public - é o qualificador do método, e indica que ele é acessível externamente por outras classes; static - é um outro qualificador, que, por sua vez, indica que o método deve ser compartilhado por todos os objetos que são

criados a partir desta classe; void - é o valor de retorno do método. Quando o método não retorna valor, ele é chamado void, uma espécie de valor vazio,

que deve ser especificado; main - é o nome do método que indica ao compilador o início do programa, Este é o método principal, onde todas as

variáveis, argumentos e instruções são interpretados e processados para a execução do programa; (String args [ ] ) - é o argumento do método principal e conseqüentemente do programa todo; ele é um vetor de strings

formado por todos os argumentos passados ao programa na linha de comando do sistema operacional quando o programa é invocado; e

{ ......... } - estas chaves delimitam o bloco das instruções que integram o método, correspondem ao BEGIN e END da linguagem Pascal.

Capítulo 2 - Aspectos Fundamentais sobre Java

Neste capítulo serão apresentadas as principais características da linguagem Java relacionadas com o tipo de dados e operações.

2.1. Tipos de Dados A linguagem Java oferece diversos tipos de dados com os quais podemos trabalhar. Esta seção abordará os

tipos de dados mais importantes, que podem ser divididos em duas categorias: tipos primitivos e tipos de referências. Os tipos primitivos correspondem aos dados mais simples ou escalares e serão abordados em detalhe a seguir, os tipos de referências consistem em arrays (vetores), classes e interfaces. Estes serão vistos mais adiante.

A tabela a seguir mostra um resumo dos tipos de dados primitivos em Java:

Tipo Descrição boolean Pode assumir o valor true ou o valor false

char Caractere em notação Unicode de 16 bits. Serve para a armazenagem de dados alfanuméricos. Também pode ser usado como um dado inteiro com valores na faixa entre 0 e 65535.

byte Inteiro de 8 bits em notação de complemento de dois. Assume valores entre -27 e 27-1 (-127 a 128).

short Inteiro de 16 bits em notação de complemento de dois. Os valores possíveis cobrem a faixa de -2-15 a 215-1 ( -32.768 a 32.767 )

int Inteiro de 32 bits em notação de complemento de dois. Pode assumir valores entre -231 e 231-1 (2.147.483.648 a 2.147.483.647).

long Inteiro de 64 bits em notação de complemento de dois. Pode assumir valores entre -263 e 263-1.

floatRepresenta números em notação de ponto flutuante normalizada em precisão simples de 32 bits em conformidade com a norma IEEE 754-1985. O menor valor positivo representável por esse tipo é 1.40239846e-46 e o maior é 3.40282347e+38.

doubleRepresenta números em notação de ponto flutuante normalizada em precisão dupla de 64 bits em conformidade com a norma IEEE 754-1985. O menor valor positivo representável é 4.94065645841246544e-324 e o maior é 1.7976931348623157e+308.

Ao contrário do que acontece com outras linguagens de programação, as características dos tipos de dados listados acima independem da plataforma em que o programa deverá ser executado. Dessa forma, os tipos de dados primitivos são realmente únicos e garantem que um programa seja multiplataforma (do ponto de vista de tipos de dados primitivos).

2.1.1. Tipo Boolean Uma variável booleana pode assumir apenas um entre dois valores: true ou false. Algumas operações

possíveis em Java como a <= b, x > y, entre outras têm como resultado um valor booleano, que pode ser armazenado para uso futuro em variáveis booleanas. Estas operações são chamadas operações lógicas. As variáveis booleanas são tipicamente empregadas para sinalizar alguma condição ou a ocorrência de algum evento em um programa Java. Por exemplo:

boolean fim_do_arquivo = false;

é a declaração de uma variável do tipo boolean, cujo nome é fim_do_arquivo. O valor false à direita do operador "=" indica que a variável recebe esse valor como valor inicial. Sem essa especificação, o valor de uma variável fica indeterminado, podendo ser qualquer um dos valores possíveis para seu tipo (neste caso true ou false).

Na declaração acima, também podemos observar a forma de se declarar e inicializar variáveis em Java: 1. informar o tipo de dado que deseja declarar (boolean); 2. informar o nome que será usado para batizar a variável (fim_do_arquivo);

3. atribuir à variável um valor inicial (= false); e 4. terminar a declaração com um ponto-e-vírgula ";".

Na declaração acima foi usado o nome fim_do_arquivo para designar a variável. Um nome de variável, assim como o nome de um método, classe, rótulo ou dezenas de outros itens lexicográficos, constitui o que é chamado de identificador. Uma vez criado, um identificador representa sempre o mesmo objeto a ele associado, em qualquer contexto em que seja empregado.

As seguintes regras regem a criação de identificadores:

• o primeiro caractere de um identificador deve ser uma letra. Os demais caracteres podem ser quaisquer seqüências de numerais e letras;

• não apenas os numerais e letras latinas podem ser empregadas, como também letras de qualquer outro alfabeto;

• por razões históricas, o underscore "_" e o sinal de dólar "$" são considerados letras e podem ser usados nos identificadores ;

• assim como nas linguagens C e C++, os identificadores distinguem o tipo de caixa das letras, isto é, as maiúsculas são consideradas distintas das minúsculas. Isso significa que fimDeArquivo é um identificador diferente de FimDeArquivo ;

• Os identificadores não podem ser palavras reservadas, tais como: class, for, while, public e outras.

2.1.2. Tipos de dados inteiros Os tipos de dados primitivos byte, int, char, short e long constituem tipos de dados inteiros. Isso porque

variáveis desses tipos podem conter um valor numérico inteiro dentro da faixa estabelecida para cada tipo individual. Por exemplo: um byte pode conter um inteiro entre -128 e 127, enquanto um short pode conter um valor entre -32.768 e 32.767. Você deve escolher o tipo inteiro mais adequado às suas necessidades.

Se alguma operação aritmética cria um resultado que excede um dos limites estabelecidos para o tipo inteiro empregado, não há qualquer indicação de erro para avisar sobre essa ocorrência. Ao invés disso, um complemento de dois do valor obtido será o resultado. Por exemplo: se a variável for do tipo byte, ocorrem os seguintes resultados: 127+1 = -128, 127+9=-120 e 127+127=-2. Entretanto, uma exceção do tipo ArithmeticException é apresentada caso ocorra uma divisão por zero. public class Arith { public static void main(String args[]) { byte a = 127; short b = 32767; int c = 2147483647; long d = 9223372036854775807L; int e = 0; a += 1; b += 1; c += 1; d += 1; System.out.println("Valor de a = " + a); System.out.println("Valor de b = " + b); System.out.println("Valor de c = " + c); System.out.println("Valor de d = " + d); d /= e; // Vai dar erro porque e = 0 } }

com seu respectivo resultado de execução:

$java Arith Valor de a = -128 Valor de b = -32768 Valor de c = -2147483648 Valor de d = -9223372036854775808 java.lang.ArithmeticException: / by zero at Arith.main(Arith.java:18)

Seguem abaixo alguns exemplos de declarações de variáveis de tipo inteiro: byte Contador = 1;

int AnguloEmGraus = -45;

char indice = 6;

A diferença entre essas declarações e a declaração de dados booleanos no início da seção 2.1.1, está no tipo de

dado especificado e no valor atribuído a cada variável.

Operações com inteiros Podemos realizar uma série de operações com os dados do tipo inteiro. A tabela seguinte mostra uma lista

completa dessas operações.

Operação Descrição =, +=, -=, *=, /=, %= Operadores de atribuição ==, != Operadores de igualdade e diferença <, <=, >, >= Operadores de desigualdade +, - Operadores unários +, -, *, /, % Adição, subtração, multiplicação, divisão e módulo +=, -=, *=, /=, %= Operadores de atribuição com adição, subtração, multiplicação, divisão e módulo ++, -- Incremento e decremento <<, >>, >>> Operadores de deslocamento de bits <<=, >>=, >>>= Operadores de atribuição com deslocamento de bits ~ Operador lógico de negação &, |, ^ Operadores lógicos E, OU e OU-exclusivo &=, |=, ^= Operadores de atribuição com operação lógica E, OU e OU-exclusivo

Muitas das operações que aparecem na lista acima são familiares e praticamente não requerem explicação. Há

outras, porém, que podem ser um tanto quanto ambíguas. É o caso dos operadores de atribuição aritméticos. Por exemplo: += adiciona um valor ao valor antigo de uma variável e a soma passa a ser o novo valor. Esse padrão também é obedecido para as operações -=, *=, /= e %=. Temos assim as seguintes correspondências:

x += 5 x = x + 5 x -= y x = x - y x *= 2 x = x * 2 z /= 4 z = z / 4

Os operadores de incremento e decremento referem-se a apenas uma variável (e por isso são chamados de unários). Por exemplo, o operador de incremento soma um ao operando conforme o exemplo:

x++;

É uma maneira muito mais concisa de se escrever x = x + 1. Mas não só, esses operadores se comportam de modo diferente quando seguem ou precedem o nome de uma variável. Se o operador precede o nome da variável, então o incremento (ou decremento) ocorre antes que o valor da variável seja tomado para a expressão aritmética. No seguinte exemplo, o valor das variáveis x e y será 6:

int x = 5;

int y = ++x;

Porém, no próximo exemplo o valor de x será 6 enquanto que o valor de y será 5: int x = 5; int y = x++;

Vejamos alguns exemplos de declarações e de utilizações de operações envolvendo tipos inteiros: byte j = 60; short k = 24; int l = 30; long m = 12L; long resultado = 0L; resultado += j; // resultado = 60 (0 mais 60) resultado += k; // resultado = 84 (60 mais 24) resultado /= m; // resultado = 7 (84 dividido por 12) resultado -= l; // resultado = -23(7 menos 30) resultado = -resultado; // resultado = 23 ( -(-23) ) resultado %= m; // resultado = 11 (resto de 23 div. 12)

2.1.3. Tipo caractere Uma variável do tipo char armazena um caractere Unicode. Um caractere Unicode é um caractere de 16 bits,

sendo que de 0 a 225 correspondem aos caracteres do código ASCII (a tabela ASCII é uma tabela padronizada internacionalmente de associações entre caractere e a sua representação numérica no computador). Uma constante do tipo caractere é representada colocando-se entre apóstrofos, ou pelo valor numérico correspondente na tabela Unicode (ou ASCII), ou, ainda, pela seqüência '\x' onde x especifica o caractere a ser referido. Esta especificação de seqüência de escape obedece às mesmas convenções do C/C++. Por exemplo: 'a', 'f', '\n', etc, são constantes do tipo char.

2.1.4. Tipos de ponto flutuante Em Java, existem duas categorias de variáveis de ponto flutuante: float, que armazena valores numéricos em

ponto flutuante de precisão simples, e double, de precisão dupla. Ambas seguem a norma: IEEE Standard for Binary Floating Point Arithmetic, ANSI/IEEE Std. 754-1985 (IEEE, New York). O fato de obedecer a essa norma é que torna os tipos de dados aceitos pela linguagem Java tão portáveis. Esses dados serão aceitos por qualquer plataforma, independendo do tipo de sistema operacional e do fabricante do computador. A representação dos valores em ponto flutuante pode ser feita usando a notação decimal (exemplo: -24.321) ou a notação científica (exemplo: 2.52E-31).

Além dos possíveis valores numéricos que uma variável de ponto flutuante pode assumir há também os seguintes:

• menos infinito

• mais infinito

• zero

• NAN - not a number Estes são requeridos pela norma. O mais infinito é usado, por exemplo, ao somarmos 1 ao maior valor

possivelmente representável por esse sistema. Observe que esse comportamento é diferente do comportamento para números inteiros.

Muitas das operações realizáveis com inteiros (porém não todas) têm análogas para números de ponto flutuante. Eis um resumo:

Operação Descrição =, +=, -=, *=, /= Operadores de atribuição ==, != Operadores de igualdade e diferença

<, <=, >, >= Operadores de desigualdade +, - Sinais unários +, -, *, / Adição, subtração, multiplicação e divisão +=, -=, *=, /= Operadores de atribuição com adição, subtração, multiplicação e divisão ++, -- Operadores unários de incremento e decremento

2.2. Vetores e Matrizes Semelhante às linguagens C, C++ e Pascal, a linguagem Java também dá suporte a vetores e matrizes (arrays)

de diversas formas. Os vetores constituem uma forma muito conveniente de organizar informações em fileira. Por exemplo, podemos formar um vetor com as notas de cinco alunos de uma sala de aula do seguinte modo:

float nota[] = { 7.8, 8.4, 4.2, 1.8, 6.4 };

Neste caso nota[0] é a nota do primeiro aluno, isto é, 7.8; nota[1] é a nota do segundo, ou seja, 8.4; nota[2] é a nota do terceiro; e assim por diante.

A utilização de vetores e matrizes em Java envolve três passos: 1. Declarar o vetor ou matriz. Para isto, basta acrescentar um par de colchetes antes ou depois do nome da

variável. Por exemplo: int ind[]; double A[][],T[][][]; int []nota;

2. Reservar espaço de memória e definir o tamanho. É preciso definir o tamanho do vetor, isto é, a quantidade total de elementos que terá de armazenar. Em seguida é necessário reservar espaço de memória para armazenar os elementos. Isto é feito de maneira simples pelo operador new: ind = new int[10]; nota = new int[70]; A = new double[10][20];

3. Armazenar elementos no vetor ou matriz. Para armazenar uma informação em um dos elementos de um vetor ou matriz, é necessário fornecer um índice que indique a posição desse elemento. Por exemplo, para armazenar um valor na quarta posição do vetor nota, fazemos o seguinte: nota[3] = 5.2;

Como podemos observar, os índices começam em zero e vão até o número de posições reservadas, menos um.

No vetor nota criado acima, os índices válidos vão de 0 até 69. Caso haja a tentativa de atribuir um valor a um elemento cujo índice esteja fora desse intervalo, ocorrerá um erro que impedirá a execução do programa.

Existe, porém, um atalho para esses três passos quando desejamos criar um vetor com valores atribuídos de modo estático. Foi o que fizemos no primeiro exemplo acima, declarando o vetor nota com as notas de cinco alunos.

float nota[] = { 7.8, 8.4, 4.2, 1.8, 6.4 };

Nesse caso, espaço suficiente para as notas de cinco alunos foi reservado e as notas foram guardadas em respectivas posições do vetor. Eis mais alguns exemplos de vetores e matrizes:

// 12 primeiros termos da seqüência de Fibonacci: long Fibonacci[] = {1,1,2,3,5,8,13,34,55,89,144}; // Tabela de sen(n * pi / 6), n = 0, 1, 2,...5 float seno[] = {0.0000,0.5000,0.8660,1.0000,0.8660,0.5000}; // Tabela de log(1+n), n = 0, 2...99: double tlog[] = new double[100]; for(int n=0; n<100; n++) tlog[i] = Math.log(1+n); // Matriz dos coeficientes double A[][] = { {1,2,3}, {0,1,3}, {0,0,-1} };

2.3. Comentários Os comentários em Java podem ser feitos de três formas:

• comentário por linha: ao utilizar o “//”, toda a parte do código que estiver à direita será considerada como comentário;

• comentário por bloco: ao utilizar o “/* ..... */”, toda a parte do código que estiver entre o “/*” e o “*/” será considerada como comentário; e

• documentação (Javadoc): ao utilizar o “/** ..... */”, toda a parte do código que estiver entre o “/**” e o “*/” será considerada como parte da documentação da classe.

2.4. Operadores A linguagem Java oferece um amplo conjunto de operadores destinados à realização de operações aritméticas,

lógicas e relacionais, podendo formar expressões de qualquer tipo.

2.4.1. Operadores Aritméticos A tabela a seguir mostra os operadores aritméticos da linguagem Java:

Função Operador Exemplo Adição + X + Y Subtração - X – Y Multiplicação * X * Y Divisão / X / Y Resto da divisão inteira % X % Y Sinal negativo - -X Sinal positivo + +X Incremento unário ++ ++X ou X++ Decremento unário -- --X ou X--

2.4.2. Operadores Relacionais Os operadores relacionais possibilitam comparar valores ou expressões, retornando um resultado lógico

verdadeiro ou falso. A tabela a seguir apresenta os operadores relacionais usados em Java:

Função Operador Exemplo Igual == X == Y Diferente != X != Y Maior que > X > Y Maior ou igual a >= X >= Y Menor que < X < Y Menor ou igual a <= X <= Y

2.4.3. Operadores Lógicos São operadores que permitem avaliar o resultado lógico de diferentes operações aritméticas em uma expressão.

Os operadores lógicos utilizados em Java são os seguintes:

Função Operador Exemplo E lógico ou AND && X && Y OU lógico ou OR || X || Y Negação ou NOT ! !X

2.5. Literais

O valor literal é um valor escrito no código fonte e identificado como sendo de um tipo primitivo. int x = 1; // literal inteiro char u = ’a’; // literal char boolean b = true; // literal boolean double d = 002.11d; // literal double

Há três maneiras de representar literais numéricos inteiros em Java:

• octal (base 8), representado com um 0 (zero) na frente do valor numérico. Exemplo: int x = 01; int x = 0931; int x = 021;

• decimal (base 10), representado normalmente. Exemplo: int x = 1; int x = 931; int x = 21;

• hexadecimal (base 16), representado com um 0x (zero e x) na frente do valor numérico. Exemplo: int quinze = 0xF; // 15 (decimal) int vinte_e_nove = 0x1D; // 29 (decimal) int vinte_e_nove = 0x1d; // 29 (decimal) int valor = 0xBAFAFA; // 12253946 (decimal) int valor = 0xBaFAfa; // 12253946 (decimal)

Caracteres também possuem uma representação especial. Todo caractere se encontra entre aspas simples, porem pode-se atribuir um valor inteiro ao caractere sem precisar de uma conversão explicita. Eis alguns exemplos:

char a = ’A’; // letra A char b = ’&’; // caractere & char c = ’\u004D’; // letra M (unicode) char d = ’\n’; // salto para próxima linha char e = ’\t’; // Tabulação char f = ’\r’; // Retorno de carro char g = ’\b’; // backspace char h = ’\f’; // avanço de formulário (form feed) char i = ’\\’; // contra-barra char j = ’\’’; // caractere ’ (apóstrofo ou aspas simples) char k = ’\”’; // caractere “ (aspas duplas) char l = 65; // char recebe um valor de um int char m = (char) -1; // fora do intervalo, conversão explícita char n = (char)100000; // fora do intervalo, conversão explícita

2.6. Conversão entre tipos primitivos Existem duas maneiras de fazer conversões de tipos primitivos:

• conversão implícita quando o tamanho de uma variável é maior do que o tamanho da variável ou do valor que lhe está sendo atribuído. Exemplo: int y = 10; long x = y;

• conversão explícita quando o tamanho de uma variável é menor do que o tamanho da variável ou do valor que lhe está sendo atribuído. Exemplo: long y = 10L; int x = (int) y;

O tamanho das variáveis do tipo long (64 bits) é maior do que o das variáveis do tipo int (32 bits), tornando-se, portanto, necessário indicar uma conversão. Essa indicação é feita colocando-se entre parênteses, o tipo da variável que irá receber o valor. Se essa informação for omitida, o compilador indicará que existe um erro de compilação (tipos incompatíveis). Outra forma é usar as constantes de identificação de tipos e fazer uma conversão explícita, como em : long y = 10L;

2.7. Uso do Teclado para a Entrada de Dados A linguagem Java possui diversas formas de entrada de dados. No entanto, muitas delas exigem que se faça um

tratamento de exceções (erros). Alguns exemplos que utilizam a entrada de dados com tratamento de erros serão apresentados nos próximos capítulos. Para este curso, será utilizado o mini aplicativo gráfico que retorna uma String (seqüência de caracteres). A seguir, um exemplo de uma leitura de dados (não esquecer de acrescentar o import nos programas).

Exemplo 1: import javax.swing.JOptionPane; public class Exercicio_1_1 { public static void main (String[ ] args) { int n1, n2, n3, n4, soma; String s; s = JOptionPane.showInputDialog(“Digite o primeiro nr”); n1 = Integer.parseInt(s); s = JOptionPane.showInputDialog(“Digite o segundo nr”); n2 = Integer.parseInt(s); s = JOptionPane.showInputDialog(“Digite o terceiro nr”); n3 = Integer.parseInt(s); s = JOptionPane.showInputDialog(“Digite o quarto nr”); n4 = Integer.parseInt(s); soma = n1 + n2 + n3 + n4; System.out.println(“A soma é “ + soma); } }

Exemplo 2: import javax.swing.JOptionPane; public class Exercicio_2_1 { public static void main (String[ ] args) { double n1, n2, n3, media; String s; s = JOptionPane.showInputDialog(“Digite a primeira nota”); n1 = Integer.parseDouble(s); s = JOptionPane.showInputDialog(“Digite a segunda nota”); n2 = Integer.parseDouble(s); s = JOptionPane.showInputDialog(“Digite a terceira nota”); n3 = Integer.parseDouble(s); media = ( n1 + n2 + n3 ) / 3; System.out.println(“A média é “ + media); } }

Capítulo 3 - Comandos Condicionais e Estruturas de Controle Controle de fluxo é a habilidade de ajustar a maneira como um programa realiza suas tarefas. Por meio de

instruções especiais, chamadas comandos, essas tarefas podem ser executadas seletivamente, repetidamente ou excepcionalmente.

Podemos classificar os comandos aceitos pela linguagem Java em basicamente quatro categorias:

Comando Palavras-chave Tomada de decisões if-else, switch-case

Laços ou repetições for, while, do-while

Apontamento e tratamento de exceções try-catch-finally, throw

outros break, continue, label:, return

3.1 Execução Condicional A forma mais simples de controle de fluxo é o comando if-else. Ele é empregado para executar seletivamente ou

condicionalmente um outro comando, mediante um critério de seleção. Esse critério é dado por uma expressão, cujo valor resultante deve ser um dado do tipo booleano, isto é, true ou false. Se esse valor for true, então o comando seguinte é executado; se for false, a execução do programa salta esse comando e segue adiante. A sintaxe para esse comando é :

if ([condição]) [comando] // O comando é executado se a condição for true

Uma variação do comando if-else permite escolher, alternadamente, um entre dois comandos a executar. Nesse caso, se o valor da expressão condicional que define o critério de seleção for true, então o primeiro dos dois comandos é executado, senão, será executado o segundo comando.

if([condição]) [comando_1] // Executado se a condição for true else [comando_2] // Executado se a condição for false

Por exemplo: import java.io.StreamTokenizer;

public class sqrt { public static void main(String args[]) { double x; StreamTokenizer inp = new StreamTokenizer(System.in); // Lê um dado double a partir do teclado System.out.println("x = "); System.out.flush(); try { inp.nextToken(); x = inp.nval; } catch(java.io.IOException e) { x = 0; } // Decide se é possível calcular a raiz quadrada do // número dado. Sendo possível, calcula-a. Do contrário // emite uma if (x >= 0)

mensagem de aviso

System.out.println("raiz quadrada de x e " + sqrt(x)); else System.out.println("x e negativo"); } }

Quando mais de um comando deve ser executado, eles devem ser delimitados por chaves { }, e passam a constituir que se convencionou chamar de blocos de comandos. Para efeitos de programação, um bloco de comandos é interpretado como se fosse um único comando. Eis alguns exemplos de blocos de comandos:

Exemplo 1: { System.out.println("x="); System.out.println(x); // comandos em linhas separadas

}

Exemplo 2: { temp = y; y = x; x = temp; } // comandos em uma mesma linha

Desse modo, pode-se fazer também a execução seletiva de blocos de comandos conforme ilustra o seguinte programa para discutir sobre as raízes de uma equação do segundo grau ax2 + bx + c = 0:

import java.io.StreamTokenizer;

public class Baskhara { public static void main(String args[]) { double a, b, c, delta; StreamTokenizer in = new StreamTokenizer(System.in); // Requisitando do usuário os dados sobre a equação a ser analisada. try { System.out.println("Digite os coeficientes a, b e c, da equação"); System.out.print("a = "); System.out.flush(); in.nextToken(); a = in.nval; System.out.print("b = "); System.out.flush(); in.nextToken(); b = in.nval; System.out.print("c = "); System.out.flush(); in.nextToken(); c = in.nval; } catch(java.io.IOException e) { System.out.println("Falha na entrada dos dados."); a = 0; } // Calculando o discriminante da equação // delta = b*b - 4*a*c; System.out.println( "Equação: ("+a+")x^2+("+b+")x+("+c+")" ); // Decidindo sobre o número de raízes da equação mediante o // valor do discriminante calculado acima if ( delta > 0 ) { // A eq. tem duas raízes reais double r, d, x1, x2; r = Math.sqrt(delta); d = 2*a; x1 = ( -b - r ) / d; x2 = ( -b + r ) / d; System.out.println("A equação tem duas soluções reais: "); System.out.println(" x1 = " + x1); System.out.println(" x2 = " + x2); } else if ( delta < 0 ) // A eq. não tem raízes reais System.out.println("A equação não tem raízes reais."); else { // A eq. tem uma raiz real dupla double x; x = -b / (2*a); System.out.println("A equação tem uma única raiz real:"); System.out.println(" x1 = x2 = " + x); } System.out.println("Fim da discussão.");

} }

Exemplo 3: import javax.swing.JOptionPane; class Baskara { public static void main (String[ ] args) { int a, b, c, d; double r, x, x1, x2; String s; System.out.println("Entre com os coeficientes da equação"); s = JOptionPane.showInputDialog("Digite A"); a = Integer.parseInt(s); s = JOptionPane.showInputDialog("Digite B"); b = Integer.parseInt(s); s = JOptionPane.showInputDialog("Digite C"); c = Integer.parseInt(s); // Calculando o discriminante da equação d = b*b - 4*a*c; System.out.println( "Equação: "+a+"x2 + "+b+"x + "+c+" = 0" ); // Decidindo sobre o número de raízes da equação mediante o // valor do discriminante calculado acima if ( d > 0 ) { // A eq. tem duas raízes reais r = Math.sqrt(d); x1 = ( -b - r ) / (2*a); x2 = ( -b + r ) / (2*a); System.out.println("Esta equação tem duas soluções reais: "); System.out.println(" x1 = " + x1); System.out.println(" x2 = " + x2); } else if ( d < 0 ) // A eq. não tem raízes reais System.out.println("Esta equação não tem raízes reais."); else { // A eq. tem uma raiz real dupla x = -b / (2*a); System.out.println("Esta equação tem uma única raiz"); System.out.println(" x1 = x2 = " + x); } System.out.println("Fim da discussão."); } }

3.1.1 if-then-else compacto O if-else compacto, também conhecido como operador ternário, não é propriamente um comando, mas um

operador que realiza avaliação seletiva de seus operandos, mediante o valor de uma expressão booleana semelhante à do comando if-else. Se essa expressão for true, então um primeiro operando é avaliado; se for false então o segundo operando é avaliado. A sua sintaxe é:

[expressão condicional] ? [expressão 1] : [expressão 2]

onde [expressão condicional] deve resultar em true ou false; e [expressão 1] e [expressão 2] são os operandos, que podem ser expressões quaisquer. O valor resultante da aplicação do operador if-else compacto é, obviamente, igual ao valor do operando que tiver sido avaliado. O tipo desse valor obedece às regras de conversão entre tipos de dados discutida anteriormente. Considere o seguinte exemplo:

y = x < 1 ? x*x : 2-x;

Esta linha de comando é logicamente equivalente à seguinte seqüência de comandos:

if (x<1) then y = x*x;

else y = 2-x;

É importante notar que o if-else compacto é um operador de baixa precedência, logo o uso de parênteses para separar seus operandos não é necessário (a menos que mais de um desses operadores estejam presentes na mesma expressão). Porém, há casos em que o uso de parênteses para definir claramente os operandos é essencial. Por exemplo: y = |x|*sin(x) pode ser codificado como:

y = ( x < 0 ? -x : x ) * Math.sin(x); // aqui os parênteses são essenciais.

Sem os parênteses, x * Math.sin(x) seria visto pelo operador if-else compacto como se fosse um único operando, devido à alta precedência do operador multiplicação.

3.2. Execução Seletiva de Múltiplos Comandos Freqüentemente, desejamos que um único comando (ou único bloco de comandos) de uma lista seja executado

mediante um dado critério. Isso pode ser feito por intermédio do aninhamento ou acoplamento de vários comandos if-else, do seguinte modo:

if ([condição 1]) [comando 1] else if ([condição 2]) [comandos 2]) else if ([condição 3]) [comando 3] .... else [comando n]

A presença do último else, juntamente com seu comando, é opcional. Neste código, o [comando 1] será executado (e os demais saltados) caso a primeira condição seja true, o [comando 2] será executado (e os demais saltados) caso a primeira condição seja false e a segunda condição seja true, e assim sucessivamente. O [comando n] (se houver) somente será executado (e os demais saltados) caso todas as condições sejam false.

Observe o exemplo:

import java.io.StreamTokenizer;

public class sqrt { public static void main(String args[]) { double x, y; StreamTokenizer inp = new StreamTokenizer(System.in); /* Deseja-se definir y tal que | x + 2, se x < -1, y = | 1, se -1 <= x < 1, | x * x, se 1 <= x. */ try { System.out.print("x = "); System.out.flush(); inp.nextToken(); x = inp.nval; } catch(java.io.IOException e){ x = 0; } if (x<-1) y = x + 2; else

if(x <= 0) y = 1; else y = x*x; System.out.println("y = " + y); } }

3.3.Execução Seletiva por Valores Assim como no caso execução seletiva de múltiplos comandos, há situações em que se sabe de antemão que

as condições assumem o valor true de forma mutuamente exclusiva, isto é, apenas uma entre as condições sendo testadas assume o valor true num mesmo momento. Nesses casos, a linguagem Java (como também as linguagem C, C++ e Pascal) provê um comando de controle de fluxo bastante poderoso. Trata-se do comando swich, cuja sintaxe é a seguinte:

switch([expressão]) { case [constante 1]: [comando 1]; break; case [constante 2]: [comando 2]; break; ...... case [constante n]: [comando n]; break; default: comando]; }

onde [expressão] pode ser qualquer expressão válida com resultado do tipo int ou char. Ela é avaliada e o seu valor resultante é comparado com as constantes distintas [constante 1], [constante 2], ..., [constante n]. Caso esse valor seja igual a uma dessas constantes, o respectivo comando é executado (e todos os demais são saltados). O rótulo default: é opcional, mas se estiver presente, os comandos que o seguem serão executados se nenhuma das constantes dos rótulos anteriores se igualar ao valor da [expressão]. Por exemplo:

public class test { public static void main(String args[]) { int op; op = 2; System.out.print("valor de op eh: " + op) switch(op) { case 1: System.out.println("case 1: op=" + op); break; case 2: System.out.println("case 2: op=" + op); break; case 3: System.out.println("case 3: op=" + op); break; default: System.out.println("default: op fora do limite 1..3"); } } }

3.4. Laço de iteração enquanto/faça Em muitos programas, pode-se ter a necessidade de que alguma tarefa seja executada repetidamente enquanto

uma dada condição seja verdadeira. Isso pode ser implementado em Java pela utilização do comando while. Este comando avalia uma expressão condicional, que deve resultar no valor true ou false. Se o valor for true,

então o comando é executado; se a expressão for false, então o comando é saltado e a execução prossegue adiante. A diferença entre esta estrutura de controle de fluxo e o if-else, é que após executar o comando subjacente, a expressão condicional é mais uma vez avaliada e seu resultado novamente considerado. Desse modo, a execução do comando subjacente se repetirá até que o valor da expressão condicional seja false. Observe, porém, que, como a expressão é avaliada antes de uma possível execução do comando, existe a possibilidade deste comando jamais ser executado. A sintaxe do comando while é:

while([condição]) [comando]

Por exemplo:

// Achar a raiz quadrada de 2 pelo método de bissecção public class sqrt2 { public static void main(String args[]) { double a, b, x=1.5, erro = 0.05; a = 1; b = 2; // 1 < (ra while( (b-a) > erro ) {

iz de 2) < 2

x = (a + b) / 2; if (x*x < 2) // x < raiz de 2 a = x; else // x >= raiz de 2 b = x; } System.out.println("Valor aproximado de raiz quadrada de 2: "+x); } }

3.5. Laço de iteração faça/enquanto Outro tipo de laço de repetição, similar ao enquanto/faça, é o laço faça/enquanto. Este é introduzido pelo par de

comandos do - while, conjugados, que tem a seguinte sintaxe: do [comando] while([condição]);

Diferente do laço enquanto/faça, este tipo de laço de repetição executa o comando e em seguida avalia a expressão condicional. A repetição ocorre se o valor dessa expressão for true. Se esse valor for false, a execução prossegue adiante do while. Observe o seguinte exemplo:

public class Menu { public static void main(String args[]) { char op; int i = 0; double x = 0; do { System.out.println("\nOpcoes:"); System.out.println("p - Atribuir: x = 0.5, i = 2"); System.out.println("n - Atribuir: x = -0.2, i = -1"); System.out.println("x - ver o valor de x"); System.out.println("i - ver o valor de i"); System.out.println("f - fim"); System.out.print("Sua escolha: "); System.out.flush(); try { op =(char)System.in.read(); System.in.read(); // Para pegar o 'enter' } catch(java.io.IOException e) { op = 'q'; } switch(op) { case 'p': x = 0.5; i = 2; break; case 'n': x = -0.2; i = -1; break; case 'x': System.out.println("\n--> x = " + x); break; case 'i': System.out.println("\n--> i = " + i); break; case 'f': break; default : System.out.println("\n--> Opção invalida"); } } while(op != 'f'); System.out.println("\nAté logo."); }

}

3.6. Laço de iteração com contagem Em certas situações, são necessários de laços de repetições nos quais alguma variável seja usada para contar o

número de iterações. Para essa finalidade, usa-se o laço for. Esse tipo de estrutura de controle de laço é o mais versátil disponível na linguagem Java. Sua sintaxe é a seguinte:

for ([expressão 1 [comando]

]; [condição]; [expressão 2])

onde : [expressão 1] é chamada expressão de inicialização,; [condição] é uma expressão condicional; e [expressão 2] é uma expressão qualquer a ser executado no final de cada iteração. O laço for acima é equivalente a:

[expressão 1] while ([condição]) { [comando] [expressão 2] }

Isto que dizer que o laço for executa inicialmente a expressão de inicialização; em seguida, avalia a expressão condicional; e, se o valor desta for true, então executa o comando e a segunda expressão, e, novamente, avalia a expressão condicional. Do contrário, se o valor da expressão for false, a execução prossegue adiante do laço for. Este arranjo é muito conveniente para controlar variáveis de contagem, conforme ilustra o seguinte exemplo:

for (i = 0; i <= n; i++) System.out.println("V["+i+"]="+v[i]);

Este código imprime os valores de cada elemento v[i] de um vetor v, para i variando de 0 até n. O operador ++ foi utilizado na ultima expressão do laço for para somar um ao valor da variável de contagem. Caso fosse necessário um incremento (ou decremento) maior do que um, poder-se-ia usar os operadores += ou -=. Por exemplo, para imprimir todos os números pares entre de 0 até 10, o programa poderia ser codificado da seguinte maneira:

for(i = 0; i <= 10; i += 2) System.out.println(" " + i);

Tanto a [expressão 1] quanto a [expressão 2] do laço for permitem acomodar múltiplas expressões, bastando separá-las por vírgula. Por exemplo, a soma de {1/n} n = 1, 2, .., N pode ser obtida por:

for (soma = 0, n = 1; n <= N; n++) soma += 1/n;

ou ainda por for(soma = 0, n = 1; n <= N; soma += 1/n, n++);

3.7. break e continue O comando break é usado para interromper a execução de um dos laços de iteração vistos acima ou de um

comando switch. Este comando é comumente utilizado para produzir a parada de um laço mediante a ocorrência de alguma condição específica, antes da chegada do final natural do laço.

Exemplo: // Achar i tal que v[i]for(i = 0; i < n; i++)

é negativo

if( v[i] <0) break; if(i == n) System.out.println("elemento negativo não encontrado.");

E se isto se der dentro de um laço duplo? Nesse caso, o comando break provocará a interrupção apenas do laço em que o comando é imediatamente subjacente. Os outros laços continuam normalmente.

Exemplo: // Notificando a existência de elemento nulo em cada linha do matriz A for(i = 0; i <m; i++) // Para cada linha i da matriz faça for(j = 0; j <n; j++) // Para cada coluna j da matriz faça if(A[i][j] == 0) { System.out.println("A possui elemento nulo na linha "+i); break; }

O comando continue tem a função de ignorar os comandos restantes do laço, saltando direto para seu final, e submetendo-o a uma nova avaliação de sua condição de repetição. O exemplo a seguir explora os dois comandos, break e continue:

/* este programa calcula a média da nota dos alunos de uma sala de aula e se encerra quando for digitada uma nota negativa */

/ Notificando a existência de elemento nulo em cada linha do matriz A

import javax.swing.JOptionPane; class Media { public static void main (String[ ] args) { double nota, media = 0; int aluno = 1; System.out.println("Digite notas válidas ou nr < 0 para sair"); While ( 1 ) { // sempre verdadeiro String s; System.out.println("Aluno " + aluno); s = JOptionPane.showInputDialog("Digite a nota "); nota = Integer.parseDouble(s); if (nota < 0 ) break; // sai do laço while if (nota > 10) { System.out.println("Nota inválida, digite novamente"); continue; // desconsidera os próximos comandos

} media += nota; aluno++; } System.out.println( "A média da turma foi:"+ media / --aluno); } }

Capítulo 4 - Operações com strings e operações matemáticas

4.1. A classe String Para muitas linguagens existe um tipo primitivo de dados para representar uma cadeia de caracteres. Em Java,

a cadeia de caracteres é representada por uma classe denominada String. Neste momento, ainda não será apresentado um conceito formal da definição de classe, no entanto, alguns aspectos relevantes merecem ser apontados para possibilitar a utilização da classe String nos programas.

4.1.1. Declaração e inicialização Para se criar variáveis (objetos) do tipo String (da classe String) utiliza-se a mesma sintaxe para a criação de

variáveis dos tipos primitivos: String s = "Linguagem Java"; String nome;

4.1.2. Referências

Variáveis do tipo String (o nome correto seria Objetos da classe String), como qualquer outro objeto em Java, são acessadas por referência. Isso significa dizer que ela armazena o endereço físico de memória destinado a guardar o valor da variável. Assim, quando houver dois objetos P1 e P2 da classe Pessoa e for executado o comando P1= P2, desse ponto em diante, os dois identificadores passam a indicar o mesmo endereço de memória, e, nesse caso, P1 e P2 serão considerados o mesmo objeto. Todas as modificações realizadas em P1 terão reflexos em P2 e vice-versa. A classe String é a exceção dessa regra, pois os objetos String são inalteráveis. Para fixar esse entendimento, observe os exemplos:

String s1 = "123"; String s2 = s1; s1 = "abc";

A String s1 foi criada como "123" e modificada para "abc". Como esses objetos são acessados por referência, e s2 = s1, era de se esperar que a alteração de s1 modificasse também o objeto s2, mas nada disso ocorre. s1 fica com o valor "abc" e s2 com "123".

A explicação para isso é que, ao fazer s1 = "abc", o Java criou um NOVO objeto e, por isso, o valor de s2 não foi alterado, pois s2 mantém a referência ao endereço inicialmente indicado por s1. A variável (objeto) s1 agora se referencia a um novo objeto, cujo conteúdo é "abc".

Assim, se uma nova String for criada e não for atribuída a nenhuma variável de referência, ela ficará perdida mas continuará ocupando espaço de memória. Veja mais um exemplo:

String s1 = "abc"; String s2 = "abcd" + "e";

O código anterior criará, no pool, quatro Strings ("abc", "abcd", "e" e "abcde"), das quais, duas serão perdidas ( "abcd" e "e"). Outro exemplo:

String s1 = "ricardo"; s1 = "santana";

O que este código acabou de fazer, foi: criar a String "'ricardo" no pool e referenciá-la por s1. Depois, criou uma nova String ("santana") e a referenciou pela mesma variável s1. Assim, a string "ricardo" ficou perdida no pool (mas não saiu da memória).

4.1.3. O método substring O método substring recebe dois argumentos do tipo inteiro, onde define a posição inicial e a posição final da

string e retorna uma String (sem alterar a original). Observe os exemplos seguir: Exemplo 1:

"abcdefghijklmnop".substring(0,3); // resulta em: abc

Observe que a posição inicial (0) é a letra "a" e que a posição final (3) é a letra "d" e substring(0,3) retornou "abc".

Exemplo 2: public class TestaString { public static main(String[ ] args) { String s = "ricardo santana"; System.out.println(s.substring(8,13); } }

Saída na tela: "santa"

4.1.4. Métodos da String A seguir são apresentados alguns métodos interessantes da classe String que poderão ser úteis no

desenvolvimento de programas futuros.

• concat - Adiciona uma string a outra, porém não altera a string em que o método está sendo executado. Exemplo:

public class TestaString { public static main(String[ ] args) { String s = "teste "; System.out.println(s.concat("não mudou")); System.out.println(s); } }

Saídas na tela: teste não mudou teste

Note que nenhuma alteração foi efetuada na string s. Se fosse o caso de alterá-la, o código poderia ser o seguinte:

String s = "teste "; s = s.concat("não mudou"); System.out.println(s); // resultado: teste não mudou

• equalsIgnoreCase - Testa se uma string é igual a outra, ignorando a diferença entre maiúsculas e minúsculas.

Exemplo: String s1 = "teste"; String s2 = "TESTE"; System.out.println(s1.equals(s2)); // resultado: false System.out.println(s1.equalsIgnoreCase(s2)); // resultado: true

• length - Obtém o tamanho da string. Exemplo: String nome = "testa de ferro"; System.out.println(nome.length() ); // resultado: 15

• replace - Substitui os caracteres de uma string. Exemplo: String texto = "Est4mos 4qui p4r4 test4r"; texto = texto.replace('4','a'); System.out.println(texto); // resultado: Estamos aqui para testar

• substring - Extrai uma string de outra. Exemplo: String texto = "carambola"; System.out.println(texto.substring(2,7); // resultado: rambo

• toLowerCase - Muda todas as letras que estiverem maiúsculas para letras minúsculas (mas não altera o conteúdo da string original).

Exemplo: String s = "AKJDJE"; System.out.println(s.toLowerCase() ); // resultado: akjdje System.out.println(s); // resultado: AKJDJE

• toUpperCase - Transforma em maiúsculas todas as letras que estiverem minúsculas. Exemplo: String s = "teste de caixa alta"; s = s.toUpperCase(); System.out.println(s); // TESTE DE CAIXA ALTA

• trim - Retira os espaços das extremidades de uma string. Exemplo: String s = " tem um "; s = s.trim();

System.out.println("["+ s +"]"); // resultado: [tem um]

• equals - Compara o valor de uma string com o de outra, informada como argumento. Para se comparar duas strings de forma correta, assim como para comparar dois objetos, utiliza-se o método equals e não o operador ==, pois, como os objetos são acessados por referência, o operador == compararia o valor do endereço físico de memória dessas variáveis variável (objeto) e não o conteúdo delas. Somente quando dois objetos se referem ao mesmo endereço de memória é que o == retorna true. Observe atentamente o exemplo a seguir:

Exemplo: String s1 = "texto"; String s2 = "texto"; System.out.println(s1 == s2); // resultado 1: true System.out.println(s1.equals(s2)); // resultado 2: true s1 = new String ("texto"); s2 = new String ("texto"); System.out.println(s1 == s2); // resultado 3: false System.out.println(s1.equals(s2)); // resultado 4: true

4.2. A classe StrinBuffer A classe StringBuffer é uma alternativa a ser utilizada nos casos em que o uso da classe String se torna inviável

(seja por consumir memória ou por outra razão). Essa classe difere da String por ser passível de sofrer alteração. Exemplo:

StringBuffer sb = new StringBuffer("testando"; Sb.append(" se mudou"); System.out.println(sb); // resultado: testando se mudou

4.2.1. Métodos importantes da classe StrinBuffer • append - esse método adiciona o conteúdo de uma string ao StringBuffer.

Exemplo: StringBuffer sb = new StringBuffer("abc"); sb = sb.uppend("def"); System.out.println(sb); // resultado: abcdef

• insert - insere uma string no StringBuffer a partir de uma determinada posição. Exemplo: StringBuffer sb = new StringBuffer("calla"); sb = sb.insert(2,"rambo"); System.out.println(sb); // resultado: carambola sb = sb.insert(22,"-amarga"); System.out.println(sb); // resultado: carambola-amarga

Como se pôde observar, no caso do número passado como argumento ultrapassar o tamanho da string, a inserção se dará imediatamente depois da última posição da String.

• reverse - inverte todos os caracteres do StringBuffer. Exemplo: StringBuffer sb = new StringBuffer("lamina"); sb.reverse(); System.out.println(sb); // resultado: animal

• equals - O método equals da classe StringBuffer não é substituído, isso significa que ele não compara valores, portanto, uma questão com essa consideração pode confundir, observe:

Exemplo: StringBuffer sb1 = new StringBuffer("animal"); StringBuffer sb2 = new StringBuffer("animal"); System.out.println(sb1.equals(sb2)); // resultado 2: false

Apesar de conterem o mesmo valor, o resultado será false, pois é comparado se as variáveis referenciam o mesmo objeto na memória, e como na memória existem dois objetos distintos, o resultado é false.

4.2.2. Encadeamento de métodos Essa característica da linguagem Java, muito explorada na programação orientada a objeto, também pode ser

utilizada no desenvolvimento de programas estruturados. Trata-se de aninhar métodos, isto é, um método pode chamar um segundo método, que por sua vez pode chamar a um outro. Veja o exemplo:

StringBuffer sb = new StringBuffer("zoref"); System.out.println(sb.insert(sb.length()," lamina").reverse().toString());

O resultado dessas linhas de código será o seguinte: Animal feroz

Pode-se observar o aninhamento dos métodos: insert, reverse e toString. O sb.insert(....) é um StringBuffer e, portanto, possui o método reverse(), que, por sua vez, tem como resultado um StringBuffer, o qual possui o método toString. Os métodos foram executados da ESQUERDA para a DIREITA, exatamente como se lê, ou seja, o primeiro método a ser executado foi o insert, alterando o objeto sb, mudando o StringBuffer para "zoref lamina"; depois, a string foi invertida para "animal feroz" e, por fim, o método to String.

4.3. Usando a classe Java.lang.Math Os desenvolvedores da linguagem Java criaram a classe Math, com vários métodos (funções) para uso em

expressões matemáticas. Por se tratar de uma classe estática, seus métodos podem ser acessados diretamente. O abs, por exemplo, é um método usado para retornar o valor absoluto de um argumento. Veja suas assinaturas:

public static int abs(int value) public static long abs (long value)

public static float abs (float value) public static double abs (double value)

Esses métodos podem aceitar diversas possibilidades de entrada e saída, ou seja, ele é um método sobreposto. A seguir, são apresentados três exemplos do uso do abs:

System.out.println("Valor absoluto de "+Math.abs(-25)); // 25 System.out.println("Valor absoluto de "+Math.abs(-2342L)) // 2342 System.out.println("Valor absoluto de "+Math.abs(-5.5F)) // 5.5

O método abs possui duas exceções: o cálculo do abs do limite mínimo para inteiros e long. Por exemplo, considerando Integer.MIN_VALUE igual a -2147483648 e Long.MIN_VALUE igual a -922337236854775808:

System.out.println("Valor absoluto de "+Math.abs(-2147483648)); // -2147483648 System.out.println("Valor absoluto de "+Math.abs(-922337236854775808)); // -922337236854775808

Quando esses valores forem apresentados como argumento, o método não retornará o valor absoluto. Outros métodos da classe Math:

• ceil - Elimina a parte fracionária de um número de tipo flutuante, mantendo o seu tipo, e incrementa uma unidade (arredonda para cima).

Exemplo: public static float ceil(float value) public static double ceil(double value)

System.out.println(Math.ceil(3.8)); // Resultado: 4.0 System.out.println(Math.ceil(-3.3)); // Resultado: -3.0

• floor - Elimina a parte fracionária de um número de tipo flutuante, mantendo o seu tipo (arredonda para baixo).

Exemplo: public static float ceil(float value) public static double ceil(double value)

System.out.println(Math.floor(-10.9)); // Resultado: -11.0 System.out.println(Math.floor(12.2)); // Resultado: 12.0

• max - Retorna o maior entre dois argumentos informados como entrada. Exemplos de assinaturas: public static int max(int a, int b) public static long max(long a, long b)) public static float max(float a, float b)) public static double max(double a, double b))

Atenção. Não existe assinatura para os tipos byte e short, pois esses tipos não podem, implicitamente, ser convertidos para int.

Exemplos:

System.out.println("o maior entre -8 e -9 é "+Math.max(-8,-9)); // Resultado: o maior entre -8 e -9 é -8 System.out.println("o maior entre -8 e -9 é "+Math.max(-8.0,9)); // Resultado: o maior entre -8 e 9 é 9.0

Neste último exemplo, apesar de não existir uma assinatura (double,int), o valor 9 é convertido implicitamente para double, por isso é que foi apresentado o resultado 9.0 (double) e não 9 (int).

• min - Retorna o menor entre dois argumentos informados como entrada. Exemplos de assinaturas: public static int min(int a, int b) public static long min(long a, long b)) public static float min(float a, float b)) public static double min(double a, double b))

Atenção. Não existe assinatura para os tipos byte e short, pois esses tipos não podem ser convertidos para int. Exemplos:

System.out.println("o menor entre -8 e -9 é "+Math.max(-8,-9)); // Resultado: o menor entre -8 e -9 é -9 System.out.println("o menor entre -8 e -9 é "+Math.max(-8.0,9)); // Resultado: o menor entre -8 e 9 é 8.0

Neste último exemplo, apesar de não existir uma assinatura (double,int), o valor 8 é convertido implicitamente para double, por isso é que foi apresentado o resultado 8.0 (double) e não 8 (int).

• round - Arredonda um número de ponto flutuante recebido como argumento. Exemplos de assinaturas: public static int round(float a) public static long round(double a)

Observe que esse método deve receber um valor de tipo flutuante (float ou double) e retornar esse valor arredondado e convertido para um tipo inteiro (int ou long).

Exemplo: Class teste { Public static void main(String args[ ]) { System.out.println(Math.round(9.4)); // resultado: 9 System.out.println(Math.round(9.5)); // resultado: 10 System.out.println(Math.round(9.9)); // resultado: 10 System.out.println(Math.round(-9.9)); // resultado: -10 } }

• random - O método Math.random retorna um número double maior ou igual a 0.0 e menor do que 1.0, escolhido aleatoriamente.

Exemplo: System.out.println(Math.random()); // resultado: um número nr qualquer, tal que 0.0 <= nr < 1.0

• sqrt - Retorna a raiz quadrada de um número. Sua assinatura é: public static double sqrt(double a)

Exemplo: System.out.println(Math.sqrt(49)); // resultado: 7.0

Se o número passado como argumento for negativo, o retorno será NaN.

Observações importantes da classe Math: double x; float p_i = Float.POSITIVE_INFINITY; double n_i = Double.POSITIVE_INFINITY; double n_a_n = Double.NaN; if (n_a_n != n_a_n ) System.out.println("NaN é diferente de NaN"); // resultado: NaN não é igual a nada, nem mesmo a outro NaN if(Double.isNaN(n_a_n)) System.out.println("é um NaN"); // resultado: é um NaN x = Math.sqrt(n_i); // alerta geral! Será atribuído um NaN para x if(Double.isNaN(x)) System.out.println("x é um NaN"); // resultado: x é um NaN System.out.println( 32 / 0 ); // resultado: Java.lang.ArithmeticException System.out.println( 32.0 / 0.0 ); // resultado Infinity System.out.println( 32.0 / 0.0 ); // resultado Infinity System.out.println( 32.0 / -0.0 ); // resultado -Infinity System.out.println( -32.0 / 0.0 ); // resultado -Infinity System.out.println( -32.0 / -0.0 ); // resultado Infinity System.out.println( 32.0 / 0 ); // resultado Infinity System.out.println( -32.0 / 0 ); // resultado -Infinity System.out.println( -32.0 / -0 ); // resultado -Infinity

Entendendo como o Java trata a divisão por 0 (zero)

• Quando o divisor e o dividendo forem números inteiros, se o divisor for zero, será lançada uma exceção (erro)

System.out.println( 32 / 0 ); // exceção System.out.println( 32 / -0 ); // exceção

• Quando a expressão envolver pelo menos um número de ponto flutuante, se o divisor for 0 (zero inteiro), nunca haverá a troca de sinais, sempre será mantido o sinal do dividendo e, se o divisor 0.0 (float) sempre haverá a troca de sinais.

System.out.println( 32.0 / 0 ); // resultado Infinity System.out.println( -32.0 / 0 ); // resultado -Infinity System.out.println( 32.0 / 0.0 ); // resultado Infinity System.out.println( 32.0 / 0.0 ); // resultado Infinity System.out.println( 32.0 / -0.0 ); // resultado -Infinity System.out.println( -32.0 / 0.0 ); // resultado -Infinity System.out.println( -32.0 / -0.0 ); // resultado Infinity

4.3. Usando as classes Wrappers As classes wrappers têm o papel de encapsular os tipos primitivos para permitir a possibilidade de operações

como: conversões, mudança de bases decimais e algumas operações somente permitidas a objetos. Por exemplo, trabalhar com coleções (Listas encadeadas e pilhas, entre outros).

Todo tipo primitivo tem uma classe wrapper correspondente. Veja:

Tipo primitivo Classe wrapper byte Byte short Short

integer Integer long Long

character Character

float Float double Double boolean Boolean

Para se cria um objeto pode-se fazer o seguinte: Integer a = new Integer ("10"); Integer b = new Integer (10);

Note que as classes têm os construtores sobrecarregados, ou seja, aceitam o Valor String e o valor int. Veja outro exemplo:

Float f1 = new Float ("10"); Float b = new Float (10); Boolean b1 = new Boolean("TRUE") Boolean b2 = new Boolean("true");

No caso da classe Boolean, pode-se instanciar um objeto passando uma String TRUE ou FALSE, independentemente de elas estarem escritas com maiúsculas ou minúsculas.

4.4.1. O método valueOf( ) É um método estático encontrado em quase dotas as classes wrappers, o qual retorna o objeto wrapper à classe

relacionada. Esse método tem uma funcionalidade muito interessante, pois pode converter números em bases diferentes. Observe:

Integer i1 = Integer.valueOf("89");

Essa linha de comando retorna um Integer na base decimal.

4.4.2. O método ...... value( ) Esse método tem a seguinte assinatura:

Public int intValue( ) Public byte byteValue( ) Public short shortValue( ) Public long longValue( ) Public float floatValue( ) Public double doubleValue( )

Esses métodos não são estáticos. Eles têm a função de realizar conversões dentro das classes wrapper: Public class TestWrappers { Public static void main(String Args[ ]) { Float w = Float.valueOf("89"); int i = w.intValue(); System.out.println(w.toString()); System.out.println(i); } }

Esse programa faz uma conversão do valor armazenado pelo objeto w e o converte para o tipo primitivo int. Esse processo poderia ser para conversão de qualquer outro tipo primitivo de variáveis. Veja os demais exemplos:

byte b = w.byteValue(); short s = w.shortValue();

4.4.2. O método estático parse...( ) Outra forma de se converter é usando-se o método parse’s. Note:

int i = Integer.parseInt("12");

A string passada como parâmetro deve corresponder ao tipo primitivo int, senão, uma exceção será lançada, como se pode ver no seguinte exemplo:

int i = Integer.parseInt("12f");

Nesse caso, será lançada a exceção: Java.lang.NumberFormatException. Da mesma forma, existem o Double.parseDouble( ) e o Float.parseFloat( ), entre outros.

4.4.3. O método toString Esse método é utilizado para converter objetos wrapper para string. Sendo int i = 10, a sua conversão para

string poderia ser feita por uma das seguintes formas: String s = "" + i; ou String s = new Integer(i).toString(); ou, ainda String s = Interger.toString(i);